Merge remote-tracking branch 'aosp/oreo-cts-dev' into HEAD
diff --git a/.clang-format-2 b/.clang-format-2
index aab4665..41591ce 100644
--- a/.clang-format-2
+++ b/.clang-format-2
@@ -1,4 +1,5 @@
BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: Inline
ColumnLimit: 100
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
diff --git a/.clang-format-4 b/.clang-format-4
index 1497447..ae4a451 100644
--- a/.clang-format-4
+++ b/.clang-format-4
@@ -1,5 +1,6 @@
BasedOnStyle: Google
AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
ColumnLimit: 100
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
diff --git a/adb/Android.bp b/adb/Android.bp
new file mode 100644
index 0000000..41f7b89
--- /dev/null
+++ b/adb/Android.bp
@@ -0,0 +1,51 @@
+// 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.
+
+python_binary_host {
+ name: "adb_integration_test_adb",
+ main: "test_adb.py",
+ srcs: [
+ "test_adb.py",
+ ],
+ libs: [
+ "adb_py",
+ ],
+ version: {
+ py2: {
+ enabled: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ },
+}
+
+python_binary_host {
+ name: "adb_integration_test_device",
+ main: "test_device.py",
+ srcs: [
+ "test_device.py",
+ ],
+ libs: [
+ "adb_py",
+ ],
+ version: {
+ py2: {
+ enabled: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ },
+}
diff --git a/adb/Android.mk b/adb/Android.mk
index e841205..05b0284 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -5,23 +5,27 @@
LOCAL_PATH:= $(call my-dir)
+include $(LOCAL_PATH)/../platform_tools_tool_version.mk
+
adb_host_sanitize :=
adb_target_sanitize :=
-adb_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
-
ADB_COMMON_CFLAGS := \
-Wall -Wextra -Werror \
-Wno-unused-parameter \
-Wno-missing-field-initializers \
-Wvla \
- -DADB_REVISION='"$(adb_version)"' \
+ -DADB_VERSION="\"$(tool_version)\"" \
+
+ADB_COMMON_posix_CFLAGS := \
+ -Wexit-time-destructors \
+ -Wthread-safety \
ADB_COMMON_linux_CFLAGS := \
- -Wexit-time-destructors \
+ $(ADB_COMMON_posix_CFLAGS) \
ADB_COMMON_darwin_CFLAGS := \
- -Wexit-time-destructors \
+ $(ADB_COMMON_posix_CFLAGS) \
# Define windows.h and tchar.h Unicode preprocessor symbols so that
# CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
@@ -81,12 +85,14 @@
LIBADB_darwin_SRC_FILES := \
sysdeps_unix.cpp \
+ sysdeps/posix/network.cpp \
client/usb_dispatch.cpp \
client/usb_libusb.cpp \
client/usb_osx.cpp \
LIBADB_linux_SRC_FILES := \
sysdeps_unix.cpp \
+ sysdeps/posix/network.cpp \
client/usb_dispatch.cpp \
client/usb_libusb.cpp \
client/usb_linux.cpp \
@@ -102,7 +108,6 @@
sysdeps_win32_test.cpp \
include $(CLEAR_VARS)
-LOCAL_CLANG := true
LOCAL_MODULE := libadbd_usb
LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
LOCAL_SRC_FILES := daemon/usb.cpp
@@ -111,18 +116,18 @@
# Even though we're building a static library (and thus there's no link step for
# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase
+LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase libasyncio
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_CLANG := true
LOCAL_MODULE := libadbd
LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
LOCAL_SRC_FILES := \
$(LIBADB_SRC_FILES) \
adbd_auth.cpp \
jdwp_service.cpp \
+ sysdeps/posix/network.cpp \
LOCAL_SANITIZE := $(adb_target_sanitize)
@@ -164,7 +169,6 @@
include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_CLANG := true
LOCAL_MODULE := adbd_test
LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
LOCAL_SRC_FILES := \
@@ -217,15 +221,15 @@
LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
LOCAL_SRC_FILES_windows := $(LIBADB_TEST_windows_SRCS)
LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_SHARED_LIBRARIES := libbase
LOCAL_STATIC_LIBRARIES := \
libadb \
+ libbase \
libcrypto_utils \
libcrypto \
libcutils \
libdiagnose_usb \
libmdnssd \
- libgmock_host
+ libgmock_host \
LOCAL_STATIC_LIBRARIES_linux := libusb
LOCAL_STATIC_LIBRARIES_darwin := libusb
@@ -293,7 +297,7 @@
libcrypto \
libdiagnose_usb \
liblog \
- libmdnssd
+ libmdnssd \
# Don't use libcutils on Windows.
LOCAL_STATIC_LIBRARIES_darwin := libcutils
@@ -323,8 +327,6 @@
include $(CLEAR_VARS)
-LOCAL_CLANG := true
-
LOCAL_SRC_FILES := \
daemon/main.cpp \
daemon/mdns.cpp \
@@ -353,13 +355,13 @@
LOCAL_MODULE := adbd
LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
LOCAL_SANITIZE := $(adb_target_sanitize)
LOCAL_STRIP_MODULE := keep_symbols
LOCAL_STATIC_LIBRARIES := \
libadbd \
+ libasyncio \
+ libavb_user \
libbase \
libbootloader_message \
libfs_mgr \
@@ -379,4 +381,9 @@
include $(BUILD_EXECUTABLE)
+# adb integration test
+# =========================================================
+$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_adb.BUILT))
+$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_device.BUILT))
+
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 577e9b9..8c24bbb 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -31,6 +31,8 @@
#include <time.h>
#include <chrono>
+#include <condition_variable>
+#include <mutex>
#include <string>
#include <thread>
#include <vector>
@@ -47,7 +49,9 @@
#include "adb_auth.h"
#include "adb_io.h"
#include "adb_listeners.h"
+#include "adb_unique_fd.h"
#include "adb_utils.h"
+#include "sysdeps/chrono.h"
#include "transport.h"
#if !ADB_HOST
@@ -59,10 +63,12 @@
std::string adb_version() {
// Don't change the format of this --- it's parsed by ddmlib.
- return android::base::StringPrintf("Android Debug Bridge version %d.%d.%d\n"
- "Revision %s\n",
- ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
- ADB_REVISION);
+ return android::base::StringPrintf(
+ "Android Debug Bridge version %d.%d.%d\n"
+ "Version %s\n"
+ "Installed as %s\n",
+ ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_VERSION,
+ android::base::GetExecutablePath().c_str());
}
void fatal(const char *fmt, ...) {
@@ -251,6 +257,19 @@
send_packet(cp, t);
}
+#if ADB_HOST
+
+void SendConnectOnHost(atransport* t) {
+ // Send an empty message before A_CNXN message. This is because the data toggle of the ep_out on
+ // host and ep_in on device may not be the same.
+ apacket* p = get_apacket();
+ CHECK(p);
+ send_packet(p, t);
+ send_connect(t);
+}
+
+#endif
+
// qual_overwrite is used to overwrite a qualifier string. dst is a
// pointer to a char pointer. It is assumed that if *dst is non-NULL, it
// was malloc'ed and needs to freed. *dst will be set to a dup of src.
@@ -297,29 +316,25 @@
const std::string& type = pieces[0];
if (type == "bootloader") {
D("setting connection_state to kCsBootloader");
- t->connection_state = kCsBootloader;
- update_transports();
+ t->SetConnectionState(kCsBootloader);
} else if (type == "device") {
D("setting connection_state to kCsDevice");
- t->connection_state = kCsDevice;
- update_transports();
+ t->SetConnectionState(kCsDevice);
} else if (type == "recovery") {
D("setting connection_state to kCsRecovery");
- t->connection_state = kCsRecovery;
- update_transports();
+ t->SetConnectionState(kCsRecovery);
} else if (type == "sideload") {
D("setting connection_state to kCsSideload");
- t->connection_state = kCsSideload;
- update_transports();
+ t->SetConnectionState(kCsSideload);
} else {
D("setting connection_state to kCsHost");
- t->connection_state = kCsHost;
+ t->SetConnectionState(kCsHost);
}
}
static void handle_new_connection(atransport* t, apacket* p) {
- if (t->connection_state != kCsOffline) {
- t->connection_state = kCsOffline;
+ if (t->GetConnectionState() != kCsOffline) {
+ t->SetConnectionState(kCsOffline);
handle_offline(t);
}
@@ -338,6 +353,8 @@
send_auth_request(t);
}
#endif
+
+ update_transports();
}
void handle_packet(apacket *p, atransport *t)
@@ -353,10 +370,10 @@
if (p->msg.arg0){
send_packet(p, t);
#if ADB_HOST
- send_connect(t);
+ SendConnectOnHost(t);
#endif
} else {
- t->connection_state = kCsOffline;
+ t->SetConnectionState(kCsOffline);
handle_offline(t);
send_packet(p, t);
}
@@ -370,7 +387,9 @@
switch (p->msg.arg0) {
#if ADB_HOST
case ADB_AUTH_TOKEN:
- t->connection_state = kCsUnauthorized;
+ if (t->GetConnectionState() == kCsOffline) {
+ t->SetConnectionState(kCsUnauthorized);
+ }
send_auth_response(p->data, p->msg.data_length, t);
break;
#else
@@ -389,7 +408,7 @@
break;
#endif
default:
- t->connection_state = kCsOffline;
+ t->SetConnectionState(kCsOffline);
handle_offline(t);
break;
}
@@ -505,8 +524,8 @@
if (!_try_make_handle_noninheritable(h)) {
// Show the handle value to give us a clue in case we have problems
// with pseudo-handle values.
- fprintf(stderr, "Cannot make handle 0x%p non-inheritable: %s\n",
- h, android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ fprintf(stderr, "adb: cannot make handle 0x%p non-inheritable: %s\n", h,
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
return false;
}
@@ -521,7 +540,7 @@
HANDLE pipe_read_raw = NULL;
HANDLE pipe_write_raw = NULL;
if (!CreatePipe(&pipe_read_raw, &pipe_write_raw, sa, 0)) {
- fprintf(stderr, "Cannot create pipe: %s\n",
+ fprintf(stderr, "adb: CreatePipe failed: %s\n",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
return false;
}
@@ -552,7 +571,8 @@
std::unique_ptr<FILE, decltype(&fclose)> stream(nullptr, fclose);
if (original_fd == -1) {
- fprintf(stderr, "Failed to get file descriptor for %s: %s\n", output_name, strerror(errno));
+ fprintf(stderr, "adb: failed to get file descriptor for %s: %s\n", output_name,
+ strerror(errno));
return EXIT_FAILURE;
}
@@ -564,7 +584,7 @@
// call this function if subprocesses may be started concurrently.
const int fd = dup(original_fd);
if (fd == -1) {
- fprintf(stderr, "Failed to duplicate file descriptor for %s: %s\n", output_name,
+ fprintf(stderr, "adb: failed to duplicate file descriptor for %s: %s\n", output_name,
strerror(errno));
return EXIT_FAILURE;
}
@@ -572,7 +592,7 @@
// Note that although we call fdopen() below with a binary flag, it may not adhere to that
// flag, so we have to set the mode manually.
if (_setmode(fd, _O_BINARY) == -1) {
- fprintf(stderr, "Failed to set binary mode for duplicate of %s: %s\n", output_name,
+ fprintf(stderr, "adb: failed to set binary mode for duplicate of %s: %s\n", output_name,
strerror(errno));
unix_close(fd);
return EXIT_FAILURE;
@@ -580,7 +600,7 @@
stream.reset(fdopen(fd, "wb"));
if (stream.get() == nullptr) {
- fprintf(stderr, "Failed to open duplicate stream for %s: %s\n", output_name,
+ fprintf(stderr, "adb: failed to open duplicate stream for %s: %s\n", output_name,
strerror(errno));
unix_close(fd);
return EXIT_FAILURE;
@@ -589,7 +609,7 @@
// Unbuffer the stream because it will be buffered by default and we want subprocess output
// to be shown immediately.
if (setvbuf(stream.get(), NULL, _IONBF, 0) == -1) {
- fprintf(stderr, "Failed to unbuffer %s: %s\n", output_name, strerror(errno));
+ fprintf(stderr, "adb: failed to unbuffer %s: %s\n", output_name, strerror(errno));
return EXIT_FAILURE;
}
@@ -606,7 +626,7 @@
if (err == ERROR_BROKEN_PIPE) {
return EXIT_SUCCESS;
} else {
- fprintf(stderr, "Failed to read from %s: %s\n", output_name,
+ fprintf(stderr, "adb: failed to read from %s: %s\n", output_name,
android::base::SystemErrorCodeToString(err).c_str());
return EXIT_FAILURE;
}
@@ -617,8 +637,8 @@
// fwrite() actually calls adb_fwrite() which can write UTF-8 to the console.
const size_t bytes_written = fwrite(buf, 1, bytes_read, stream.get());
if (bytes_written != bytes_read) {
- fprintf(stderr, "Only wrote %zu of %lu bytes to %s\n", bytes_written, bytes_read,
- output_name);
+ fprintf(stderr, "adb: error: only wrote %zu of %lu bytes to %s\n", bytes_written,
+ bytes_read, output_name);
return EXIT_FAILURE;
}
}
@@ -637,6 +657,26 @@
#endif
+static void ReportServerStartupFailure(pid_t pid) {
+ fprintf(stderr, "ADB server didn't ACK\n");
+ fprintf(stderr, "Full server startup log: %s\n", GetLogFilePath().c_str());
+ fprintf(stderr, "Server had pid: %d\n", pid);
+
+ unique_fd fd(adb_open(GetLogFilePath().c_str(), O_RDONLY));
+ if (fd == -1) return;
+
+ // Let's not show more than 128KiB of log...
+ adb_lseek(fd, -128 * 1024, SEEK_END);
+ std::string content;
+ if (!android::base::ReadFdToString(fd, &content)) return;
+
+ std::string header = android::base::StringPrintf("--- adb starting (pid %d) ---", pid);
+ std::vector<std::string> lines = android::base::Split(content, "\n");
+ int i = lines.size() - 1;
+ while (i >= 0 && lines[i] != header) --i;
+ while (static_cast<size_t>(i) < lines.size()) fprintf(stderr, "%s\n", lines[i++].c_str());
+}
+
int launch_server(const std::string& socket_spec) {
#if defined(_WIN32)
/* we need to start the server in the background */
@@ -661,7 +701,7 @@
FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL));
if (nul_read.get() == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "Cannot open 'nul': %s\n",
+ fprintf(stderr, "adb: CreateFileW 'nul' failed: %s\n",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
@@ -723,8 +763,7 @@
// If this fires, either handle values are larger than 32-bits or else
// there is a bug in our casting.
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx
- fprintf(stderr, "Cannot fit pipe handle value into 32-bits: 0x%p\n",
- ack_write.get());
+ fprintf(stderr, "adb: cannot fit pipe handle value into 32-bits: 0x%p\n", ack_write.get());
return -1;
}
@@ -734,7 +773,7 @@
arraysize(program_path));
if ((module_result >= arraysize(program_path)) || (module_result == 0)) {
// String truncation or some other error.
- fprintf(stderr, "Cannot get executable path: %s\n",
+ fprintf(stderr, "adb: cannot get executable path: %s\n",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
@@ -759,7 +798,7 @@
NULL, /* use parent's starting directory */
&startup, /* startup info, i.e. std handles */
&pinfo )) {
- fprintf(stderr, "Cannot create process: %s\n",
+ fprintf(stderr, "adb: CreateProcessW failed: %s\n",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
@@ -789,7 +828,7 @@
_beginthreadex(NULL, 0, _redirect_stdout_thread, stdout_read.get(),
0, NULL)));
if (stdout_thread.get() == nullptr) {
- fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+ fprintf(stderr, "adb: cannot create thread: %s\n", strerror(errno));
return -1;
}
stdout_read.release(); // Transfer ownership to new thread
@@ -798,7 +837,7 @@
_beginthreadex(NULL, 0, _redirect_stderr_thread, stderr_read.get(),
0, NULL)));
if (stderr_thread.get() == nullptr) {
- fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+ fprintf(stderr, "adb: cannot create thread: %s\n", strerror(errno));
return -1;
}
stderr_read.release(); // Transfer ownership to new thread
@@ -817,7 +856,8 @@
memcmp(temp, expected, expected_length) == 0) {
got_ack = true;
} else {
- fprintf(stderr, "ADB server didn't ACK\n");
+ ReportServerStartupFailure(GetProcessId(process_handle.get()));
+ return -1;
}
} else {
const DWORD err = GetLastError();
@@ -843,22 +883,20 @@
if (wait_result == WAIT_TIMEOUT) {
// Threads did not finish after waiting a little while. Perhaps the
// server didn't close pipes, or it is hung.
- fprintf(stderr, "Timed-out waiting for threads to finish reading from "
- "ADB Server\n");
+ fprintf(stderr, "adb: timed out waiting for threads to finish reading from ADB server\n");
// Process handles are signaled when the process exits, so if we wait
// on the handle for 0 seconds and it returns 'timeout', that means that
// the process is still running.
if (WaitForSingleObject(process_handle.get(), 0) == WAIT_TIMEOUT) {
// We could TerminateProcess(), but that seems somewhat presumptive.
- fprintf(stderr, "ADB Server is running: process id %lu\n",
- pinfo.dwProcessId);
+ fprintf(stderr, "adb: server is running with process id %lu\n", pinfo.dwProcessId);
}
return -1;
}
if (wait_result != WAIT_OBJECT_0) {
- fprintf(stderr, "Unexpected result waiting for threads: %lu: %s\n",
- wait_result, android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ fprintf(stderr, "adb: unexpected result waiting for threads: %lu: %s\n", wait_result,
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
@@ -892,13 +930,10 @@
int result = execl(path.c_str(), "adb", "-L", socket_spec.c_str(), "fork-server", "server",
"--reply-fd", reply_fd, NULL);
// this should not return
- fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
- } else {
+ fprintf(stderr, "adb: execl returned %d: %s\n", result, strerror(errno));
+ } else {
// parent side of the fork
-
- char temp[3];
-
- temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';
+ char temp[3] = {};
// wait for the "OK\n" message
adb_close(fd[1]);
int ret = adb_read(fd[0], temp, 3);
@@ -909,7 +944,7 @@
return -1;
}
if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
- fprintf(stderr, "ADB server didn't ACK\n" );
+ ReportServerStartupFailure(pid);
return -1;
}
}
@@ -921,8 +956,8 @@
// Try to handle a network forwarding request.
// This returns 1 on success, 0 on failure, and -1 to indicate this is not
// a forwarding-related request.
-int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd)
-{
+int handle_forward_request(const char* service, TransportType type, const char* serial,
+ TransportId transport_id, int reply_fd) {
if (!strcmp(service, "list-forward")) {
// Create the list of forward redirections.
std::string listeners = format_listeners();
@@ -975,7 +1010,8 @@
}
std::string error_msg;
- atransport* transport = acquire_one_transport(type, serial, nullptr, &error_msg);
+ atransport* transport =
+ acquire_one_transport(type, serial, transport_id, nullptr, &error_msg);
if (!transport) {
SendFail(reply_fd, error_msg);
return 1;
@@ -1032,26 +1068,21 @@
SendProtocolString(fd, s);
return 0;
}
-#endif
-int handle_host_request(const char* service, TransportType type,
- const char* serial, int reply_fd, asocket* s) {
+int handle_host_request(const char* service, TransportType type, const char* serial,
+ TransportId transport_id, int reply_fd, asocket* s) {
if (strcmp(service, "kill") == 0) {
fprintf(stderr, "adb server killed by remote request\n");
fflush(stdout);
+
+ // Send a reply even though we don't read it anymore, so that old versions
+ // of adb that do read it don't spew error messages.
SendOkay(reply_fd);
- // On Windows, if the process exits with open sockets that
- // shutdown(SD_SEND) has not been called on, TCP RST segments will be
- // sent to the peers which will cause their next recv() to error-out
- // with WSAECONNRESET. In the case of this code, that means the client
- // may not read the OKAY sent above.
- adb_shutdown(reply_fd);
-
+ // Rely on process exit to close the socket for us.
android::base::quick_exit(0);
}
-#if ADB_HOST
// "transport:" is used for switching transport with a specified serial number
// "transport-usb:" is used for switching transport to the only USB transport
// "transport-local:" is used for switching transport to the only local transport
@@ -1059,7 +1090,14 @@
if (!strncmp(service, "transport", strlen("transport"))) {
TransportType type = kTransportAny;
- if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
+ if (!strncmp(service, "transport-id:", strlen("transport-id:"))) {
+ service += strlen("transport-id:");
+ transport_id = strtoll(service, const_cast<char**>(&service), 10);
+ if (*service != '\0') {
+ SendFail(reply_fd, "invalid transport id");
+ return 1;
+ }
+ } else if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
type = kTransportUsb;
} else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
type = kTransportLocal;
@@ -1071,7 +1109,7 @@
}
std::string error;
- atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t != nullptr) {
s->transport = t;
SendOkay(reply_fd);
@@ -1096,16 +1134,10 @@
if (!strcmp(service, "reconnect-offline")) {
std::string response;
close_usb_devices([&response](const atransport* transport) {
- switch (transport->connection_state) {
+ switch (transport->GetConnectionState()) {
case kCsOffline:
case kCsUnauthorized:
- response += "reconnecting ";
- if (transport->serial) {
- response += transport->serial;
- } else {
- response += "<unknown>";
- }
- response += "\n";
+ response += "reconnecting " + transport->serial_name() + "\n";
return true;
default:
return false;
@@ -1120,7 +1152,7 @@
if (!strcmp(service, "features")) {
std::string error;
- atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t != nullptr) {
SendOkay(reply_fd, FeatureSetToString(t->features()));
} else {
@@ -1129,17 +1161,16 @@
return 0;
}
-#if ADB_HOST
if (!strcmp(service, "host-features")) {
FeatureSet features = supported_features();
// Abuse features to report libusb status.
if (should_use_libusb()) {
features.insert(kFeatureLibusb);
}
+ features.insert(kFeaturePushSync);
SendOkay(reply_fd, FeatureSetToString(features));
return 0;
}
-#endif
// remove TCP transport
if (!strncmp(service, "disconnect:", 11)) {
@@ -1174,7 +1205,7 @@
// These always report "unknown" rather than the actual error, for scripts.
if (!strcmp(service, "get-serialno")) {
std::string error;
- atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t) {
return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
} else {
@@ -1183,7 +1214,7 @@
}
if (!strcmp(service, "get-devpath")) {
std::string error;
- atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t) {
return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
} else {
@@ -1192,7 +1223,7 @@
}
if (!strcmp(service, "get-state")) {
std::string error;
- atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t) {
return SendOkay(reply_fd, t->connection_state_name());
} else {
@@ -1209,15 +1240,63 @@
}
if (!strcmp(service, "reconnect")) {
- if (s->transport != nullptr) {
- kick_transport(s->transport);
+ std::string response;
+ atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &response, true);
+ if (t != nullptr) {
+ kick_transport(t);
+ response =
+ "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
}
- return SendOkay(reply_fd, "done");
+ return SendOkay(reply_fd, response);
}
-#endif // ADB_HOST
- int ret = handle_forward_request(service, type, serial, reply_fd);
+ int ret = handle_forward_request(service, type, serial, transport_id, reply_fd);
if (ret >= 0)
return ret - 1;
return -1;
}
+
+static auto& init_mutex = *new std::mutex();
+static auto& init_cv = *new std::condition_variable();
+static bool device_scan_complete = false;
+static bool transports_ready = false;
+
+void update_transport_status() {
+ bool result = iterate_transports([](const atransport* t) {
+ if (t->type == kTransportUsb && t->online != 1) {
+ return false;
+ }
+ return true;
+ });
+
+ bool ready;
+ {
+ std::lock_guard<std::mutex> lock(init_mutex);
+ transports_ready = result;
+ ready = transports_ready && device_scan_complete;
+ }
+
+ if (ready) {
+ init_cv.notify_all();
+ }
+}
+
+void adb_notify_device_scan_complete() {
+ {
+ std::lock_guard<std::mutex> lock(init_mutex);
+ if (device_scan_complete) {
+ return;
+ }
+
+ device_scan_complete = true;
+ }
+
+ update_transport_status();
+}
+
+void adb_wait_for_device_initialization() {
+ std::unique_lock<std::mutex> lock(init_mutex);
+ init_cv.wait_for(lock, 3s, []() { return device_scan_complete && transports_ready; });
+}
+
+#endif // ADB_HOST
diff --git a/adb/adb.h b/adb/adb.h
index aea5fb8..6a9897f 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -31,8 +31,7 @@
#include "usb.h"
constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
-constexpr size_t MAX_PAYLOAD_V2 = 256 * 1024;
-constexpr size_t MAX_PAYLOAD = MAX_PAYLOAD_V2;
+constexpr size_t MAX_PAYLOAD = 1024 * 1024;
constexpr size_t LINUX_MAX_SOCKET_SIZE = 4194304;
@@ -56,6 +55,7 @@
// Increment this when we want to force users to start a new adb server.
#define ADB_SERVER_VERSION 39
+using TransportId = uint64_t;
class atransport;
struct amessage {
@@ -139,7 +139,7 @@
int get_available_local_transport_index();
#endif
int init_socket_transport(atransport *t, int s, int port, int local);
-void init_usb_transport(atransport *t, usb_handle *usb, ConnectionState state);
+void init_usb_transport(atransport* t, usb_handle* usb);
std::string getEmulatorSerialString(int console_port);
#if ADB_HOST
@@ -149,7 +149,7 @@
int service_to_fd(const char* name, const atransport* transport);
#if ADB_HOST
-asocket *host_service_to_socket(const char* name, const char *serial);
+asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id);
#endif
#if !ADB_HOST
@@ -159,7 +159,8 @@
int create_jdwp_connection_fd(int jdwp_pid);
#endif
-int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd);
+int handle_forward_request(const char* service, TransportType type, const char* serial,
+ TransportId transport_id, int reply_fd);
#if !ADB_HOST
void framebuffer_service(int fd, void *cookie);
@@ -216,13 +217,31 @@
#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2)
#endif
-int handle_host_request(const char* service, TransportType type, const char* serial, int reply_fd, asocket *s);
+int handle_host_request(const char* service, TransportType type, const char* serial,
+ TransportId transport_id, int reply_fd, asocket* s);
void handle_online(atransport *t);
void handle_offline(atransport *t);
void send_connect(atransport *t);
+#if ADB_HOST
+void SendConnectOnHost(atransport* t);
+#endif
void parse_banner(const std::string&, atransport* t);
+// On startup, the adb server needs to wait until all of the connected devices are ready.
+// To do this, we need to know when the scan has identified all of the potential new transports, and
+// when each transport becomes ready.
+// TODO: Do this for mDNS as well, instead of just USB?
+
+// We've found all of the transports we potentially care about.
+void adb_notify_device_scan_complete();
+
+// One or more transports have changed status, check to see if we're ready.
+void update_transport_status();
+
+// Wait until device scan has completed and every transport is ready, or a timeout elapses.
+void adb_wait_for_device_initialization();
+
#endif
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index ef52189..849a6e7 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -20,6 +20,7 @@
#include "adb_client.h"
#include <errno.h>
+#include <inttypes.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
@@ -28,12 +29,15 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <condition_variable>
+#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
#include <cutils/sockets.h>
#include "adb_io.h"
@@ -43,12 +47,20 @@
static TransportType __adb_transport = kTransportAny;
static const char* __adb_serial = NULL;
+static TransportId __adb_transport_id = 0;
static const char* __adb_server_socket_spec;
-void adb_set_transport(TransportType type, const char* serial) {
+void adb_set_transport(TransportType type, const char* serial, TransportId transport_id) {
__adb_transport = type;
__adb_serial = serial;
+ __adb_transport_id = transport_id;
+}
+
+void adb_get_transport(TransportType* type, const char** serial, TransportId* transport_id) {
+ if (type) *type = __adb_transport;
+ if (serial) *serial = __adb_serial;
+ if (transport_id) *transport_id = __adb_transport_id;
}
void adb_set_socket_spec(const char* socket_spec) {
@@ -60,7 +72,10 @@
static int switch_socket_transport(int fd, std::string* error) {
std::string service;
- if (__adb_serial) {
+ if (__adb_transport_id) {
+ service += "host:transport-id:";
+ service += std::to_string(__adb_transport_id);
+ } else if (__adb_serial) {
service += "host:transport:";
service += __adb_serial;
} else {
@@ -120,9 +135,9 @@
return false;
}
-int _adb_connect(const std::string& service, std::string* error) {
+static int _adb_connect(const std::string& service, std::string* error) {
D("_adb_connect: %s", service.c_str());
- if (service.empty() || service.size() > MAX_PAYLOAD_V1) {
+ if (service.empty() || service.size() > MAX_PAYLOAD) {
*error = android::base::StringPrintf("bad service name length (%zd)",
service.size());
return -1;
@@ -136,8 +151,7 @@
return -2;
}
- if ((memcmp(&service[0],"host",4) != 0 || service == "host:reconnect") &&
- switch_socket_transport(fd, error)) {
+ if (memcmp(&service[0], "host", 4) != 0 && switch_socket_transport(fd, error)) {
return -1;
}
@@ -147,42 +161,58 @@
return -1;
}
- if (service != "reconnect") {
- if (!adb_status(fd, error)) {
- adb_close(fd);
- return -1;
- }
+ if (!adb_status(fd, error)) {
+ adb_close(fd);
+ return -1;
}
D("_adb_connect: return fd %d", fd);
return fd;
}
+bool adb_kill_server() {
+ D("adb_kill_server");
+ std::string reason;
+ int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
+ if (fd < 0) {
+ fprintf(stderr, "cannot connect to daemon at %s: %s\n", __adb_server_socket_spec,
+ reason.c_str());
+ return true;
+ }
+
+ if (!SendProtocolString(fd, "host:kill")) {
+ fprintf(stderr, "error: write failure during connection: %s\n", strerror(errno));
+ return false;
+ }
+
+ ReadOrderlyShutdown(fd);
+ return true;
+}
+
int adb_connect(const std::string& service, std::string* error) {
// first query the adb server's version
int fd = _adb_connect("host:version", error);
D("adb_connect: service %s", service.c_str());
if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
- fprintf(stderr,"** Cannot start server on remote host\n");
+ fprintf(stderr, "* cannot start server on remote host\n");
// error is the original network connection error
return fd;
} else if (fd == -2) {
- fprintf(stdout, "* daemon not running. starting it now at %s *\n", __adb_server_socket_spec);
+ fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec);
start_server:
if (launch_server(__adb_server_socket_spec)) {
- fprintf(stderr,"* failed to start daemon *\n");
+ fprintf(stderr, "* failed to start daemon\n");
// launch_server() has already printed detailed error info, so just
// return a generic error string about the overall adb_connect()
// that the caller requested.
*error = "cannot connect to daemon";
return -1;
} else {
- fprintf(stdout,"* daemon started successfully *\n");
+ fprintf(stderr, "* daemon started successfully\n");
}
- // Give the server some time to start properly and detect devices.
- std::this_thread::sleep_for(3s);
- // fall through to _adb_connect
+ // The server will wait until it detects all of its connected devices before acking.
+ // Fall through to _adb_connect.
} else {
// If a server is already running, check its version matches.
int version = ADB_SERVER_VERSION - 1;
@@ -213,20 +243,9 @@
}
if (version != ADB_SERVER_VERSION) {
- printf("adb server version (%d) doesn't match this client (%d); killing...\n",
- version, ADB_SERVER_VERSION);
- fd = _adb_connect("host:kill", error);
- if (fd >= 0) {
- ReadOrderlyShutdown(fd);
- adb_close(fd);
- } else {
- // If we couldn't connect to the server or had some other error,
- // report it, but still try to start the server.
- fprintf(stderr, "error: %s\n", error->c_str());
- }
-
- /* XXX can we better detect its death? */
- std::this_thread::sleep_for(2s);
+ fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n",
+ version, ADB_SERVER_VERSION);
+ adb_kill_server();
goto start_server;
}
}
@@ -240,7 +259,7 @@
if (fd == -1) {
D("_adb_connect error: %s", error->c_str());
} else if(fd == -2) {
- fprintf(stderr,"** daemon still not running\n");
+ fprintf(stderr, "* daemon still not running\n");
}
D("adb_connect: return fd %d", fd);
@@ -285,15 +304,18 @@
return true;
}
-std::string format_host_command(const char* command, TransportType type, const char* serial) {
- if (serial) {
- return android::base::StringPrintf("host-serial:%s:%s", serial, command);
+std::string format_host_command(const char* command) {
+ if (__adb_transport_id) {
+ return android::base::StringPrintf("host-transport-id:%" PRIu64 ":%s", __adb_transport_id,
+ command);
+ } else if (__adb_serial) {
+ return android::base::StringPrintf("host-serial:%s:%s", __adb_serial, command);
}
const char* prefix = "host";
- if (type == kTransportUsb) {
+ if (__adb_transport == kTransportUsb) {
prefix = "host-usb";
- } else if (type == kTransportLocal) {
+ } else if (__adb_transport == kTransportLocal) {
prefix = "host-local";
}
return android::base::StringPrintf("%s:%s", prefix, command);
@@ -301,7 +323,7 @@
bool adb_get_feature_set(FeatureSet* feature_set, std::string* error) {
std::string result;
- if (adb_query(format_host_command("features", __adb_transport, __adb_serial), &result, error)) {
+ if (adb_query(format_host_command("features"), &result, error)) {
*feature_set = StringToFeatureSet(result);
return true;
}
diff --git a/adb/adb_client.h b/adb/adb_client.h
index d07c1e9..fca435e 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -26,7 +26,9 @@
// Connect to adb, connect to the named service, and return a valid fd for
// interacting with that service upon success or a negative number on failure.
int adb_connect(const std::string& service, std::string* _Nonnull error);
-int _adb_connect(const std::string& service, std::string* _Nonnull error);
+
+// Kill the currently running adb server, if it exists.
+bool adb_kill_server();
// Connect to adb, connect to the named service, returns true if the connection
// succeeded AND the service returned OKAY. Outputs any returned error otherwise.
@@ -38,7 +40,9 @@
std::string* _Nonnull error);
// Set the preferred transport to connect to.
-void adb_set_transport(TransportType type, const char* _Nullable serial);
+void adb_set_transport(TransportType type, const char* _Nullable serial, TransportId transport_id);
+void adb_get_transport(TransportType* _Nullable type, const char* _Nullable* _Nullable serial,
+ TransportId* _Nullable transport_id);
// Set the socket specification for the adb server.
// This function can only be called once, and the argument must live to the end of the process.
@@ -55,8 +59,7 @@
bool adb_status(int fd, std::string* _Nonnull error);
// Create a host command corresponding to selected transport type/serial.
-std::string format_host_command(const char* _Nonnull command, TransportType type,
- const char* _Nullable serial);
+std::string format_host_command(const char* _Nonnull command);
// Get the feature set of the current preferred transport.
bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error);
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index ca8729e..38e3116 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -31,7 +31,7 @@
bool SendProtocolString(int fd, const std::string& s) {
unsigned int length = s.size();
- if (length > MAX_PAYLOAD_V1 - 4) {
+ if (length > MAX_PAYLOAD - 4) {
errno = EMSGSIZE;
return false;
}
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 18b1492..30cb29b 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -19,8 +19,12 @@
#include <stdio.h>
#include <stdlib.h>
+#include <algorithm>
+#include <list>
+
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
#include <cutils/sockets.h>
#include "socket_spec.h"
@@ -64,8 +68,9 @@
// listener_list retains ownership of all created alistener objects. Removing an alistener from
// this list will cause it to be deleted.
+static auto& listener_list_mutex = *new std::mutex();
typedef std::list<std::unique_ptr<alistener>> ListenerList;
-static ListenerList& listener_list = *new ListenerList();
+static ListenerList& listener_list GUARDED_BY(listener_list_mutex) = *new ListenerList();
static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
if (ev & FDE_READ) {
@@ -108,7 +113,8 @@
}
// Called as a transport disconnect function. |arg| is the raw alistener*.
-static void listener_disconnect(void* arg, atransport*) {
+static void listener_disconnect(void* arg, atransport*) EXCLUDES(listener_list_mutex) {
+ std::lock_guard<std::mutex> lock(listener_list_mutex);
for (auto iter = listener_list.begin(); iter != listener_list.end(); ++iter) {
if (iter->get() == arg) {
(*iter)->transport = nullptr;
@@ -119,7 +125,8 @@
}
// Write the list of current listeners (network redirections) into a string.
-std::string format_listeners() {
+std::string format_listeners() EXCLUDES(listener_list_mutex) {
+ std::lock_guard<std::mutex> lock(listener_list_mutex);
std::string result;
for (auto& l : listener_list) {
// Ignore special listeners like those for *smartsocket*
@@ -135,7 +142,9 @@
return result;
}
-InstallStatus remove_listener(const char* local_name, atransport* transport) {
+InstallStatus remove_listener(const char* local_name, atransport* transport)
+ EXCLUDES(listener_list_mutex) {
+ std::lock_guard<std::mutex> lock(listener_list_mutex);
for (auto iter = listener_list.begin(); iter != listener_list.end(); ++iter) {
if (local_name == (*iter)->local_name) {
listener_list.erase(iter);
@@ -145,7 +154,8 @@
return INSTALL_STATUS_LISTENER_NOT_FOUND;
}
-void remove_all_listeners() {
+void remove_all_listeners() EXCLUDES(listener_list_mutex) {
+ std::lock_guard<std::mutex> lock(listener_list_mutex);
auto iter = listener_list.begin();
while (iter != listener_list.end()) {
// Never remove smart sockets.
@@ -157,9 +167,18 @@
}
}
+void close_smartsockets() EXCLUDES(listener_list_mutex) {
+ std::lock_guard<std::mutex> lock(listener_list_mutex);
+ auto pred = [](const std::unique_ptr<alistener>& listener) {
+ return listener->local_name == "*smartsocket*";
+ };
+ listener_list.erase(std::remove_if(listener_list.begin(), listener_list.end(), pred));
+}
+
InstallStatus install_listener(const std::string& local_name, const char* connect_to,
atransport* transport, int no_rebind, int* resolved_tcp_port,
- std::string* error) {
+ std::string* error) EXCLUDES(listener_list_mutex) {
+ std::lock_guard<std::mutex> lock(listener_list_mutex);
for (auto& l : listener_list) {
if (local_name == l->local_name) {
// Can't repurpose a smartsocket.
@@ -212,7 +231,7 @@
if (transport) {
listener->disconnect.opaque = listener.get();
- listener->disconnect.func = listener_disconnect;
+ listener->disconnect.func = listener_disconnect;
transport->AddDisconnect(&listener->disconnect);
}
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
index 8eba00a..70a2ee1 100644
--- a/adb/adb_listeners.h
+++ b/adb/adb_listeners.h
@@ -41,4 +41,6 @@
InstallStatus remove_listener(const char* local_name, atransport* transport);
void remove_all_listeners(void);
+void close_smartsockets();
+
#endif /* __ADB_LISTENERS_H */
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index c369d60..eac923d 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -155,7 +155,7 @@
}
#endif
-#if !defined(_WIN32)
+#if ADB_HOST && !defined(_WIN32)
// adb historically ignored $ANDROID_LOG_TAGS but passed it through to logcat.
// If set, move it out of the way so that libbase logging doesn't try to parse it.
std::string log_tags;
@@ -168,7 +168,7 @@
android::base::InitLogging(argv, &AdbLogger);
-#if !defined(_WIN32)
+#if ADB_HOST && !defined(_WIN32)
// Put $ANDROID_LOG_TAGS back so we can pass it to logcat.
if (!log_tags.empty()) setenv("ANDROID_LOG_TAGS", log_tags.c_str(), 1);
#endif
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index aaffa29..fc6560c 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -58,6 +58,9 @@
void adb_trace_init(char**);
void adb_trace_enable(AdbTrace trace_tag);
+// Include <atomic> before stdatomic.h (introduced in cutils/trace.h) to avoid compile error.
+#include <atomic>
+
#define ATRACE_TAG ATRACE_TAG_ADB
#include <cutils/trace.h>
#include <utils/Trace.h>
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 31d3dc6..b236fb3 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -157,7 +157,12 @@
}
std::string dump_hex(const void* data, size_t byte_count) {
- byte_count = std::min(byte_count, size_t(16));
+ size_t truncate_len = 16;
+ bool truncated = false;
+ if (byte_count > truncate_len) {
+ byte_count = truncate_len;
+ truncated = true;
+ }
const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
@@ -172,6 +177,10 @@
line.push_back(isprint(ch) ? ch : '.');
}
+ if (truncated) {
+ line += " [truncated]";
+ }
+
return line;
}
@@ -267,8 +276,8 @@
adb_close(fd);
}
-int usage(const char* fmt, ...) {
- fprintf(stderr, "adb: ");
+int syntax_error(const char* fmt, ...) {
+ fprintf(stderr, "adb: usage: ");
va_list ap;
va_start(ap, fmt);
@@ -278,3 +287,29 @@
fprintf(stderr, "\n");
return 1;
}
+
+std::string GetLogFilePath() {
+#if defined(_WIN32)
+ const char log_name[] = "adb.log";
+ WCHAR temp_path[MAX_PATH];
+
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
+ DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
+ if (nchars >= arraysize(temp_path) || nchars == 0) {
+ // If string truncation or some other error.
+ fatal("cannot retrieve temporary file path: %s\n",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ }
+
+ std::string temp_path_utf8;
+ if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
+ fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
+ }
+
+ return temp_path_utf8 + log_name;
+#else
+ const char* tmp_dir = getenv("TMPDIR");
+ if (tmp_dir == nullptr) tmp_dir = "/tmp";
+ return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
+#endif
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index e0ad103..f764a0e 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -17,11 +17,14 @@
#ifndef _ADB_UTILS_H_
#define _ADB_UTILS_H_
+#include <condition_variable>
+#include <mutex>
#include <string>
+#include <vector>
#include <android-base/macros.h>
-int usage(const char*, ...);
+int syntax_error(const char*, ...);
void close_stdin();
@@ -53,4 +56,39 @@
bool forward_targets_are_valid(const std::string& source, const std::string& dest,
std::string* error);
+// A thread-safe blocking queue.
+template <typename T>
+class BlockingQueue {
+ std::mutex mutex;
+ std::condition_variable cv;
+ std::vector<T> queue;
+
+ public:
+ void Push(const T& t) {
+ {
+ std::unique_lock<std::mutex> lock(mutex);
+ queue.push_back(t);
+ }
+ cv.notify_one();
+ }
+
+ template <typename Fn>
+ void PopAll(Fn fn) {
+ std::vector<T> popped;
+
+ {
+ std::unique_lock<std::mutex> lock(mutex);
+ cv.wait(lock, [this]() { return !queue.empty(); });
+ popped = std::move(queue);
+ queue.clear();
+ }
+
+ for (const T& t : popped) {
+ fn(t);
+ }
+ }
+};
+
+std::string GetLogFilePath();
+
#endif
diff --git a/adb/adbd_auth.cpp b/adb/adbd_auth.cpp
index b5f87be..3488ad1 100644
--- a/adb/adbd_auth.cpp
+++ b/adb/adbd_auth.cpp
@@ -217,8 +217,8 @@
send_packet(p, t);
}
-void adbd_auth_verified(atransport *t)
-{
+void adbd_auth_verified(atransport* t) {
+ LOG(INFO) << "adb client authorized";
handle_online(t);
send_connect(t);
}
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index b3e391b..abef86a 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -102,7 +102,7 @@
std::vector<const char*> srcs{src_file_.c_str()};
SetLineMessage("pulling");
status_ =
- br_->DoSyncPull(srcs, destination.c_str(), true, line_message_.c_str()) ? 0 : 1;
+ br_->DoSyncPull(srcs, destination.c_str(), false, line_message_.c_str()) ? 0 : 1;
if (status_ != 0) {
fprintf(stderr,
"Bug report finished but could not be copied to '%s'.\n"
@@ -137,7 +137,7 @@
SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]);
} else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
- fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
+ fprintf(stderr, "adb: device failed to take a zipped bugreport: %s\n", error_message);
status_ = -1;
} else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
// progress_line should have the following format:
@@ -195,13 +195,13 @@
DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
};
-int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
- if (argc > 2) return usage("usage: adb bugreport [PATH]");
+int Bugreport::DoIt(int argc, const char** argv) {
+ if (argc > 2) return syntax_error("adb bugreport [PATH]");
// Gets bugreportz version.
std::string bugz_stdout, bugz_stderr;
DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr);
- int status = SendShellCommand(transport_type, serial, "bugreportz -v", false, &version_callback);
+ int status = SendShellCommand("bugreportz -v", false, &version_callback);
std::string bugz_version = android::base::Trim(bugz_stderr);
std::string bugz_output = android::base::Trim(bugz_stdout);
@@ -214,7 +214,7 @@
fprintf(stderr,
"Failed to get bugreportz version, which is only available on devices "
"running Android 7.0 or later.\nTrying a plain-text bug report instead.\n");
- return SendShellCommand(transport_type, serial, "bugreport", false);
+ return SendShellCommand("bugreport", false);
}
// But if user explicitly asked for a zipped bug report, fails instead (otherwise calling
@@ -265,7 +265,7 @@
bugz_command = "bugreportz";
}
BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this);
- return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
+ return SendShellCommand(bugz_command, false, &bugz_callback);
}
void Bugreport::UpdateProgress(const std::string& message, int progress_percentage) {
@@ -274,10 +274,9 @@
LinePrinter::INFO);
}
-int Bugreport::SendShellCommand(TransportType transport_type, const char* serial,
- const std::string& command, bool disable_shell_protocol,
+int Bugreport::SendShellCommand(const std::string& command, bool disable_shell_protocol,
StandardStreamsCallbackInterface* callback) {
- return send_shell_command(transport_type, serial, command, disable_shell_protocol, callback);
+ return send_shell_command(command, disable_shell_protocol, callback);
}
bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
diff --git a/adb/bugreport.h b/adb/bugreport.h
index d9a4468..413439b 100644
--- a/adb/bugreport.h
+++ b/adb/bugreport.h
@@ -29,14 +29,13 @@
public:
Bugreport() : line_printer_() {
}
- int DoIt(TransportType transport_type, const char* serial, int argc, const char** argv);
+ int DoIt(int argc, const char** argv);
protected:
// Functions below are abstractions of external functions so they can be
// mocked on tests.
virtual int SendShellCommand(
- TransportType transport_type, const char* serial, const std::string& command,
- bool disable_shell_protocol,
+ const std::string& command, bool disable_shell_protocol,
StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
virtual bool DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
index d3787b4..72ca59a 100644
--- a/adb/bugreport_test.cpp
+++ b/adb/bugreport_test.cpp
@@ -51,8 +51,8 @@
// Empty functions so tests don't need to be linked against commandline.cpp
DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
-int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
- bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
+int send_shell_command(const std::string& command, bool disable_shell_protocol,
+ StandardStreamsCallbackInterface* callback) {
ADD_FAILURE() << "send_shell_command() should have been mocked";
return -42;
}
@@ -62,7 +62,7 @@
kStreamStderr,
};
-// gmock black magic to provide a WithArg<4>(WriteOnStdout(output)) matcher
+// gmock black magic to provide a WithArg<2>(WriteOnStdout(output)) matcher
typedef void OnStandardStreamsCallbackFunction(StandardStreamsCallbackInterface*);
class OnStandardStreamsCallbackAction : public ActionInterface<OnStandardStreamsCallbackFunction> {
@@ -118,9 +118,8 @@
class BugreportMock : public Bugreport {
public:
- MOCK_METHOD5(SendShellCommand,
- int(TransportType transport_type, const char* serial, const std::string& command,
- bool disable_shell_protocol, StandardStreamsCallbackInterface* callback));
+ MOCK_METHOD3(SendShellCommand, int(const std::string& command, bool disable_shell_protocol,
+ StandardStreamsCallbackInterface* callback));
MOCK_METHOD4(DoSyncPull, bool(const std::vector<const char*>& srcs, const char* dst,
bool copy_attrs, const char* name));
MOCK_METHOD2(UpdateProgress, void(const std::string&, int));
@@ -136,10 +135,9 @@
}
void ExpectBugreportzVersion(const std::string& version) {
- EXPECT_CALL(br_,
- SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStderr(version.c_str())),
- WithArg<4>(ReturnCallbackDone(0))));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStderr(version.c_str())),
+ WithArg<2>(ReturnCallbackDone(0))));
}
void ExpectProgress(int progress_percentage, const std::string& file = "file.zip") {
@@ -153,26 +151,26 @@
// Tests when called with invalid number of arguments
TEST_F(BugreportTest, InvalidNumberArgs) {
const char* args[] = {"bugreport", "to", "principal"};
- ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 3, args));
+ ASSERT_EQ(1, br_.DoIt(3, args));
}
// Tests the 'adb bugreport' option when the device does not support 'bugreportz' - it falls back
// to the flat-file format ('bugreport' binary on device)
TEST_F(BugreportTest, NoArgumentsPreNDevice) {
// clang-format off
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStderr("")),
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStderr("")),
// Write some bogus output on stdout to make sure it's ignored
- WithArg<4>(WriteOnStdout("Dude, where is my bugreportz?")),
- WithArg<4>(ReturnCallbackDone(0))));
+ WithArg<2>(WriteOnStdout("Dude, where is my bugreportz?")),
+ WithArg<2>(ReturnCallbackDone(0))));
// clang-format on
std::string bugreport = "Reported the bug was.";
CaptureStdout();
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreport", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout(bugreport)), Return(0)));
+ EXPECT_CALL(br_, SendShellCommand("bugreport", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout(bugreport)), Return(0)));
const char* args[] = {"bugreport"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+ ASSERT_EQ(0, br_.DoIt(1, args));
ASSERT_THAT(GetCapturedStdout(), StrEq(bugreport));
}
@@ -183,15 +181,15 @@
std::string dest_file =
android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
- true, StrEq("pulling da_bugreport.zip")))
+ false, StrEq("pulling da_bugreport.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+ ASSERT_EQ(0, br_.DoIt(1, args));
}
// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.1 - it will
@@ -201,47 +199,47 @@
std::string dest_file =
android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
ExpectProgress(50, "da_bugreport.zip");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
- WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")),
- WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<2>(WriteOnStdout("PROGRESS:50/100\n")),
+ WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
- true, StrEq("pulling da_bugreport.zip")))
+ false, StrEq("pulling da_bugreport.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+ ASSERT_EQ(0, br_.DoIt(1, args));
}
// Tests 'adb bugreport file.zip' when it succeeds and device does not support progress.
TEST_F(BugreportTest, OkNDevice) {
ExpectBugreportzVersion("1.0");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, StrEq("pulling file.zip")))
+ false, StrEq("pulling file.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when it succeeds but response was sent in
// multiple buffer writers and without progress updates.
TEST_F(BugreportTest, OkNDeviceSplitBuffer) {
ExpectBugreportzVersion("1.0");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device")),
- WithArg<4>(WriteOnStdout("/bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device")),
+ WithArg<2>(WriteOnStdout("/bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, StrEq("pulling file.zip")))
+ false, StrEq("pulling file.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when it succeeds and displays progress.
@@ -252,32 +250,32 @@
ExpectProgress(50);
ExpectProgress(99);
// clang-format off
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
// NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
.WillOnce(DoAll(
// Name might change on OK, so make sure the right one is picked.
- WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
+ WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
// Progress line in one write
- WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")),
+ WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")),
// Add some bogus lines
- WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
+ WithArg<2>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
// Multiple progress lines in one write
- WithArg<4>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
+ WithArg<2>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
// Progress line in multiple writes
- WithArg<4>(WriteOnStdout("PROG")),
- WithArg<4>(WriteOnStdout("RESS:99")),
- WithArg<4>(WriteOnStdout("/100\n")),
+ WithArg<2>(WriteOnStdout("PROG")),
+ WithArg<2>(WriteOnStdout("RESS:99")),
+ WithArg<2>(WriteOnStdout("/100\n")),
// Split last message as well, just in case
- WithArg<4>(WriteOnStdout("OK:/device/bugreport")),
- WithArg<4>(WriteOnStdout(".zip")),
- WithArg<4>(ReturnCallbackDone())));
+ WithArg<2>(WriteOnStdout("OK:/device/bugreport")),
+ WithArg<2>(WriteOnStdout(".zip")),
+ WithArg<2>(ReturnCallbackDone())));
// clang-format on
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, StrEq("pulling file.zip")))
+ false, StrEq("pulling file.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when it succeeds and displays progress, even if progress recedes.
@@ -287,28 +285,28 @@
ExpectProgress(50);
ExpectProgress(75);
// clang-format off
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
// NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
.WillOnce(DoAll(
- WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
- WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
- WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")), // 50%
+ WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
+ WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
+ WithArg<2>(WriteOnStdout("PROGRESS:50/100\n")), // 50%
// 25% should be ignored becaused it receded.
- WithArg<4>(WriteOnStdout("PROGRESS:25/100\n")), // 25%
- WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
+ WithArg<2>(WriteOnStdout("PROGRESS:25/100\n")), // 25%
+ WithArg<2>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
// 75% should be ignored becaused it didn't change.
- WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
+ WithArg<2>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
// Try a receeding percentage with a different max progress
- WithArg<4>(WriteOnStdout("PROGRESS:700/1000\n")), // 70%
- WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ WithArg<2>(WriteOnStdout("PROGRESS:700/1000\n")), // 70%
+ WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
// clang-format on
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, StrEq("pulling file.zip")))
+ false, StrEq("pulling file.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when it succeeds and displays the initial progress of 0%
@@ -317,21 +315,21 @@
ExpectProgress(0);
ExpectProgress(1);
// clang-format off
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
// NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
.WillOnce(DoAll(
- WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
- WithArg<4>(WriteOnStdout("PROGRESS:1/100000\n")),
- WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
- WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
+ WithArg<2>(WriteOnStdout("PROGRESS:1/100000\n")),
+ WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
+ WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
// clang-format on
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, StrEq("pulling file.zip")))
+ false, StrEq("pulling file.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport dir' when it succeeds and destination is a directory.
@@ -341,30 +339,30 @@
std::string dest_file =
android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
- WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
- true, StrEq("pulling da_bugreport.zip")))
+ false, StrEq("pulling da_bugreport.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", td.path};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport file' when it succeeds
TEST_F(BugreportTest, OkNoExtension) {
ExpectBugreportzVersion("1.1");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip\n")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip\n")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, StrEq("pulling file.zip")))
+ false, StrEq("pulling file.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", "file"};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport dir' when it succeeds and destination is a directory and device runs N.
@@ -374,28 +372,28 @@
std::string dest_file =
android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
- WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
- true, StrEq("pulling da_bugreport.zip")))
+ false, StrEq("pulling da_bugreport.zip")))
.WillOnce(Return(true));
const char* args[] = {"bugreport", td.path};
- ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(0, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when the bugreport itself failed
TEST_F(BugreportTest, BugreportzReturnedFail) {
ExpectBugreportzVersion("1.1");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
.WillOnce(
- DoAll(WithArg<4>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<4>(ReturnCallbackDone())));
+ DoAll(WithArg<2>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<2>(ReturnCallbackDone())));
CaptureStderr();
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(-1, br_.DoIt(2, args));
ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
}
@@ -404,13 +402,13 @@
// multiple buffer writes
TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) {
ExpectBugreportzVersion("1.1");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("FAIL")), WithArg<4>(WriteOnStdout(":D'OH!\n")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("FAIL")), WithArg<2>(WriteOnStdout(":D'OH!\n")),
+ WithArg<2>(ReturnCallbackDone())));
CaptureStderr();
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(-1, br_.DoIt(2, args));
ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
}
@@ -418,23 +416,22 @@
// response.
TEST_F(BugreportTest, BugreportzReturnedUnsupported) {
ExpectBugreportzVersion("1.1");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("bugreportz? What am I, a zombie?")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("bugreportz? What am I, a zombie?")),
+ WithArg<2>(ReturnCallbackDone())));
CaptureStderr();
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(-1, br_.DoIt(2, args));
ASSERT_THAT(GetCapturedStderr(), HasSubstr("bugreportz? What am I, a zombie?"));
}
// Tests 'adb bugreport file.zip' when the bugreportz -v command failed
TEST_F(BugreportTest, BugreportzVersionFailed) {
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
- .WillOnce(Return(666));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _)).WillOnce(Return(666));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(666, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output.
@@ -442,29 +439,28 @@
ExpectBugreportzVersion("");
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(-1, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when the main bugreportz command failed
TEST_F(BugreportTest, BugreportzFailed) {
ExpectBugreportzVersion("1.1");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(Return(666));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)).WillOnce(Return(666));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(666, br_.DoIt(2, args));
}
// Tests 'adb bugreport file.zip' when the bugreport could not be pulled
TEST_F(BugreportTest, PullFails) {
ExpectBugreportzVersion("1.1");
- EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
- .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
- WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+ WithArg<2>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, HasSubstr("file.zip")))
+ false, HasSubstr("file.zip")))
.WillOnce(Return(false));
const char* args[] = {"bugreport", "file.zip"};
- ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+ ASSERT_EQ(1, br_.DoIt(2, args));
}
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 606203c..f0d0ce7 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -39,33 +39,7 @@
#include "sysdeps/chrono.h"
#include "transport.h"
-static std::string GetLogFilePath() {
-#if defined(_WIN32)
- const char log_name[] = "adb.log";
- WCHAR temp_path[MAX_PATH];
-
- // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
- DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
- if (nchars >= arraysize(temp_path) || nchars == 0) {
- // If string truncation or some other error.
- fatal("cannot retrieve temporary file path: %s\n",
- android::base::SystemErrorCodeToString(GetLastError()).c_str());
- }
-
- std::string temp_path_utf8;
- if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
- fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
- }
-
- return temp_path_utf8 + log_name;
-#else
- const char* tmp_dir = getenv("TMPDIR");
- if (tmp_dir == nullptr) tmp_dir = "/tmp";
- return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
-#endif
-}
-
-static void setup_daemon_logging(void) {
+static void setup_daemon_logging() {
const std::string log_file_path(GetLogFilePath());
int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
if (fd == -1) {
@@ -92,6 +66,16 @@
}
#endif
+void adb_server_cleanup() {
+ // Upon exit, we want to clean up in the following order:
+ // 1. close_smartsockets, so that we don't get any new clients
+ // 2. kick_all_transports, to avoid writing only part of a packet to a transport.
+ // 3. usb_cleanup, to tear down the USB stack.
+ close_smartsockets();
+ kick_all_transports();
+ usb_cleanup();
+}
+
int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd) {
#if defined(_WIN32)
// adb start-server starts us up with stdout and stderr hooked up to
@@ -111,12 +95,18 @@
SetConsoleCtrlHandler(ctrlc_handler, TRUE);
#else
signal(SIGINT, [](int) {
- android::base::quick_exit(0);
+ fdevent_run_on_main_thread([]() { android::base::quick_exit(0); });
});
#endif
- init_transport_registration();
+ if (is_daemon) {
+ close_stdin();
+ setup_daemon_logging();
+ }
+ android::base::at_quick_exit(adb_server_cleanup);
+
+ init_transport_registration();
init_mdns_transport_discovery();
usb_init();
@@ -137,11 +127,6 @@
std::this_thread::sleep_for(100ms);
}
- if (is_daemon) {
- close_stdin();
- setup_daemon_logging();
- }
-
adb_auth_init();
if (is_daemon) {
@@ -156,33 +141,38 @@
}
#endif
- // Inform our parent that we are up and running.
+ // Wait for the USB scan to complete before notifying the parent that we're up.
+ // We need to perform this in a thread, because we would otherwise block the event loop.
+ std::thread notify_thread([ack_reply_fd]() {
+ adb_wait_for_device_initialization();
- // Any error output written to stderr now goes to adb.log. We could
- // keep around a copy of the stderr fd and use that to write any errors
- // encountered by the following code, but that is probably overkill.
+ // Any error output written to stderr now goes to adb.log. We could
+ // keep around a copy of the stderr fd and use that to write any errors
+ // encountered by the following code, but that is probably overkill.
#if defined(_WIN32)
- const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
- const CHAR ack[] = "OK\n";
- const DWORD bytes_to_write = arraysize(ack) - 1;
- DWORD written = 0;
- if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
- fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
- android::base::SystemErrorCodeToString(GetLastError()).c_str());
- }
- if (written != bytes_to_write) {
- fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes",
- bytes_to_write, written);
- }
- CloseHandle(ack_reply_handle);
+ const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+ const CHAR ack[] = "OK\n";
+ const DWORD bytes_to_write = arraysize(ack) - 1;
+ DWORD written = 0;
+ if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
+ fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ }
+ if (written != bytes_to_write) {
+ fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes", bytes_to_write,
+ written);
+ }
+ CloseHandle(ack_reply_handle);
#else
- // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
- // "OKAY".
- if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
- fatal_errno("error writing ACK to fd %d", ack_reply_fd);
- }
- unix_close(ack_reply_fd);
+ // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
+ // "OKAY".
+ if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
+ fatal_errno("error writing ACK to fd %d", ack_reply_fd);
+ }
+ unix_close(ack_reply_fd);
#endif
+ });
+ notify_thread.detach();
}
D("Event loop starting");
diff --git a/adb/client/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp
index bfc8e16..ce57731 100644
--- a/adb/client/usb_dispatch.cpp
+++ b/adb/client/usb_dispatch.cpp
@@ -27,6 +27,14 @@
}
}
+void usb_cleanup() {
+ if (should_use_libusb()) {
+ libusb::usb_cleanup();
+ } else {
+ native::usb_cleanup();
+ }
+}
+
int usb_write(usb_handle* h, const void* data, int len) {
return should_use_libusb()
? libusb::usb_write(reinterpret_cast<libusb::usb_handle*>(h), data, len)
@@ -48,3 +56,9 @@
should_use_libusb() ? libusb::usb_kick(reinterpret_cast<libusb::usb_handle*>(h))
: native::usb_kick(reinterpret_cast<native::usb_handle*>(h));
}
+
+size_t usb_get_max_packet_size(usb_handle* h) {
+ return should_use_libusb()
+ ? libusb::usb_get_max_packet_size(reinterpret_cast<libusb::usb_handle*>(h))
+ : native::usb_get_max_packet_size(reinterpret_cast<native::usb_handle*>(h));
+}
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 7adb262..7025f28 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -37,6 +37,7 @@
#include <android-base/strings.h>
#include "adb.h"
+#include "adb_utils.h"
#include "transport.h"
#include "usb.h"
@@ -62,12 +63,11 @@
using unique_device_handle = std::unique_ptr<libusb_device_handle, DeviceHandleDeleter>;
struct transfer_info {
- transfer_info(const char* name, uint16_t zero_mask) :
- name(name),
- transfer(libusb_alloc_transfer(0)),
- zero_mask(zero_mask)
- {
- }
+ transfer_info(const char* name, uint16_t zero_mask, bool is_bulk_out)
+ : name(name),
+ transfer(libusb_alloc_transfer(0)),
+ is_bulk_out(is_bulk_out),
+ zero_mask(zero_mask) {}
~transfer_info() {
libusb_free_transfer(transfer);
@@ -75,6 +75,7 @@
const char* name;
libusb_transfer* transfer;
+ bool is_bulk_out;
bool transfer_complete;
std::condition_variable cv;
std::mutex mutex;
@@ -91,17 +92,17 @@
struct usb_handle : public ::usb_handle {
usb_handle(const std::string& device_address, const std::string& serial,
unique_device_handle&& device_handle, uint8_t interface, uint8_t bulk_in,
- uint8_t bulk_out, size_t zero_mask)
+ uint8_t bulk_out, size_t zero_mask, size_t max_packet_size)
: device_address(device_address),
serial(serial),
closing(false),
device_handle(device_handle.release()),
- read("read", zero_mask),
- write("write", zero_mask),
+ read("read", zero_mask, false),
+ write("write", zero_mask, true),
interface(interface),
bulk_in(bulk_in),
- bulk_out(bulk_out) {
- }
+ bulk_out(bulk_out),
+ max_packet_size(max_packet_size) {}
~usb_handle() {
Close();
@@ -144,19 +145,43 @@
uint8_t interface;
uint8_t bulk_in;
uint8_t bulk_out;
+
+ size_t max_packet_size;
};
static auto& usb_handles = *new std::unordered_map<std::string, std::unique_ptr<usb_handle>>();
static auto& usb_handles_mutex = *new std::mutex();
-static std::thread* device_poll_thread = nullptr;
-static std::atomic<bool> terminate_device_poll_thread(false);
+static libusb_hotplug_callback_handle hotplug_handle;
static std::string get_device_address(libusb_device* device) {
return StringPrintf("usb:%d:%d", libusb_get_bus_number(device),
libusb_get_device_address(device));
}
+#if defined(__linux__)
+static std::string get_device_serial_path(libusb_device* device) {
+ uint8_t ports[7];
+ int port_count = libusb_get_port_numbers(device, ports, 7);
+ if (port_count < 0) return "";
+
+ std::string path =
+ StringPrintf("/sys/bus/usb/devices/%d-%d", libusb_get_bus_number(device), ports[0]);
+ for (int port = 1; port < port_count; ++port) {
+ path += StringPrintf(".%d", ports[port]);
+ }
+ path += "/serial";
+ return path;
+}
+
+static std::string get_device_dev_path(libusb_device* device) {
+ uint8_t ports[7];
+ int port_count = libusb_get_port_numbers(device, ports, 7);
+ if (port_count < 0) return "";
+ return StringPrintf("/dev/bus/usb/%03u/%03u", libusb_get_bus_number(device), ports[0]);
+}
+#endif
+
static bool endpoint_is_output(uint8_t endpoint) {
return (endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT;
}
@@ -166,191 +191,268 @@
(write_length & zero_mask) == 0;
}
-static void poll_for_devices() {
- libusb_device** list;
- adb_thread_setname("device poll");
- while (!terminate_device_poll_thread) {
- const ssize_t device_count = libusb_get_device_list(nullptr, &list);
+static void process_device(libusb_device* device) {
+ std::string device_address = get_device_address(device);
+ std::string device_serial;
- LOG(VERBOSE) << "found " << device_count << " attached devices";
-
- for (ssize_t i = 0; i < device_count; ++i) {
- libusb_device* device = list[i];
- std::string device_address = get_device_address(device);
- std::string device_serial;
-
- // Figure out if we want to open the device.
- libusb_device_descriptor device_desc;
- int rc = libusb_get_device_descriptor(device, &device_desc);
- if (rc != 0) {
- LOG(WARNING) << "failed to get device descriptor for device at " << device_address
- << ": " << libusb_error_name(rc);
- }
-
- if (device_desc.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
- // Assume that all Android devices have the device class set to per interface.
- // TODO: Is this assumption valid?
- LOG(VERBOSE) << "skipping device with incorrect class at " << device_address;
- continue;
- }
-
- libusb_config_descriptor* config_raw;
- rc = libusb_get_active_config_descriptor(device, &config_raw);
- if (rc != 0) {
- LOG(WARNING) << "failed to get active config descriptor for device at "
- << device_address << ": " << libusb_error_name(rc);
- continue;
- }
- const unique_config_descriptor config(config_raw);
-
- // Use size_t for interface_num so <iostream>s don't mangle it.
- size_t interface_num;
- uint16_t zero_mask;
- uint8_t bulk_in = 0, bulk_out = 0;
- bool found_adb = false;
-
- for (interface_num = 0; interface_num < config->bNumInterfaces; ++interface_num) {
- const libusb_interface& interface = config->interface[interface_num];
- if (interface.num_altsetting != 1) {
- // Assume that interfaces with alternate settings aren't adb interfaces.
- // TODO: Is this assumption valid?
- LOG(VERBOSE) << "skipping interface with incorrect num_altsetting at "
- << device_address << " (interface " << interface_num << ")";
- continue;
- }
-
- const libusb_interface_descriptor& interface_desc = interface.altsetting[0];
- if (!is_adb_interface(interface_desc.bInterfaceClass,
- interface_desc.bInterfaceSubClass,
- interface_desc.bInterfaceProtocol)) {
- LOG(VERBOSE) << "skipping non-adb interface at " << device_address
- << " (interface " << interface_num << ")";
- continue;
- }
-
- LOG(VERBOSE) << "found potential adb interface at " << device_address
- << " (interface " << interface_num << ")";
-
- bool found_in = false;
- bool found_out = false;
- for (size_t endpoint_num = 0; endpoint_num < interface_desc.bNumEndpoints;
- ++endpoint_num) {
- const auto& endpoint_desc = interface_desc.endpoint[endpoint_num];
- const uint8_t endpoint_addr = endpoint_desc.bEndpointAddress;
- const uint8_t endpoint_attr = endpoint_desc.bmAttributes;
-
- const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK;
-
- if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) {
- continue;
- }
-
- if (endpoint_is_output(endpoint_addr) && !found_out) {
- found_out = true;
- bulk_out = endpoint_addr;
- zero_mask = endpoint_desc.wMaxPacketSize - 1;
- } else if (!endpoint_is_output(endpoint_addr) && !found_in) {
- found_in = true;
- bulk_in = endpoint_addr;
- }
- }
-
- if (found_in && found_out) {
- found_adb = true;
- break;
- } else {
- LOG(VERBOSE) << "rejecting potential adb interface at " << device_address
- << "(interface " << interface_num << "): missing bulk endpoints "
- << "(found_in = " << found_in << ", found_out = " << found_out
- << ")";
- }
- }
-
- if (!found_adb) {
- LOG(VERBOSE) << "skipping device with no adb interfaces at " << device_address;
- continue;
- }
-
- {
- std::unique_lock<std::mutex> lock(usb_handles_mutex);
- if (usb_handles.find(device_address) != usb_handles.end()) {
- LOG(VERBOSE) << "device at " << device_address
- << " has already been registered, skipping";
- continue;
- }
- }
-
- libusb_device_handle* handle_raw;
- rc = libusb_open(list[i], &handle_raw);
- if (rc != 0) {
- LOG(WARNING) << "failed to open usb device at " << device_address << ": "
- << libusb_error_name(rc);
- continue;
- }
-
- unique_device_handle handle(handle_raw);
- LOG(DEBUG) << "successfully opened adb device at " << device_address << ", "
- << StringPrintf("bulk_in = %#x, bulk_out = %#x", bulk_in, bulk_out);
-
- device_serial.resize(255);
- rc = libusb_get_string_descriptor_ascii(
- handle_raw, device_desc.iSerialNumber,
- reinterpret_cast<unsigned char*>(&device_serial[0]), device_serial.length());
- if (rc == 0) {
- LOG(WARNING) << "received empty serial from device at " << device_address;
- continue;
- } else if (rc < 0) {
- LOG(WARNING) << "failed to get serial from device at " << device_address
- << libusb_error_name(rc);
- continue;
- }
- device_serial.resize(rc);
-
- // Try to reset the device.
- rc = libusb_reset_device(handle_raw);
- if (rc != 0) {
- LOG(WARNING) << "failed to reset opened device '" << device_serial
- << "': " << libusb_error_name(rc);
- continue;
- }
-
- // WARNING: this isn't released via RAII.
- rc = libusb_claim_interface(handle.get(), interface_num);
- if (rc != 0) {
- LOG(WARNING) << "failed to claim adb interface for device '" << device_serial << "'"
- << libusb_error_name(rc);
- continue;
- }
-
- for (uint8_t endpoint : {bulk_in, bulk_out}) {
- rc = libusb_clear_halt(handle.get(), endpoint);
- if (rc != 0) {
- LOG(WARNING) << "failed to clear halt on device '" << device_serial
- << "' endpoint 0x" << std::hex << endpoint << ": "
- << libusb_error_name(rc);
- libusb_release_interface(handle.get(), interface_num);
- continue;
- }
- }
-
- auto result =
- std::make_unique<usb_handle>(device_address, device_serial, std::move(handle),
- interface_num, bulk_in, bulk_out, zero_mask);
- usb_handle* usb_handle_raw = result.get();
-
- {
- std::unique_lock<std::mutex> lock(usb_handles_mutex);
- usb_handles[device_address] = std::move(result);
- }
-
- register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), 1);
-
- LOG(INFO) << "registered new usb device '" << device_serial << "'";
- }
- libusb_free_device_list(list, 1);
-
- std::this_thread::sleep_for(500ms);
+ // Figure out if we want to open the device.
+ libusb_device_descriptor device_desc;
+ int rc = libusb_get_device_descriptor(device, &device_desc);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to get device descriptor for device at " << device_address << ": "
+ << libusb_error_name(rc);
+ return;
}
+
+ if (device_desc.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
+ // Assume that all Android devices have the device class set to per interface.
+ // TODO: Is this assumption valid?
+ LOG(VERBOSE) << "skipping device with incorrect class at " << device_address;
+ return;
+ }
+
+ libusb_config_descriptor* config_raw;
+ rc = libusb_get_active_config_descriptor(device, &config_raw);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to get active config descriptor for device at " << device_address
+ << ": " << libusb_error_name(rc);
+ return;
+ }
+ const unique_config_descriptor config(config_raw);
+
+ // Use size_t for interface_num so <iostream>s don't mangle it.
+ size_t interface_num;
+ uint16_t zero_mask;
+ uint8_t bulk_in = 0, bulk_out = 0;
+ size_t packet_size = 0;
+ bool found_adb = false;
+
+ for (interface_num = 0; interface_num < config->bNumInterfaces; ++interface_num) {
+ const libusb_interface& interface = config->interface[interface_num];
+ if (interface.num_altsetting != 1) {
+ // Assume that interfaces with alternate settings aren't adb interfaces.
+ // TODO: Is this assumption valid?
+ LOG(VERBOSE) << "skipping interface with incorrect num_altsetting at " << device_address
+ << " (interface " << interface_num << ")";
+ continue;
+ }
+
+ const libusb_interface_descriptor& interface_desc = interface.altsetting[0];
+ if (!is_adb_interface(interface_desc.bInterfaceClass, interface_desc.bInterfaceSubClass,
+ interface_desc.bInterfaceProtocol)) {
+ LOG(VERBOSE) << "skipping non-adb interface at " << device_address << " (interface "
+ << interface_num << ")";
+ continue;
+ }
+
+ LOG(VERBOSE) << "found potential adb interface at " << device_address << " (interface "
+ << interface_num << ")";
+
+ bool found_in = false;
+ bool found_out = false;
+ for (size_t endpoint_num = 0; endpoint_num < interface_desc.bNumEndpoints; ++endpoint_num) {
+ const auto& endpoint_desc = interface_desc.endpoint[endpoint_num];
+ const uint8_t endpoint_addr = endpoint_desc.bEndpointAddress;
+ const uint8_t endpoint_attr = endpoint_desc.bmAttributes;
+
+ const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK;
+
+ if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) {
+ continue;
+ }
+
+ if (endpoint_is_output(endpoint_addr) && !found_out) {
+ found_out = true;
+ bulk_out = endpoint_addr;
+ zero_mask = endpoint_desc.wMaxPacketSize - 1;
+ } else if (!endpoint_is_output(endpoint_addr) && !found_in) {
+ found_in = true;
+ bulk_in = endpoint_addr;
+ }
+
+ size_t endpoint_packet_size = endpoint_desc.wMaxPacketSize;
+ CHECK(endpoint_packet_size != 0);
+ if (packet_size == 0) {
+ packet_size = endpoint_packet_size;
+ } else {
+ CHECK(packet_size == endpoint_packet_size);
+ }
+ }
+
+ if (found_in && found_out) {
+ found_adb = true;
+ break;
+ } else {
+ LOG(VERBOSE) << "rejecting potential adb interface at " << device_address
+ << "(interface " << interface_num << "): missing bulk endpoints "
+ << "(found_in = " << found_in << ", found_out = " << found_out << ")";
+ }
+ }
+
+ if (!found_adb) {
+ LOG(VERBOSE) << "skipping device with no adb interfaces at " << device_address;
+ return;
+ }
+
+ {
+ std::unique_lock<std::mutex> lock(usb_handles_mutex);
+ if (usb_handles.find(device_address) != usb_handles.end()) {
+ LOG(VERBOSE) << "device at " << device_address
+ << " has already been registered, skipping";
+ return;
+ }
+ }
+
+ bool writable = true;
+ libusb_device_handle* handle_raw = nullptr;
+ rc = libusb_open(device, &handle_raw);
+ unique_device_handle handle(handle_raw);
+ if (rc == 0) {
+ LOG(DEBUG) << "successfully opened adb device at " << device_address << ", "
+ << StringPrintf("bulk_in = %#x, bulk_out = %#x", bulk_in, bulk_out);
+
+ device_serial.resize(255);
+ rc = libusb_get_string_descriptor_ascii(handle_raw, device_desc.iSerialNumber,
+ reinterpret_cast<unsigned char*>(&device_serial[0]),
+ device_serial.length());
+ if (rc == 0) {
+ LOG(WARNING) << "received empty serial from device at " << device_address;
+ return;
+ } else if (rc < 0) {
+ LOG(WARNING) << "failed to get serial from device at " << device_address
+ << libusb_error_name(rc);
+ return;
+ }
+ device_serial.resize(rc);
+
+ // WARNING: this isn't released via RAII.
+ rc = libusb_claim_interface(handle.get(), interface_num);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to claim adb interface for device '" << device_serial << "'"
+ << libusb_error_name(rc);
+ return;
+ }
+
+ for (uint8_t endpoint : {bulk_in, bulk_out}) {
+ rc = libusb_clear_halt(handle.get(), endpoint);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to clear halt on device '" << device_serial
+ << "' endpoint 0x" << std::hex << endpoint << ": "
+ << libusb_error_name(rc);
+ libusb_release_interface(handle.get(), interface_num);
+ return;
+ }
+ }
+ } else {
+ LOG(WARNING) << "failed to open usb device at " << device_address << ": "
+ << libusb_error_name(rc);
+ writable = false;
+
+#if defined(__linux__)
+ // libusb doesn't think we should be messing around with devices we don't have
+ // write access to, but Linux at least lets us get the serial number anyway.
+ if (!android::base::ReadFileToString(get_device_serial_path(device), &device_serial)) {
+ // We don't actually want to treat an unknown serial as an error because
+ // devices aren't able to communicate a serial number in early bringup.
+ // http://b/20883914
+ device_serial = "unknown";
+ }
+ device_serial = android::base::Trim(device_serial);
+#else
+ // On Mac OS and Windows, we're screwed. But I don't think this situation actually
+ // happens on those OSes.
+ return;
+#endif
+ }
+
+ auto result =
+ std::make_unique<usb_handle>(device_address, device_serial, std::move(handle),
+ interface_num, bulk_in, bulk_out, zero_mask, packet_size);
+ usb_handle* usb_handle_raw = result.get();
+
+ {
+ std::unique_lock<std::mutex> lock(usb_handles_mutex);
+ usb_handles[device_address] = std::move(result);
+
+ register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(),
+ writable);
+ }
+ LOG(INFO) << "registered new usb device '" << device_serial << "'";
+}
+
+static std::atomic<int> connecting_devices(0);
+
+static void device_connected(libusb_device* device) {
+#if defined(__linux__)
+ // Android's host linux libusb uses netlink instead of udev for device hotplug notification,
+ // which means we can get hotplug notifications before udev has updated ownership/perms on the
+ // device. Since we're not going to be able to link against the system's libudev any time soon,
+ // hack around this by inserting a sleep.
+ auto thread = std::thread([device]() {
+ std::string device_path = get_device_dev_path(device);
+ std::this_thread::sleep_for(1s);
+
+ process_device(device);
+ if (--connecting_devices == 0) {
+ adb_notify_device_scan_complete();
+ }
+ });
+ thread.detach();
+#else
+ process_device(device);
+#endif
+}
+
+static void device_disconnected(libusb_device* device) {
+ std::string device_address = get_device_address(device);
+
+ LOG(INFO) << "device disconnected: " << device_address;
+ std::unique_lock<std::mutex> lock(usb_handles_mutex);
+ auto it = usb_handles.find(device_address);
+ if (it != usb_handles.end()) {
+ if (!it->second->device_handle) {
+ // If the handle is null, we were never able to open the device.
+
+ // Temporarily release the usb handles mutex to avoid deadlock.
+ std::unique_ptr<usb_handle> handle = std::move(it->second);
+ usb_handles.erase(it);
+ lock.unlock();
+ unregister_usb_transport(handle.get());
+ lock.lock();
+ } else {
+ // Closure of the transport will erase the usb_handle.
+ }
+ }
+}
+
+static auto& hotplug_queue = *new BlockingQueue<std::pair<libusb_hotplug_event, libusb_device*>>();
+static void hotplug_thread() {
+ adb_thread_setname("libusb hotplug");
+ while (true) {
+ hotplug_queue.PopAll([](std::pair<libusb_hotplug_event, libusb_device*> pair) {
+ libusb_hotplug_event event = pair.first;
+ libusb_device* device = pair.second;
+ if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
+ device_connected(device);
+ } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
+ device_disconnected(device);
+ }
+ });
+ }
+}
+
+static int hotplug_callback(libusb_context*, libusb_device* device, libusb_hotplug_event event,
+ void*) {
+ // We're called with the libusb lock taken. Call these on a separate thread outside of this
+ // function so that the usb_handle mutex is always taken before the libusb mutex.
+ static std::once_flag once;
+ std::call_once(once, []() { std::thread(hotplug_thread).detach(); });
+
+ if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
+ ++connecting_devices;
+ }
+ hotplug_queue.Push({event, device});
+ return 0;
}
void usb_init() {
@@ -360,6 +462,17 @@
LOG(FATAL) << "failed to initialize libusb: " << libusb_error_name(rc);
}
+ // Register the hotplug callback.
+ rc = libusb_hotplug_register_callback(
+ nullptr, static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
+ LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
+ LIBUSB_CLASS_PER_INTERFACE, hotplug_callback, nullptr, &hotplug_handle);
+
+ if (rc != LIBUSB_SUCCESS) {
+ LOG(FATAL) << "failed to register libusb hotplug callback";
+ }
+
// Spawn a thread for libusb_handle_events.
std::thread([]() {
adb_thread_setname("libusb");
@@ -367,19 +480,10 @@
libusb_handle_events(nullptr);
}
}).detach();
+}
- // Spawn a thread to do device enumeration.
- // TODO: Use libusb_hotplug_* instead?
- device_poll_thread = new std::thread(poll_for_devices);
- android::base::at_quick_exit([]() {
- terminate_device_poll_thread = true;
- std::unique_lock<std::mutex> lock(usb_handles_mutex);
- for (auto& it : usb_handles) {
- it.second->Close();
- }
- lock.unlock();
- device_poll_thread->join();
- });
+void usb_cleanup() {
+ libusb_hotplug_deregister_callback(nullptr, hotplug_handle);
}
// Dispatch a libusb transfer, unlock |device_lock|, and then wait for the result.
@@ -405,7 +509,8 @@
return;
}
- if (transfer->actual_length != transfer->length) {
+ // usb_read() can return when receiving some data.
+ if (info->is_bulk_out && transfer->actual_length != transfer->length) {
LOG(DEBUG) << info->name << " transfer incomplete, resubmitting";
transfer->length -= transfer->actual_length;
transfer->buffer += transfer->actual_length;
@@ -499,8 +604,12 @@
info->transfer->num_iso_packets = 0;
int rc = perform_usb_transfer(h, info, std::move(lock));
- LOG(DEBUG) << "usb_read(" << len << ") = " << rc;
- return rc;
+ LOG(DEBUG) << "usb_read(" << len << ") = " << rc << ", actual_length "
+ << info->transfer->actual_length;
+ if (rc < 0) {
+ return rc;
+ }
+ return info->transfer->actual_length;
}
int usb_close(usb_handle* h) {
@@ -516,4 +625,10 @@
void usb_kick(usb_handle* h) {
h->Close();
}
+
+size_t usb_get_max_packet_size(usb_handle* h) {
+ CHECK(h->max_packet_size != 0);
+ return h->max_packet_size;
+}
+
} // namespace libusb
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 13b7674..a7df0ed 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -65,6 +65,7 @@
unsigned char ep_in;
unsigned char ep_out;
+ size_t max_packet_size;
unsigned zero_mask;
unsigned writeable = 1;
@@ -120,9 +121,9 @@
}
static void find_usb_device(const std::string& base,
- void (*register_device_callback)
- (const char*, const char*, unsigned char, unsigned char, int, int, unsigned))
-{
+ void (*register_device_callback)(const char*, const char*,
+ unsigned char, unsigned char, int, int,
+ unsigned, size_t)) {
std::unique_ptr<DIR, int(*)(DIR*)> bus_dir(opendir(base.c_str()), closedir);
if (!bus_dir) return;
@@ -144,6 +145,7 @@
struct usb_interface_descriptor* interface;
struct usb_endpoint_descriptor *ep1, *ep2;
unsigned zero_mask = 0;
+ size_t max_packet_size = 0;
unsigned vid, pid;
if (contains_non_digit(de->d_name)) continue;
@@ -251,7 +253,8 @@
continue;
}
/* aproto 01 needs 0 termination */
- if(interface->bInterfaceProtocol == 0x01) {
+ if (interface->bInterfaceProtocol == 0x01) {
+ max_packet_size = ep1->wMaxPacketSize;
zero_mask = ep1->wMaxPacketSize - 1;
}
@@ -281,9 +284,9 @@
}
}
- register_device_callback(dev_name.c_str(), devpath,
- local_ep_in, local_ep_out,
- interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
+ register_device_callback(dev_name.c_str(), devpath, local_ep_in,
+ local_ep_out, interface->bInterfaceNumber,
+ device->iSerialNumber, zero_mask, max_packet_size);
break;
}
} else {
@@ -401,7 +404,6 @@
}
}
-
int usb_write(usb_handle *h, const void *_data, int len)
{
D("++ usb_write ++");
@@ -429,19 +431,16 @@
int n;
D("++ usb_read ++");
- while(len > 0) {
+ int orig_len = len;
+ while (len == orig_len) {
int xfer = len;
D("[ usb read %d fd = %d], path=%s", xfer, h->fd, h->path.c_str());
n = usb_bulk_read(h, data, xfer);
D("[ usb read %d ] = %d, path=%s", xfer, n, h->path.c_str());
- if(n != xfer) {
+ if (n <= 0) {
if((errno == ETIMEDOUT) && (h->fd != -1)) {
D("[ timeout ]");
- if(n > 0){
- data += n;
- len -= n;
- }
continue;
}
D("ERROR: n = %d, errno = %d (%s)",
@@ -449,12 +448,12 @@
return -1;
}
- len -= xfer;
- data += xfer;
+ len -= n;
+ data += n;
}
D("-- usb_read --");
- return 0;
+ return orig_len - len;
}
void usb_kick(usb_handle* h) {
@@ -501,10 +500,13 @@
return 0;
}
-static void register_device(const char* dev_name, const char* dev_path,
- unsigned char ep_in, unsigned char ep_out,
- int interface, int serial_index,
- unsigned zero_mask) {
+size_t usb_get_max_packet_size(usb_handle* h) {
+ return h->max_packet_size;
+}
+
+static void register_device(const char* dev_name, const char* dev_path, unsigned char ep_in,
+ unsigned char ep_out, int interface, int serial_index,
+ unsigned zero_mask, size_t max_packet_size) {
// Since Linux will not reassign the device ID (and dev_name) as long as the
// device is open, we can add to the list here once we open it and remove
// from the list when we're finally closed and everything will work out
@@ -527,6 +529,7 @@
usb->ep_in = ep_in;
usb->ep_out = ep_out;
usb->zero_mask = zero_mask;
+ usb->max_packet_size = max_packet_size;
// Initialize mark so we don't get garbage collected after the device scan.
usb->mark = true;
@@ -574,7 +577,7 @@
register_usb_transport(done_usb, serial.c_str(), dev_path, done_usb->writeable);
}
-static void device_poll_thread(void*) {
+static void device_poll_thread() {
adb_thread_setname("device poll");
D("Created device thread");
while (true) {
@@ -593,8 +596,9 @@
actions.sa_handler = [](int) {};
sigaction(SIGALRM, &actions, nullptr);
- if (!adb_thread_create(device_poll_thread, nullptr)) {
- fatal_errno("cannot create device_poll thread");
- }
+ std::thread(device_poll_thread).detach();
}
+
+void usb_cleanup() {}
+
} // namespace native
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index d4fc7c0..4e1480f 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -38,6 +38,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
#include "adb.h"
#include "transport.h"
@@ -51,15 +52,21 @@
UInt8 bulkOut;
IOUSBInterfaceInterface190** interface;
unsigned int zero_mask;
+ size_t max_packet_size;
// For garbage collecting disconnected devices.
bool mark;
std::string devpath;
std::atomic<bool> dead;
- usb_handle() : bulkIn(0), bulkOut(0), interface(nullptr),
- zero_mask(0), mark(false), dead(false) {
- }
+ usb_handle()
+ : bulkIn(0),
+ bulkOut(0),
+ interface(nullptr),
+ zero_mask(0),
+ max_packet_size(0),
+ mark(false),
+ dead(false) {}
};
static std::atomic<bool> usb_inited_flag;
@@ -390,6 +397,7 @@
}
handle->zero_mask = maxPacketSize - 1;
+ handle->max_packet_size = maxPacketSize;
}
handle->interface = interface;
@@ -405,7 +413,7 @@
std::mutex& operate_device_lock = *new std::mutex();
-static void RunLoopThread(void* unused) {
+static void RunLoopThread() {
adb_thread_setname("RunLoop");
VLOG(USB) << "RunLoopThread started";
@@ -422,7 +430,7 @@
VLOG(USB) << "RunLoopThread done";
}
-static void usb_cleanup() {
+void usb_cleanup() NO_THREAD_SAFETY_ANALYSIS {
VLOG(USB) << "usb_cleanup";
// Wait until usb operations in RunLoopThread finish, and prevent further operations.
operate_device_lock.lock();
@@ -432,13 +440,9 @@
void usb_init() {
static bool initialized = false;
if (!initialized) {
- atexit(usb_cleanup);
-
usb_inited_flag = false;
- if (!adb_thread_create(RunLoopThread, nullptr)) {
- fatal_errno("cannot create RunLoop thread");
- }
+ std::thread(RunLoopThread).detach();
// Wait for initialization to finish
while (!usb_inited_flag) {
@@ -520,7 +524,7 @@
}
if (kIOReturnSuccess == result)
- return 0;
+ return numBytes;
else {
LOG(ERROR) << "usb_read failed with status: " << std::hex << result;
}
@@ -560,4 +564,9 @@
std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
usb_kick_locked(handle);
}
+
+size_t usb_get_max_packet_size(usb_handle* handle) {
+ return handle->max_packet_size;
+}
+
} // namespace native
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 640e91e..1620e6e 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -65,6 +65,9 @@
/// Interface name
wchar_t* interface_name;
+ /// Maximum packet size.
+ unsigned max_packet_size;
+
/// Mask for determining when to use zero length packets
unsigned zero_mask;
};
@@ -103,7 +106,7 @@
/// Entry point for thread that polls (every second) for new usb interfaces.
/// This routine calls find_devices in infinite loop.
-static void device_poll_thread(void*);
+static void device_poll_thread();
/// Initializes this module
void usb_init();
@@ -174,7 +177,7 @@
return 1;
}
-void device_poll_thread(void*) {
+void device_poll_thread() {
adb_thread_setname("Device Poll");
D("Created device thread");
@@ -203,7 +206,7 @@
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
-static void _power_notification_thread(void*) {
+static void _power_notification_thread() {
// This uses a thread with its own window message pump to get power
// notifications. If adb runs from a non-interactive service account, this
// might not work (not sure). If that happens to not work, we could use
@@ -258,14 +261,12 @@
}
void usb_init() {
- if (!adb_thread_create(device_poll_thread, nullptr)) {
- fatal_errno("cannot create device poll thread");
- }
- if (!adb_thread_create(_power_notification_thread, nullptr)) {
- fatal_errno("cannot create power notification thread");
- }
+ std::thread(device_poll_thread).detach();
+ std::thread(_power_notification_thread).detach();
}
+void usb_cleanup() {}
+
usb_handle* do_usb_open(const wchar_t* interface_name) {
unsigned long name_len = 0;
@@ -419,6 +420,7 @@
unsigned long time_out = 0;
unsigned long read = 0;
int err = 0;
+ int orig_len = len;
D("usb_read %d", len);
if (NULL == handle) {
@@ -427,9 +429,8 @@
goto fail;
}
- while (len > 0) {
- if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read,
- time_out)) {
+ while (len == orig_len) {
+ if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read, time_out)) {
D("AdbReadEndpointSync failed: %s",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
err = EIO;
@@ -437,11 +438,11 @@
}
D("usb_read got: %ld, expected: %d", read, len);
- data = (char *)data + read;
+ data = (char*)data + read;
len -= read;
}
- return 0;
+ return orig_len - len;
fail:
// Any failure should cause us to kick the device instead of leaving it a
@@ -526,6 +527,10 @@
return 0;
}
+size_t usb_get_max_packet_size(usb_handle* handle) {
+ return handle->max_packet_size;
+}
+
int recognized_device(usb_handle* handle) {
if (NULL == handle)
return 0;
@@ -561,6 +566,7 @@
AdbEndpointInformation endpoint_info;
// assuming zero is a valid bulk endpoint ID
if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
+ handle->max_packet_size = endpoint_info.max_packet_size;
handle->zero_mask = endpoint_info.max_packet_size - 1;
D("device zero_mask: 0x%x", handle->zero_mask);
} else {
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 4979eef..d126f52 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -62,26 +62,23 @@
#include "shell_service.h"
#include "sysdeps/chrono.h"
-static int install_app(TransportType t, const char* serial, int argc, const char** argv);
-static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
-static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv);
-static int install_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
-static int uninstall_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
+static int install_app(int argc, const char** argv);
+static int install_multiple_app(int argc, const char** argv);
+static int uninstall_app(int argc, const char** argv);
+static int install_app_legacy(int argc, const char** argv);
+static int uninstall_app_legacy(int argc, const char** argv);
-static auto& gProductOutPath = *new std::string();
extern int gListenAll;
DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
-static std::string product_file(const char *extra) {
- if (gProductOutPath.empty()) {
- fprintf(stderr, "adb: Product directory not specified; "
- "use -p or define ANDROID_PRODUCT_OUT\n");
+static std::string product_file(const char* file) {
+ const char* ANDROID_PRODUCT_OUT = getenv("ANDROID_PRODUCT_OUT");
+ if (ANDROID_PRODUCT_OUT == nullptr) {
+ fprintf(stderr, "adb: product directory not specified; set $ANDROID_PRODUCT_OUT\n");
exit(1);
}
-
- return android::base::StringPrintf("%s%s%s",
- gProductOutPath.c_str(), OS_PATH_SEPARATOR_STR, extra);
+ return android::base::StringPrintf("%s%s%s", ANDROID_PRODUCT_OUT, OS_PATH_SEPARATOR_STR, file);
}
static void help() {
@@ -92,11 +89,8 @@
" -a listen on all network interfaces, not just localhost\n"
" -d use USB device (error if multiple devices connected)\n"
" -e use TCP/IP device (error if multiple TCP/IP devices available)\n"
- " -s SERIAL\n"
- " use device with given serial number (overrides $ANDROID_SERIAL)\n"
- " -p PRODUCT\n"
- " name or path ('angler'/'out/target/product/angler');\n"
- " default $ANDROID_PRODUCT_OUT\n"
+ " -s SERIAL use device with given serial (overrides $ANDROID_SERIAL)\n"
+ " -t ID use device with given transport id\n"
" -H name of adb server host [default=localhost]\n"
" -P port of adb server [default=5037]\n"
" -L SOCKET listen on given socket for adb server [default=tcp:localhost:5037]\n"
@@ -133,14 +127,14 @@
" reverse --remove-all remove all reverse socket connections from device\n"
"\n"
"file transfer:\n"
- " push LOCAL... REMOTE\n"
+ " push [--sync] LOCAL... REMOTE\n"
" copy local files/directories to device\n"
+ " --sync: only push files that are newer on the host than the device\n"
" pull [-a] REMOTE... LOCAL\n"
" copy files/dirs from device\n"
" -a: preserve file timestamp and mode\n"
- " sync [DIR]\n"
- " copy all changed files to device; if DIR is \"system\", \"vendor\", \"oem\",\n"
- " or \"data\", only sync that partition (default all)\n"
+ " sync [system|vendor|oem|data|all]\n"
+ " sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
" -l: list but don't copy\n"
"\n"
"shell:\n"
@@ -169,15 +163,7 @@
" '-k': keep the data and cache directories\n"
"\n"
"backup/restore:\n"
- " backup [-f FILE] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] [PACKAGE...]\n"
- " write an archive of the device's data to FILE [default=backup.adb]\n"
- " package list optional if -all/-shared are supplied\n"
- " -apk/-noapk: do/don't back up .apk files (default -noapk)\n"
- " -obb/-noobb: do/don't back up .obb files (default -noobb)\n"
- " -shared|-noshared: do/don't back up shared storage (default -noshared)\n"
- " -all: back up all installed applications\n"
- " -system|-nosystem: include system apps in -all (default -system)\n"
- " restore FILE restore device contents from FILE\n"
+ " to show usage run \"adb shell bu help\"\n"
"\n"
"debugging:\n"
" bugreport [PATH]\n"
@@ -220,6 +206,7 @@
" kill-server kill the server if it is running\n"
" reconnect kick connection from host side to force reconnect\n"
" reconnect device kick connection from device side to force reconnect\n"
+ " reconnect offline reset offline/unauthorized devices to force reconnect\n"
"\n"
"environment variables:\n"
" $ADB_TRACE\n"
@@ -613,6 +600,13 @@
std::string service_string = ShellServiceString(use_shell_protocol,
type_arg, command);
+ // Old devices can't handle a service string that's longer than MAX_PAYLOAD_V1.
+ // Use |use_shell_protocol| to determine whether to allow a command longer than that.
+ if (service_string.size() > MAX_PAYLOAD_V1 && !use_shell_protocol) {
+ fprintf(stderr, "error: shell command too long\n");
+ return 1;
+ }
+
// Make local stdin raw if the device allocates a PTY, which happens if:
// 1. We are explicitly asking for a PTY shell, or
// 2. We don't specify shell type and are starting an interactive session.
@@ -663,13 +657,8 @@
#endif
// TODO: combine read_and_dump with stdin_read_thread to make life simpler?
- int exit_code = 1;
- if (!adb_thread_create(stdin_read_thread_loop, args)) {
- PLOG(ERROR) << "error starting stdin read thread";
- delete args;
- } else {
- exit_code = read_and_dump(fd, use_shell_protocol);
- }
+ std::thread(stdin_read_thread_loop, args).detach();
+ int exit_code = read_and_dump(fd, use_shell_protocol);
// TODO: properly exit stdin_read_thread_loop and close |fd|.
@@ -697,14 +686,17 @@
// Parse shell-specific command-line options.
argv[0] = "adb shell"; // So getopt(3) error messages start "adb shell".
+#ifdef _WIN32
+ // fixes "adb shell -l" crash on Windows, b/37284906
+ __argv = const_cast<char**>(argv);
+#endif
optind = 1; // argv[0] is always "shell", so set `optind` appropriately.
int opt;
while ((opt = getopt(argc, const_cast<char**>(argv), "+e:ntTx")) != -1) {
switch (opt) {
case 'e':
if (!(strlen(optarg) == 1 || strcmp(optarg, "none") == 0)) {
- fprintf(stderr, "error: -e requires a single-character argument or 'none'\n");
- return 1;
+ return syntax_error("-e requires a single-character argument or 'none'");
}
escape_char = (strcmp(optarg, "none") == 0) ? 0 : optarg[0];
break;
@@ -782,55 +774,46 @@
return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
}
-static int adb_download_buffer(const char* service, const char* filename) {
- std::string content;
- if (!android::base::ReadFileToString(filename, &content)) {
- fprintf(stderr, "error: couldn't read %s: %s\n", filename, strerror(errno));
- return -1;
- }
-
- const uint8_t* data = reinterpret_cast<const uint8_t*>(content.data());
- unsigned sz = content.size();
-
+static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
std::string error;
- int fd = adb_connect(android::base::StringPrintf("%s:%d", service, sz), &error);
- if (fd < 0) {
- fprintf(stderr,"error: %s\n", error.c_str());
+ int out_fd = adb_connect(android::base::StringPrintf("sideload:%d", size), &error);
+ if (out_fd < 0) {
+ fprintf(stderr, "adb: pre-KitKat sideload connection failed: %s\n", error.c_str());
return -1;
}
int opt = CHUNK_SIZE;
- opt = adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt));
+ opt = adb_setsockopt(out_fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
- unsigned total = sz;
- const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
-
- const char* x = strrchr(service, ':');
- if (x) service = x + 1;
-
- while (sz > 0) {
- unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz;
- if (!WriteFdExactly(fd, ptr, xfer)) {
- std::string error;
- adb_status(fd, &error);
- fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
- adb_close(fd);
+ char buf[CHUNK_SIZE];
+ int total = size;
+ while (size > 0) {
+ unsigned xfer = (size > CHUNK_SIZE) ? CHUNK_SIZE : size;
+ if (!ReadFdExactly(in_fd, buf, xfer)) {
+ fprintf(stderr, "adb: failed to read data from %s: %s\n", filename, strerror(errno));
+ adb_close(out_fd);
return -1;
}
- sz -= xfer;
- ptr += xfer;
- printf("sending: '%s' %4d%% \r", filename, (int)(100LL - ((100LL * sz) / (total))));
+ if (!WriteFdExactly(out_fd, buf, xfer)) {
+ std::string error;
+ adb_status(out_fd, &error);
+ fprintf(stderr, "adb: failed to write data: %s\n", error.c_str());
+ adb_close(out_fd);
+ return -1;
+ }
+ size -= xfer;
+ printf("sending: '%s' %4d%% \r", filename, (int)(100LL - ((100LL * size) / (total))));
fflush(stdout);
}
printf("\n");
- if (!adb_status(fd, &error)) {
- fprintf(stderr,"* error response '%s' *\n", error.c_str());
- adb_close(fd);
+ if (!adb_status(out_fd, &error)) {
+ fprintf(stderr, "adb: error response: %s\n", error.c_str());
+ adb_close(out_fd);
return -1;
}
- adb_close(fd);
+ adb_close(out_fd);
return 0;
}
@@ -856,19 +839,18 @@
* we hang up.
*/
static int adb_sideload_host(const char* filename) {
- fprintf(stderr, "opening '%s'...\n", filename);
+ // TODO: use a LinePrinter instead...
struct stat sb;
if (stat(filename, &sb) == -1) {
- fprintf(stderr, "failed to stat file %s: %s\n", filename, strerror(errno));
+ fprintf(stderr, "adb: failed to stat file %s: %s\n", filename, strerror(errno));
return -1;
}
unique_fd package_fd(adb_open(filename, O_RDONLY));
if (package_fd == -1) {
- fprintf(stderr, "failed to open file %s: %s\n", filename, strerror(errno));
+ fprintf(stderr, "adb: failed to open file %s: %s\n", filename, strerror(errno));
return -1;
}
- fprintf(stderr, "connecting...\n");
std::string service = android::base::StringPrintf(
"sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
std::string error;
@@ -876,8 +858,9 @@
if (device_fd < 0) {
// Try falling back to the older (<= K) sideload method. Maybe this
// is an older device that doesn't support sideload-host.
- fprintf(stderr, "falling back to older sideload method...\n");
- return adb_download_buffer("sideload", filename);
+ fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str());
+ fprintf(stderr, "adb: trying pre-KitKat sideload method...\n");
+ return adb_sideload_legacy(filename, package_fd, static_cast<int>(sb.st_size));
}
int opt = SIDELOAD_HOST_BLOCK_SIZE;
@@ -889,7 +872,7 @@
int last_percent = -1;
while (true) {
if (!ReadFdExactly(device_fd, buf, 8)) {
- fprintf(stderr, "* failed to read command: %s\n", strerror(errno));
+ fprintf(stderr, "adb: failed to read command: %s\n", strerror(errno));
return -1;
}
buf[8] = '\0';
@@ -905,7 +888,7 @@
size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
if (offset >= static_cast<size_t>(sb.st_size)) {
- fprintf(stderr, "* attempt to read block %d past end\n", block);
+ fprintf(stderr, "adb: failed to read block %d past end\n", block);
return -1;
}
@@ -915,17 +898,17 @@
}
if (adb_lseek(package_fd, offset, SEEK_SET) != static_cast<int>(offset)) {
- fprintf(stderr, "* failed to seek to package block: %s\n", strerror(errno));
+ fprintf(stderr, "adb: failed to seek to package block: %s\n", strerror(errno));
return -1;
}
if (!ReadFdExactly(package_fd, buf, to_write)) {
- fprintf(stderr, "* failed to read package block: %s\n", strerror(errno));
+ fprintf(stderr, "adb: failed to read package block: %s\n", strerror(errno));
return -1;
}
if (!WriteFdExactly(device_fd, buf, to_write)) {
adb_status(device_fd, &error);
- fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
+ fprintf(stderr, "adb: failed to write data '%s' *\n", error.c_str());
return -1;
}
xfer += to_write;
@@ -957,19 +940,13 @@
fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
return -1;
#else
- if (argc < 2) {
- fprintf(stderr, "usage: adb %s <adb service name> [ppp opts]\n",
- argv[0]);
-
- return 1;
- }
+ if (argc < 2) return syntax_error("adb %s <adb service name> [ppp opts]", argv[0]);
const char* adb_service_name = argv[1];
std::string error;
int fd = adb_connect(adb_service_name, &error);
if (fd < 0) {
- fprintf(stderr,"Error: Could not open adb service: %s. Error: %s\n",
- adb_service_name, error.c_str());
+ fprintf(stderr, "adb: could not open adb service %s: %s\n", adb_service_name, error.c_str());
return 1;
}
@@ -1014,13 +991,16 @@
#endif /* !defined(_WIN32) */
}
-static bool wait_for_device(const char* service, TransportType t, const char* serial) {
+static bool wait_for_device(const char* service) {
std::vector<std::string> components = android::base::Split(service, "-");
if (components.size() < 3 || components.size() > 4) {
fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
return false;
}
+ TransportType t;
+ adb_get_transport(&t, nullptr, nullptr);
+
// Was the caller vague about what they'd like us to wait for?
// If so, check they weren't more specific in their choice of transport type.
if (components.size() == 3) {
@@ -1047,7 +1027,7 @@
return false;
}
- std::string cmd = format_host_command(android::base::Join(components, "-").c_str(), t, serial);
+ std::string cmd = format_host_command(android::base::Join(components, "-").c_str());
return adb_command(cmd);
}
@@ -1093,8 +1073,8 @@
return true;
}
-int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
- bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
+int send_shell_command(const std::string& command, bool disable_shell_protocol,
+ StandardStreamsCallbackInterface* callback) {
int fd;
bool use_shell_protocol = false;
@@ -1125,7 +1105,7 @@
}
fprintf(stderr, "- waiting for device -\n");
- if (!wait_for_device("wait-for-device", transport_type, serial)) {
+ if (!wait_for_device("wait-for-device")) {
return 1;
}
}
@@ -1139,7 +1119,7 @@
return exit_code;
}
-static int logcat(TransportType transport, const char* serial, int argc, const char** argv) {
+static int logcat(int argc, const char** argv) {
char* log_tags = getenv("ANDROID_LOG_TAGS");
std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags);
@@ -1156,7 +1136,7 @@
}
// No need for shell protocol with logcat, always disable for simplicity.
- return send_shell_command(transport, serial, cmd, true);
+ return send_shell_command(cmd, true);
}
static void write_zeros(int bytes, int fd) {
@@ -1188,10 +1168,7 @@
/* find, extract, and use any -f argument */
for (int i = 1; i < argc; i++) {
if (!strcmp("-f", argv[i])) {
- if (i == argc-1) {
- fprintf(stderr, "adb: backup -f passed with no filename.\n");
- return EXIT_FAILURE;
- }
+ if (i == argc - 1) return syntax_error("backup -f passed with no filename");
filename = argv[i+1];
for (int j = i+2; j <= argc; ) {
argv[i++] = argv[j++];
@@ -1203,10 +1180,7 @@
// Bare "adb backup" or "adb backup -f filename" are not valid invocations ---
// a list of packages is required.
- if (argc < 2) {
- fprintf(stderr, "adb: backup either needs a list of packages or -all/-shared.\n");
- return EXIT_FAILURE;
- }
+ if (argc < 2) return syntax_error("backup either needs a list of packages or -all/-shared");
adb_unlink(filename);
int outFd = adb_creat(filename, 0640);
@@ -1231,7 +1205,7 @@
return EXIT_FAILURE;
}
- printf("Now unlock your device and confirm the backup operation...\n");
+ fprintf(stdout, "Now unlock your device and confirm the backup operation...\n");
fflush(stdout);
copy_to_file(fd, outFd);
@@ -1242,7 +1216,7 @@
}
static int restore(int argc, const char** argv) {
- if (argc != 2) return usage("restore requires an argument");
+ if (argc != 2) return syntax_error("restore requires an argument");
const char* filename = argv[1];
int tarFd = adb_open(filename, O_RDONLY);
@@ -1259,7 +1233,9 @@
return -1;
}
- printf("Now unlock your device and confirm the restore operation.\n");
+ fprintf(stdout, "Now unlock your device and confirm the restore operation.\n");
+ fflush(stdout);
+
copy_to_file(tarFd, fd);
// Provide an in-band EOD marker in case the archive file is malformed
@@ -1273,69 +1249,8 @@
return 0;
}
-/* <hint> may be:
- * - A simple product name
- * e.g., "sooner"
- * - A relative path from the CWD to the ANDROID_PRODUCT_OUT dir
- * e.g., "out/target/product/sooner"
- * - An absolute path to the PRODUCT_OUT dir
- * e.g., "/src/device/out/target/product/sooner"
- *
- * Given <hint>, try to construct an absolute path to the
- * ANDROID_PRODUCT_OUT dir.
- */
-static std::string find_product_out_path(const std::string& hint) {
- if (hint.empty()) {
- return "";
- }
-
- // If it's already absolute, don't bother doing any work.
- if (adb_is_absolute_host_path(hint.c_str())) {
- return hint;
- }
-
- // If any of the OS_PATH_SEPARATORS is found, assume it's a relative path;
- // make it absolute.
- // NOLINT: Do not complain if OS_PATH_SEPARATORS has only one character.
- if (hint.find_first_of(OS_PATH_SEPARATORS) != std::string::npos) { // NOLINT
- std::string cwd;
- if (!getcwd(&cwd)) {
- perror("adb: getcwd failed");
- return "";
- }
- return android::base::StringPrintf("%s%c%s", cwd.c_str(), OS_PATH_SEPARATOR, hint.c_str());
- }
-
- // It's a string without any slashes. Try to do something with it.
- //
- // Try to find the root of the build tree, and build a PRODUCT_OUT
- // path from there.
- char* top = getenv("ANDROID_BUILD_TOP");
- if (top == nullptr) {
- fprintf(stderr, "adb: ANDROID_BUILD_TOP not set!\n");
- return "";
- }
-
- std::string path = top;
- path += OS_PATH_SEPARATOR_STR;
- path += "out";
- path += OS_PATH_SEPARATOR_STR;
- path += "target";
- path += OS_PATH_SEPARATOR_STR;
- path += "product";
- path += OS_PATH_SEPARATOR_STR;
- path += hint;
- if (!directory_exists(path)) {
- fprintf(stderr, "adb: Couldn't find a product dir based on -p %s; "
- "\"%s\" doesn't exist\n", hint.c_str(), path.c_str());
- return "";
- }
- return path;
-}
-
-static void parse_push_pull_args(const char** arg, int narg,
- std::vector<const char*>* srcs,
- const char** dst, bool* copy_attrs) {
+static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
+ const char** dst, bool* copy_attrs, bool* sync) {
*copy_attrs = false;
srcs->clear();
@@ -1348,10 +1263,14 @@
// Silently ignore for backwards compatibility.
} else if (!strcmp(*arg, "-a")) {
*copy_attrs = true;
+ } else if (!strcmp(*arg, "--sync")) {
+ if (sync != nullptr) {
+ *sync = true;
+ }
} else if (!strcmp(*arg, "--")) {
ignore_flags = true;
} else {
- fprintf(stderr, "adb: unrecognized option '%s'\n", *arg);
+ syntax_error("unrecognized option '%s'", *arg);
exit(1);
}
}
@@ -1423,23 +1342,13 @@
signal(SIGPIPE, SIG_IGN);
#endif
- // If defined, this should be an absolute path to
- // the directory containing all of the various system images
- // for a particular product. If not defined, and the adb
- // command requires this information, then the user must
- // specify the path using "-p".
- char* ANDROID_PRODUCT_OUT = getenv("ANDROID_PRODUCT_OUT");
- if (ANDROID_PRODUCT_OUT != nullptr) {
- gProductOutPath = ANDROID_PRODUCT_OUT;
- }
- // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
-
const char* server_host_str = nullptr;
const char* server_port_str = nullptr;
const char* server_socket_str = nullptr;
// We need to check for -d and -e before we look at $ANDROID_SERIAL.
const char* serial = nullptr;
+ TransportId transport_id = 0;
while (argc > 0) {
if (!strcmp(argv[0],"server")) {
@@ -1450,7 +1359,7 @@
/* this is a special flag used only when the ADB client launches the ADB Server */
is_daemon = 1;
} else if (!strcmp(argv[0], "--reply-fd")) {
- if (argc < 2) return usage("--reply-fd requires an argument");
+ if (argc < 2) return syntax_error("--reply-fd requires an argument");
const char* reply_fd_str = argv[1];
argc--;
argv++;
@@ -1459,30 +1368,28 @@
fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str);
return 1;
}
- } else if (!strncmp(argv[0], "-p", 2)) {
- const char* product = nullptr;
- if (argv[0][2] == '\0') {
- if (argc < 2) return usage("-p requires an argument");
- product = argv[1];
- argc--;
- argv++;
- } else {
- product = argv[0] + 2;
- }
- gProductOutPath = find_product_out_path(product);
- if (gProductOutPath.empty()) {
- fprintf(stderr, "adb: could not resolve \"-p %s\"\n", product);
- return 1;
- }
- } else if (argv[0][0]=='-' && argv[0][1]=='s') {
+ } else if (!strncmp(argv[0], "-s", 2)) {
if (isdigit(argv[0][2])) {
serial = argv[0] + 2;
} else {
- if (argc < 2 || argv[0][2] != '\0') return usage("-s requires an argument");
+ if (argc < 2 || argv[0][2] != '\0') return syntax_error("-s requires an argument");
serial = argv[1];
argc--;
argv++;
}
+ } else if (!strncmp(argv[0], "-t", 2)) {
+ const char* id;
+ if (isdigit(argv[0][2])) {
+ id = argv[0] + 2;
+ } else {
+ id = argv[1];
+ argc--;
+ argv++;
+ }
+ transport_id = strtoll(id, const_cast<char**>(&id), 10);
+ if (*id != '\0') {
+ return syntax_error("invalid transport id");
+ }
} else if (!strcmp(argv[0],"-d")) {
transport_type = kTransportUsb;
} else if (!strcmp(argv[0],"-e")) {
@@ -1491,7 +1398,7 @@
gListenAll = 1;
} else if (!strncmp(argv[0], "-H", 2)) {
if (argv[0][2] == '\0') {
- if (argc < 2) return usage("-H requires an argument");
+ if (argc < 2) return syntax_error("-H requires an argument");
server_host_str = argv[1];
argc--;
argv++;
@@ -1500,7 +1407,7 @@
}
} else if (!strncmp(argv[0], "-P", 2)) {
if (argv[0][2] == '\0') {
- if (argc < 2) return usage("-P requires an argument");
+ if (argc < 2) return syntax_error("-P requires an argument");
server_port_str = argv[1];
argc--;
argv++;
@@ -1508,7 +1415,7 @@
server_port_str = argv[0] + 2;
}
} else if (!strcmp(argv[0], "-L")) {
- if (argc < 2) return usage("-L requires an argument");
+ if (argc < 2) return syntax_error("-L requires an argument");
server_socket_str = argv[1];
argc--;
argv++;
@@ -1521,8 +1428,7 @@
}
if ((server_host_str || server_port_str) && server_socket_str) {
- fprintf(stderr, "adb: -L is incompatible with -H or -P\n");
- exit(1);
+ return syntax_error("-L is incompatible with -H or -P");
}
// If -L, -H, or -P are specified, ignore environment variables.
@@ -1567,7 +1473,7 @@
serial = getenv("ANDROID_SERIAL");
}
- adb_set_transport(transport_type, serial);
+ adb_set_transport(transport_type, serial, transport_id);
if (is_server) {
if (no_daemon || is_daemon) {
@@ -1594,7 +1500,7 @@
if (!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) {
const char* service = argv[0];
- if (!wait_for_device(service, transport_type, serial)) {
+ if (!wait_for_device(service)) {
return 1;
}
@@ -1617,8 +1523,7 @@
} else if (argc == 2 && !strcmp(argv[1], "-l")) {
listopt = argv[1];
} else {
- fprintf(stderr, "Usage: adb devices [-l]\n");
- return 1;
+ return syntax_error("adb devices [-l]");
}
std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
@@ -1626,19 +1531,13 @@
return adb_query_command(query);
}
else if (!strcmp(argv[0], "connect")) {
- if (argc != 2) {
- fprintf(stderr, "Usage: adb connect <host>[:<port>]\n");
- return 1;
- }
+ if (argc != 2) return syntax_error("adb connect <host>[:<port>]");
std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
return adb_query_command(query);
}
else if (!strcmp(argv[0], "disconnect")) {
- if (argc > 2) {
- fprintf(stderr, "Usage: adb disconnect [<host>[:<port>]]\n");
- return 1;
- }
+ if (argc > 2) return syntax_error("adb disconnect [<host>[:<port>]]");
std::string query = android::base::StringPrintf("host:disconnect:%s",
(argc == 2) ? argv[1] : "");
@@ -1653,10 +1552,7 @@
else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
int exec_in = !strcmp(argv[0], "exec-in");
- if (argc < 2) {
- fprintf(stderr, "Usage: adb %s command\n", argv[0]);
- return 1;
- }
+ if (argc < 2) return syntax_error("adb %s command", argv[0]);
std::string cmd = "exec:";
cmd += argv[1];
@@ -1683,36 +1579,22 @@
return 0;
}
else if (!strcmp(argv[0], "kill-server")) {
- std::string error;
- int fd = _adb_connect("host:kill", &error);
- if (fd == -2) {
- // Failed to make network connection to server. Don't output the
- // network error since that is expected.
- fprintf(stderr,"* server not running *\n");
- // Successful exit code because the server is already "killed".
- return 0;
- } else if (fd == -1) {
- // Some other error.
- fprintf(stderr, "error: %s\n", error.c_str());
- return 1;
- } else {
- // Successfully connected, kill command sent, okay status came back.
- // Server should exit() in a moment, if not already.
- ReadOrderlyShutdown(fd);
- adb_close(fd);
- return 0;
- }
+ return adb_kill_server() ? 0 : 1;
}
else if (!strcmp(argv[0], "sideload")) {
- if (argc != 2) return usage("sideload requires an argument");
+ if (argc != 2) return syntax_error("sideload requires an argument");
if (adb_sideload_host(argv[1])) {
return 1;
} else {
return 0;
}
- }
- else if (!strcmp(argv[0], "tcpip") && argc > 1) {
- return adb_connect_command(android::base::StringPrintf("tcpip:%s", argv[1]));
+ } else if (!strcmp(argv[0], "tcpip")) {
+ if (argc != 2) return syntax_error("tcpip requires an argument");
+ int port;
+ if (!android::base::ParseInt(argv[1], &port, 1, 65535)) {
+ return syntax_error("tcpip: invalid port: %s", argv[1]);
+ }
+ return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
}
else if (!strcmp(argv[0], "remount") ||
!strcmp(argv[0], "reboot") ||
@@ -1733,12 +1615,12 @@
return adb_root(argv[0]) ? 0 : 1;
} else if (!strcmp(argv[0], "bugreport")) {
Bugreport bugreport;
- return bugreport.DoIt(transport_type, serial, argc, argv);
+ return bugreport.DoIt(argc, argv);
} else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
bool reverse = !strcmp(argv[0], "reverse");
++argv;
--argc;
- if (argc < 1) return usage("%s requires an argument", argv[0]);
+ if (argc < 1) return syntax_error("%s requires an argument", argv[0]);
// Determine the <host-prefix> for this command.
std::string host_prefix;
@@ -1758,24 +1640,24 @@
std::string cmd, error;
if (strcmp(argv[0], "--list") == 0) {
- if (argc != 1) return usage("--list doesn't take any arguments");
+ if (argc != 1) return syntax_error("--list doesn't take any arguments");
return adb_query_command(host_prefix + ":list-forward");
} else if (strcmp(argv[0], "--remove-all") == 0) {
- if (argc != 1) return usage("--remove-all doesn't take any arguments");
+ if (argc != 1) return syntax_error("--remove-all doesn't take any arguments");
cmd = host_prefix + ":killforward-all";
} else if (strcmp(argv[0], "--remove") == 0) {
// forward --remove <local>
- if (argc != 2) return usage("--remove requires an argument");
+ if (argc != 2) return syntax_error("--remove requires an argument");
cmd = host_prefix + ":killforward:" + argv[1];
} else if (strcmp(argv[0], "--no-rebind") == 0) {
// forward --no-rebind <local> <remote>
- if (argc != 3) return usage("--no-rebind takes two arguments");
+ if (argc != 3) return syntax_error("--no-rebind takes two arguments");
if (forward_targets_are_valid(argv[1], argv[2], &error)) {
cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
}
} else {
// forward <local> <remote>
- if (argc != 2) return usage("forward takes two arguments");
+ if (argc != 2) return syntax_error("forward takes two arguments");
if (forward_targets_are_valid(argv[0], argv[1], &error)) {
cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
}
@@ -1804,44 +1686,45 @@
}
/* do_sync_*() commands */
else if (!strcmp(argv[0], "ls")) {
- if (argc != 2) return usage("ls requires an argument");
+ if (argc != 2) return syntax_error("ls requires an argument");
return do_sync_ls(argv[1]) ? 0 : 1;
}
else if (!strcmp(argv[0], "push")) {
bool copy_attrs = false;
+ bool sync = false;
std::vector<const char*> srcs;
const char* dst = nullptr;
- parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
- if (srcs.empty() || !dst) return usage("push requires an argument");
- return do_sync_push(srcs, dst) ? 0 : 1;
+ parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, &sync);
+ if (srcs.empty() || !dst) return syntax_error("push requires an argument");
+ return do_sync_push(srcs, dst, sync) ? 0 : 1;
}
else if (!strcmp(argv[0], "pull")) {
bool copy_attrs = false;
std::vector<const char*> srcs;
const char* dst = ".";
- parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
- if (srcs.empty()) return usage("pull requires an argument");
+ parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, nullptr);
+ if (srcs.empty()) return syntax_error("pull requires an argument");
return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
}
else if (!strcmp(argv[0], "install")) {
- if (argc < 2) return usage("install requires an argument");
+ if (argc < 2) return syntax_error("install requires an argument");
if (_use_legacy_install()) {
- return install_app_legacy(transport_type, serial, argc, argv);
+ return install_app_legacy(argc, argv);
}
- return install_app(transport_type, serial, argc, argv);
+ return install_app(argc, argv);
}
else if (!strcmp(argv[0], "install-multiple")) {
- if (argc < 2) return usage("install-multiple requires an argument");
- return install_multiple_app(transport_type, serial, argc, argv);
+ if (argc < 2) return syntax_error("install-multiple requires an argument");
+ return install_multiple_app(argc, argv);
}
else if (!strcmp(argv[0], "uninstall")) {
- if (argc < 2) return usage("uninstall requires an argument");
+ if (argc < 2) return syntax_error("uninstall requires an argument");
if (_use_legacy_install()) {
- return uninstall_app_legacy(transport_type, serial, argc, argv);
+ return uninstall_app_legacy(argc, argv);
}
- return uninstall_app(transport_type, serial, argc, argv);
+ return uninstall_app(argc, argv);
}
else if (!strcmp(argv[0], "sync")) {
std::string src;
@@ -1860,12 +1743,14 @@
// A local path or "android"/"data" arg was specified.
src = argv[1];
} else {
- return usage("usage: adb sync [-l] [PARTITION]");
+ return syntax_error("adb sync [-l] [PARTITION]");
}
+ if (src == "all") src = "";
+
if (src != "" &&
src != "system" && src != "data" && src != "vendor" && src != "oem") {
- return usage("don't know how to sync %s partition", src.c_str());
+ return syntax_error("don't know how to sync %s partition", src.c_str());
}
std::string system_src_path = product_file("system");
@@ -1893,11 +1778,11 @@
!strcmp(argv[0],"get-serialno") ||
!strcmp(argv[0],"get-devpath"))
{
- return adb_query_command(format_host_command(argv[0], transport_type, serial));
+ return adb_query_command(format_host_command(argv[0]));
}
/* other commands */
else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) {
- return logcat(transport_type, serial, argc, argv);
+ return logcat(argc, argv);
}
else if (!strcmp(argv[0],"ppp")) {
return ppp(argc, argv);
@@ -1917,7 +1802,7 @@
return restore(argc, argv);
}
else if (!strcmp(argv[0], "keygen")) {
- if (argc != 2) return usage("keygen requires an argument");
+ if (argc != 2) return syntax_error("keygen requires an argument");
// Always print key generation information for keygen command.
adb_trace_enable(AUTH);
return adb_auth_keygen(argv[1]);
@@ -1960,7 +1845,7 @@
return adb_query_command("host:host-features");
} else if (!strcmp(argv[0], "reconnect")) {
if (argc == 1) {
- return adb_query_command("host:reconnect");
+ return adb_query_command(format_host_command(argv[0]));
} else if (argc == 2) {
if (!strcmp(argv[1], "device")) {
std::string err;
@@ -1970,16 +1855,16 @@
std::string err;
return adb_query_command("host:reconnect-offline");
} else {
- return usage("usage: adb reconnect [device|offline]");
+ return syntax_error("adb reconnect [device|offline]");
}
}
}
- usage("unknown command %s", argv[0]);
+ syntax_error("unknown command %s", argv[0]);
return 1;
}
-static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
+static int uninstall_app(int argc, const char** argv) {
// 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
std::string cmd = "cmd package";
while (argc-- > 0) {
@@ -1995,26 +1880,25 @@
cmd += " " + escape_arg(*argv++);
}
- return send_shell_command(transport, serial, cmd, false);
+ return send_shell_command(cmd, false);
}
-static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
+static int install_app(int argc, const char** argv) {
// The last argument must be the APK file
const char* file = argv[argc - 1];
if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
- fprintf(stderr, "Filename doesn't end .apk: %s\n", file);
- return EXIT_FAILURE;
+ return syntax_error("filename doesn't end .apk: %s", file);
}
struct stat sb;
if (stat(file, &sb) == -1) {
- fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
+ fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
return 1;
}
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
- fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
return 1;
}
@@ -2032,7 +1916,7 @@
int remoteFd = adb_connect(cmd, &error);
if (remoteFd < 0) {
- fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+ fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
adb_close(localFd);
return 1;
}
@@ -2044,17 +1928,15 @@
adb_close(localFd);
adb_close(remoteFd);
- if (strncmp("Success", buf, 7)) {
- fprintf(stderr, "Failed to install %s: %s", file, buf);
- return 1;
+ if (!strncmp("Success", buf, 7)) {
+ fputs(buf, stdout);
+ return 0;
}
- fputs(buf, stderr);
- return 0;
+ fprintf(stderr, "adb: failed to install %s: %s", file, buf);
+ return 1;
}
-static int install_multiple_app(TransportType transport, const char* serial, int argc,
- const char** argv)
-{
+static int install_multiple_app(int argc, const char** argv) {
// Find all APK arguments starting at end.
// All other arguments passed through verbatim.
int first_apk = -1;
@@ -2071,10 +1953,7 @@
}
}
- if (first_apk == -1) {
- fprintf(stderr, "No APK file on command line\n");
- return 1;
- }
+ if (first_apk == -1) return syntax_error("need APK file on command line");
std::string install_cmd;
if (_use_legacy_install()) {
@@ -2092,7 +1971,7 @@
std::string error;
int fd = adb_connect(cmd, &error);
if (fd < 0) {
- fprintf(stderr, "Connect error for create: %s\n", error.c_str());
+ fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
return EXIT_FAILURE;
}
char buf[BUFSIZ];
@@ -2109,7 +1988,7 @@
}
}
if (session_id < 0) {
- fprintf(stderr, "Failed to create session\n");
+ fprintf(stderr, "adb: failed to create session\n");
fputs(buf, stderr);
return EXIT_FAILURE;
}
@@ -2120,7 +1999,7 @@
const char* file = argv[i];
struct stat sb;
if (stat(file, &sb) == -1) {
- fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
+ fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
success = 0;
goto finalize_session;
}
@@ -2132,7 +2011,7 @@
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
- fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
success = 0;
goto finalize_session;
}
@@ -2140,7 +2019,7 @@
std::string error;
int remoteFd = adb_connect(cmd, &error);
if (remoteFd < 0) {
- fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+ fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
adb_close(localFd);
success = 0;
goto finalize_session;
@@ -2153,7 +2032,7 @@
adb_close(remoteFd);
if (strncmp("Success", buf, 7)) {
- fprintf(stderr, "Failed to write %s\n", file);
+ fprintf(stderr, "adb: failed to write %s\n", file);
fputs(buf, stderr);
success = 0;
goto finalize_session;
@@ -2167,33 +2046,32 @@
install_cmd.c_str(), success ? "commit" : "abandon", session_id);
fd = adb_connect(service, &error);
if (fd < 0) {
- fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
+ fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
return EXIT_FAILURE;
}
read_status_line(fd, buf, sizeof(buf));
adb_close(fd);
if (!strncmp("Success", buf, 7)) {
- fputs(buf, stderr);
+ fputs(buf, stdout);
return 0;
- } else {
- fprintf(stderr, "Failed to finalize session\n");
- fputs(buf, stderr);
- return EXIT_FAILURE;
}
+ fprintf(stderr, "adb: failed to finalize session\n");
+ fputs(buf, stderr);
+ return EXIT_FAILURE;
}
-static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
+static int pm_command(int argc, const char** argv) {
std::string cmd = "pm";
while (argc-- > 0) {
cmd += " " + escape_arg(*argv++);
}
- return send_shell_command(transport, serial, cmd, false);
+ return send_shell_command(cmd, false);
}
-static int uninstall_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+static int uninstall_app_legacy(int argc, const char** argv) {
/* if the user choose the -k option, we refuse to do it until devices are
out with the option to uninstall the remaining data somehow (adb/ui) */
int i;
@@ -2209,15 +2087,15 @@
}
/* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
- return pm_command(transport, serial, argc, argv);
+ return pm_command(argc, argv);
}
-static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
+static int delete_file(const std::string& filename) {
std::string cmd = "rm -f " + escape_arg(filename);
- return send_shell_command(transport, serial, cmd, false);
+ return send_shell_command(cmd, false);
}
-static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+static int install_app_legacy(int argc, const char** argv) {
static const char *const DATA_DEST = "/data/local/tmp/%s";
static const char *const SD_DEST = "/sdcard/tmp/%s";
const char* where = DATA_DEST;
@@ -2238,20 +2116,17 @@
}
}
- if (last_apk == -1) {
- fprintf(stderr, "No APK file on command line\n");
- return EXIT_FAILURE;
- }
+ if (last_apk == -1) return syntax_error("need APK file on command line");
int result = -1;
std::vector<const char*> apk_file = {argv[last_apk]};
std::string apk_dest = android::base::StringPrintf(
where, android::base::Basename(argv[last_apk]).c_str());
- if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
+ if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
- result = pm_command(transport, serial, argc, argv);
+ result = pm_command(argc, argv);
cleanup_apk:
- delete_file(transport, serial, apk_dest);
+ delete_file(apk_dest);
return result;
}
diff --git a/adb/commandline.h b/adb/commandline.h
index 0cf655c..36cd798 100644
--- a/adb/commandline.h
+++ b/adb/commandline.h
@@ -87,13 +87,12 @@
extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
int adb_commandline(int argc, const char** argv);
-int usage();
// Connects to the device "shell" service with |command| and prints the
// resulting output.
// if |callback| is non-null, stdout/stderr output will be handled by it.
-int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
- bool disable_shell_protocol, StandardStreamsCallbackInterface* callback =
- &DEFAULT_STANDARD_STREAMS_CALLBACK);
+int send_shell_command(
+ const std::string& command, bool disable_shell_protocol,
+ StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
#endif // COMMANDLINE_H
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 7a87df4..1c94298 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -107,10 +107,10 @@
// AID_SDCARD_RW to allow writing to the SD card
// AID_NET_BW_STATS to read out qtaguid statistics
// AID_READPROC for reading /proc entries across UID boundaries
- gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT,
- AID_INET, AID_NET_BT, AID_NET_BT_ADMIN,
- AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS,
- AID_READPROC};
+ // AID_UHID for using 'hid' command to read/write to /dev/uhid
+ gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT, AID_INET,
+ AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
+ AID_NET_BW_STATS, AID_READPROC, AID_UHID};
minijail_set_supplementary_gids(jail.get(), arraysize(groups), groups);
// Don't listen on a port (default 5037) if running in secure mode.
@@ -233,9 +233,8 @@
adb_device_banner = optarg;
break;
case 'v':
- printf("Android Debug Bridge Daemon version %d.%d.%d %s\n",
- ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
- ADB_REVISION);
+ printf("Android Debug Bridge Daemon version %d.%d.%d (%s)\n", ADB_VERSION_MAJOR,
+ ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_VERSION);
return 0;
default:
// getopt already prints "adbd: invalid option -- %c" for us.
diff --git a/adb/daemon/mdns.cpp b/adb/daemon/mdns.cpp
index 7811143..849378f 100644
--- a/adb/daemon/mdns.cpp
+++ b/adb/daemon/mdns.cpp
@@ -17,12 +17,14 @@
#include "adb_mdns.h"
#include "sysdeps.h"
-#include <chrono>
#include <dns_sd.h>
#include <endian.h>
-#include <mutex>
#include <unistd.h>
+#include <chrono>
+#include <mutex>
+#include <thread>
+
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -58,7 +60,7 @@
}
}
-static void setup_mdns_thread(void* /* unused */) {
+static void setup_mdns_thread() {
start_mdns();
std::lock_guard<std::mutex> lock(mdns_lock);
@@ -88,7 +90,7 @@
void setup_mdns(int port_in) {
port = port_in;
- adb_thread_create(setup_mdns_thread, nullptr, nullptr);
+ std::thread(setup_mdns_thread).detach();
// TODO: Make this more robust against a hard kill.
atexit(teardown_mdns);
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 92e9039..87ed3db 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -26,6 +26,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
@@ -49,22 +50,17 @@
#define MAX_PACKET_SIZE_HS 512
#define MAX_PACKET_SIZE_SS 1024
-// Kernels before 3.3 have a 16KiB transfer limit That limit was replaced
-// with a 16MiB global limit in 3.3, but each URB submitted required a
-// contiguous kernel allocation, so you would get ENOMEM if you tried to
-// send something larger than the biggest available contiguous kernel
-// memory region. Large contiguous allocations could be unreliable
-// on a device kernel that has been running for a while fragmenting its
-// memory so we start with a larger allocation, and shrink the amount if
-// necessary.
#define USB_FFS_BULK_SIZE 16384
+// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
+#define USB_FFS_NUM_BUFS ((MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
+
#define cpu_to_le16(x) htole16(x)
#define cpu_to_le32(x) htole32(x)
#define FUNCTIONFS_ENDPOINT_ALLOC _IOR('g', 231, __u32)
-static constexpr size_t ENDPOINT_ALLOC_RETRIES = 2;
+static constexpr size_t ENDPOINT_ALLOC_RETRIES = 10;
static int dummy_fd = -1;
@@ -234,7 +230,29 @@
},
};
+static void aio_block_init(aio_block* aiob) {
+ aiob->iocb.resize(USB_FFS_NUM_BUFS);
+ aiob->iocbs.resize(USB_FFS_NUM_BUFS);
+ aiob->events.resize(USB_FFS_NUM_BUFS);
+ aiob->num_submitted = 0;
+ for (unsigned i = 0; i < USB_FFS_NUM_BUFS; i++) {
+ aiob->iocbs[i] = &aiob->iocb[i];
+ }
+}
+
+static int getMaxPacketSize(int ffs_fd) {
+ usb_endpoint_descriptor desc;
+ if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {
+ D("[ could not get endpoint descriptor! (%d) ]", errno);
+ return MAX_PACKET_SIZE_HS;
+ } else {
+ return desc.wMaxPacketSize;
+ }
+}
+
bool init_functionfs(struct usb_handle* h) {
+ LOG(INFO) << "initializing functionfs";
+
ssize_t ret;
struct desc_v1 v1_descriptor;
struct desc_v2 v2_descriptor;
@@ -255,10 +273,10 @@
v2_descriptor.os_desc = os_desc_compat;
if (h->control < 0) { // might have already done this before
- D("OPENING %s", USB_FFS_ADB_EP0);
+ LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
if (h->control < 0) {
- D("[ %s: cannot open control endpoint: errno=%d]", USB_FFS_ADB_EP0, errno);
+ PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
goto err;
}
@@ -289,16 +307,24 @@
h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
if (h->bulk_out < 0) {
- D("[ %s: cannot open bulk-out ep: errno=%d ]", USB_FFS_ADB_OUT, errno);
+ PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
goto err;
}
h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
if (h->bulk_in < 0) {
- D("[ %s: cannot open bulk-in ep: errno=%d ]", USB_FFS_ADB_IN, errno);
+ PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
goto err;
}
+ if (io_setup(USB_FFS_NUM_BUFS, &h->read_aiob.ctx) ||
+ io_setup(USB_FFS_NUM_BUFS, &h->write_aiob.ctx)) {
+ D("[ aio: got error on io_setup (%d) ]", errno);
+ }
+
+ h->read_aiob.fd = h->bulk_out;
+ h->write_aiob.fd = h->bulk_in;
+
h->max_rw = MAX_PAYLOAD;
while (h->max_rw >= USB_FFS_BULK_SIZE && retries < ENDPOINT_ALLOC_RETRIES) {
int ret_in = ioctl(h->bulk_in, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(h->max_rw));
@@ -356,12 +382,13 @@
while (true) {
if (init_functionfs(usb)) {
+ LOG(INFO) << "functionfs successfully initialized";
break;
}
std::this_thread::sleep_for(1s);
}
- D("[ usb_thread - registering device ]");
+ LOG(INFO) << "registering usb transport";
register_usb_transport(usb, 0, 0, 1);
}
@@ -407,6 +434,65 @@
return 0;
}
+static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
+ aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
+ bool zero_packet = false;
+
+ int num_bufs = len / USB_FFS_BULK_SIZE + (len % USB_FFS_BULK_SIZE == 0 ? 0 : 1);
+ const char* cur_data = reinterpret_cast<const char*>(data);
+ int packet_size = getMaxPacketSize(aiob->fd);
+
+ if (posix_madvise(const_cast<void*>(data), len, POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) <
+ 0) {
+ D("[ Failed to madvise: %d ]", errno);
+ }
+
+ for (int i = 0; i < num_bufs; i++) {
+ int buf_len = std::min(len, USB_FFS_BULK_SIZE);
+ io_prep(&aiob->iocb[i], aiob->fd, cur_data, buf_len, 0, read);
+
+ len -= buf_len;
+ cur_data += buf_len;
+
+ if (len == 0 && buf_len % packet_size == 0 && read) {
+ // adb does not expect the device to send a zero packet after data transfer,
+ // but the host *does* send a zero packet for the device to read.
+ zero_packet = true;
+ }
+ }
+ if (zero_packet) {
+ io_prep(&aiob->iocb[num_bufs], aiob->fd, reinterpret_cast<const void*>(cur_data),
+ packet_size, 0, read);
+ num_bufs += 1;
+ }
+
+ if (io_submit(aiob->ctx, num_bufs, aiob->iocbs.data()) < num_bufs) {
+ D("[ aio: got error submitting %s (%d) ]", read ? "read" : "write", errno);
+ return -1;
+ }
+ if (TEMP_FAILURE_RETRY(
+ io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(), nullptr)) < num_bufs) {
+ D("[ aio: got error waiting %s (%d) ]", read ? "read" : "write", errno);
+ return -1;
+ }
+ for (int i = 0; i < num_bufs; i++) {
+ if (aiob->events[i].res < 0) {
+ errno = aiob->events[i].res;
+ D("[ aio: got error event on %s (%d) ]", read ? "read" : "write", errno);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int usb_ffs_aio_read(usb_handle* h, void* data, int len) {
+ return usb_ffs_do_aio(h, data, len, true);
+}
+
+static int usb_ffs_aio_write(usb_handle* h, const void* data, int len) {
+ return usb_ffs_do_aio(h, data, len, false);
+}
+
static void usb_ffs_kick(usb_handle* h) {
int err;
@@ -430,9 +516,14 @@
}
static void usb_ffs_close(usb_handle* h) {
+ LOG(INFO) << "closing functionfs transport";
+
h->kicked = false;
adb_close(h->bulk_out);
adb_close(h->bulk_in);
+ io_destroy(h->read_aiob.ctx);
+ io_destroy(h->write_aiob.ctx);
+
// Notify usb_adb_open_thread to open a new connection.
h->lock.lock();
h->open_new_connection = true;
@@ -445,15 +536,22 @@
usb_handle* h = new usb_handle();
- h->write = usb_ffs_write;
- h->read = usb_ffs_read;
+ if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
+ // Devices on older kernels (< 3.18) will not have aio support for ffs
+ // unless backported. Fall back on the non-aio functions instead.
+ h->write = usb_ffs_write;
+ h->read = usb_ffs_read;
+ } else {
+ h->write = usb_ffs_aio_write;
+ h->read = usb_ffs_aio_read;
+ aio_block_init(&h->read_aiob);
+ aio_block_init(&h->write_aiob);
+ }
h->kick = usb_ffs_kick;
h->close = usb_ffs_close;
D("[ usb_init - starting thread ]");
- if (!adb_thread_create(usb_ffs_open_thread, h)) {
- fatal_errno("[ cannot create usb thread ]\n");
- }
+ std::thread(usb_ffs_open_thread, h).detach();
}
void usb_init() {
diff --git a/adb/daemon/usb.h b/adb/daemon/usb.h
index 55b5995..db1a6d6 100644
--- a/adb/daemon/usb.h
+++ b/adb/daemon/usb.h
@@ -20,6 +20,17 @@
#include <condition_variable>
#include <mutex>
+#include <asyncio/AsyncIO.h>
+
+struct aio_block {
+ std::vector<struct iocb> iocb;
+ std::vector<struct iocb*> iocbs;
+ std::vector<struct io_event> events;
+ aio_context_t ctx;
+ int num_submitted;
+ int fd;
+};
+
struct usb_handle {
usb_handle() : kicked(false) {
}
@@ -39,7 +50,11 @@
int bulk_out = -1; /* "out" from the host's perspective => source for adbd */
int bulk_in = -1; /* "in" from the host's perspective => sink for adbd */
+ // Access to these blocks is very not thread safe. Have one block for both the
+ // read and write threads.
+ struct aio_block read_aiob;
+ struct aio_block write_aiob;
+
int max_rw;
};
-bool init_functionfs(struct usb_handle* h);
diff --git a/adb/diagnose_usb.cpp b/adb/diagnose_usb.cpp
index 0f067b0..9f721bf 100644
--- a/adb/diagnose_usb.cpp
+++ b/adb/diagnose_usb.cpp
@@ -25,13 +25,14 @@
#if defined(__linux__)
#include <grp.h>
+#include <pwd.h>
#endif
static const char kPermissionsHelpUrl[] = "http://developer.android.com/tools/device.html";
-// Returns a message describing any potential problems we find with udev, or nullptr if we can't
-// find plugdev information (i.e. udev is not installed).
-static const char* GetUdevProblem() {
+// Returns a message describing any potential problems we find with udev, or an empty string if we
+// can't find plugdev information (i.e. udev is not installed).
+static std::string GetUdevProblem() {
#if defined(__linux__)
errno = 0;
group* plugdev_group = getgrnam("plugdev");
@@ -41,43 +42,45 @@
perror("failed to read plugdev group info");
}
// We can't give any generally useful advice here, just let the caller print the help URL.
- return nullptr;
+ return "";
}
- // getgroups(2) indicates that the group_member() may not check the egid so we check it
+ // getgroups(2) indicates that the GNU group_member(3) may not check the egid so we check it
// additionally just to be sure.
if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
// The user is in plugdev so the problem is likely with the udev rules.
- return "verify udev rules";
+ return "user in plugdev group; are your udev rules wrong?";
}
- return "udev requires plugdev group membership";
+ passwd* pwd = getpwuid(getuid());
+ return android::base::StringPrintf("user %s is not in the plugdev group",
+ pwd ? pwd->pw_name : "?");
#else
- return nullptr;
+ return "";
#endif
}
// Short help text must be a single line, and will look something like:
-// no permissions (reason); see <URL>
+//
+// no permissions (reason); see [URL]
std::string UsbNoPermissionsShortHelpText() {
std::string help_text = "no permissions";
- const char* problem = GetUdevProblem();
- if (problem != nullptr) {
- help_text += android::base::StringPrintf(" (%s)", problem);
- }
+ std::string problem(GetUdevProblem());
+ if (!problem.empty()) help_text += " (" + problem + ")";
return android::base::StringPrintf("%s; see [%s]", help_text.c_str(), kPermissionsHelpUrl);
}
-// Long help text can span multiple lines and should provide more detailed information.
+// Long help text can span multiple lines but doesn't currently provide more detailed information:
+//
+// insufficient permissions for device: reason
+// See [URL] for more information
std::string UsbNoPermissionsLongHelpText() {
std::string header = "insufficient permissions for device";
- const char* problem = GetUdevProblem();
- if (problem != nullptr) {
- header += android::base::StringPrintf(": %s", problem);
- }
+ std::string problem(GetUdevProblem());
+ if (!problem.empty()) header += ": " + problem;
- return android::base::StringPrintf("%s.\nSee [%s] for more information.",
- header.c_str(), kPermissionsHelpUrl);
+ return android::base::StringPrintf("%s\nSee [%s] for more information", header.c_str(),
+ kPermissionsHelpUrl);
}
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 04cd865..b28de4b 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -26,15 +26,19 @@
#include <unistd.h>
#include <atomic>
+#include <functional>
#include <list>
+#include <mutex>
#include <unordered_map>
#include <vector>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
#include "adb_io.h"
#include "adb_trace.h"
+#include "adb_unique_fd.h"
#include "adb_utils.h"
#if !ADB_HOST
@@ -75,13 +79,17 @@
static bool main_thread_valid;
static unsigned long main_thread_id;
-static void check_main_thread() {
+static auto& run_queue_notify_fd = *new unique_fd();
+static auto& run_queue_mutex = *new std::mutex();
+static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::vector<std::function<void()>>();
+
+void check_main_thread() {
if (main_thread_valid) {
CHECK_EQ(main_thread_id, adb_thread_id());
}
}
-static void set_main_thread() {
+void set_main_thread() {
main_thread_valid = true;
main_thread_id = adb_thread_id();
}
@@ -112,8 +120,7 @@
return android::base::StringPrintf("(fdevent %d %s)", fde->fd, state.c_str());
}
-fdevent *fdevent_create(int fd, fd_func func, void *arg)
-{
+fdevent* fdevent_create(int fd, fd_func func, void* arg) {
check_main_thread();
fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
if(fde == 0) return 0;
@@ -122,8 +129,7 @@
return fde;
}
-void fdevent_destroy(fdevent *fde)
-{
+void fdevent_destroy(fdevent* fde) {
check_main_thread();
if(fde == 0) return;
if(!(fde->state & FDE_CREATED)) {
@@ -278,8 +284,7 @@
}
}
-static void fdevent_call_fdfunc(fdevent* fde)
-{
+static void fdevent_call_fdfunc(fdevent* fde) {
unsigned events = fde->events;
fde->events = 0;
CHECK(fde->state & FDE_PENDING);
@@ -292,10 +297,7 @@
#include <sys/ioctl.h>
-static void fdevent_subproc_event_func(int fd, unsigned ev,
- void* /* userdata */)
-{
-
+static void fdevent_subproc_event_func(int fd, unsigned ev, void* /* userdata */) {
D("subproc handling on fd = %d, ev = %x", fd, ev);
CHECK_GE(fd, 0);
@@ -342,8 +344,7 @@
}
}
-void fdevent_subproc_setup()
-{
+static void fdevent_subproc_setup() {
int s[2];
if(adb_socketpair(s)) {
@@ -358,12 +359,63 @@
}
#endif // !ADB_HOST
-void fdevent_loop()
-{
+static void fdevent_run_flush() REQUIRES(run_queue_mutex) {
+ for (auto& f : run_queue) {
+ f();
+ }
+ run_queue.clear();
+}
+
+static void fdevent_run_func(int fd, unsigned ev, void* /* userdata */) {
+ CHECK_GE(fd, 0);
+ CHECK(ev & FDE_READ);
+
+ char buf[1024];
+
+ // Empty the fd.
+ if (adb_read(fd, buf, sizeof(buf)) == -1) {
+ PLOG(FATAL) << "failed to empty run queue notify fd";
+ }
+
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ fdevent_run_flush();
+}
+
+static void fdevent_run_setup() {
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ CHECK(run_queue_notify_fd.get() == -1);
+ int s[2];
+ if (adb_socketpair(s) != 0) {
+ PLOG(FATAL) << "failed to create run queue notify socketpair";
+ }
+
+ run_queue_notify_fd.reset(s[0]);
+ fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
+ CHECK(fde != nullptr);
+ fdevent_add(fde, FDE_READ);
+
+ fdevent_run_flush();
+}
+
+void fdevent_run_on_main_thread(std::function<void()> fn) {
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ run_queue.push_back(std::move(fn));
+
+ // run_queue_notify_fd could still be -1 if we're called before fdevent has finished setting up.
+ // In that case, rely on the setup code to flush the queue without a notification being needed.
+ if (run_queue_notify_fd != -1) {
+ if (adb_write(run_queue_notify_fd.get(), "", 1) != 1) {
+ PLOG(FATAL) << "failed to write to run queue notify fd";
+ }
+ }
+}
+
+void fdevent_loop() {
set_main_thread();
#if !ADB_HOST
fdevent_subproc_setup();
#endif // !ADB_HOST
+ fdevent_run_setup();
while (true) {
if (terminate_loop) {
@@ -393,6 +445,11 @@
void fdevent_reset() {
g_poll_node_map.clear();
g_pending_list.clear();
+
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ run_queue_notify_fd.reset();
+ run_queue.clear();
+
main_thread_valid = false;
terminate_loop = false;
}
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 207f9b7..896400a 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -20,6 +20,8 @@
#include <stddef.h>
#include <stdint.h> /* for int64_t */
+#include <functional>
+
/* events that may be observed */
#define FDE_READ 0x0001
#define FDE_WRITE 0x0002
@@ -76,9 +78,15 @@
*/
void fdevent_loop();
+void check_main_thread();
+
+// Queue an operation to run on the main thread.
+void fdevent_run_on_main_thread(std::function<void()> fn);
+
// The following functions are used only for tests.
void fdevent_terminate_loop();
size_t fdevent_installed_count();
void fdevent_reset();
+void set_main_thread();
#endif
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index c933ed5..86e0209 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -21,6 +21,7 @@
#include <limits>
#include <queue>
#include <string>
+#include <thread>
#include <vector>
#include "adb_io.h"
@@ -77,9 +78,9 @@
};
TEST_F(FdeventTest, fdevent_terminate) {
- adb_thread_t thread;
PrepareThread();
- ASSERT_TRUE(adb_thread_create([](void*) { fdevent_loop(); }, nullptr, &thread));
+
+ std::thread thread(fdevent_loop);
TerminateThread(thread);
}
@@ -112,7 +113,6 @@
int fd_pair2[2];
ASSERT_EQ(0, adb_socketpair(fd_pair1));
ASSERT_EQ(0, adb_socketpair(fd_pair2));
- adb_thread_t thread;
ThreadArg thread_arg;
thread_arg.first_read_fd = fd_pair1[0];
thread_arg.last_write_fd = fd_pair2[1];
@@ -121,8 +121,7 @@
int reader = fd_pair2[0];
PrepareThread();
- ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(FdEventThreadFunc), &thread_arg,
- &thread));
+ std::thread thread(FdEventThreadFunc, &thread_arg);
for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
std::string read_buffer = MESSAGE;
@@ -152,7 +151,7 @@
}
}
-static void InvalidFdThreadFunc(void*) {
+static void InvalidFdThreadFunc() {
const int INVALID_READ_FD = std::numeric_limits<int>::max() - 1;
size_t happened_event_count = 0;
InvalidFdArg read_arg;
@@ -171,7 +170,27 @@
}
TEST_F(FdeventTest, invalid_fd) {
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(InvalidFdThreadFunc, nullptr, &thread));
- ASSERT_TRUE(adb_thread_join(thread));
+ std::thread thread(InvalidFdThreadFunc);
+ thread.join();
+}
+
+TEST_F(FdeventTest, run_on_main_thread) {
+ std::vector<int> vec;
+
+ PrepareThread();
+ std::thread thread(fdevent_loop);
+
+ for (int i = 0; i < 100; ++i) {
+ fdevent_run_on_main_thread([i, &vec]() {
+ check_main_thread();
+ vec.push_back(i);
+ });
+ }
+
+ TerminateThread(thread);
+
+ ASSERT_EQ(100u, vec.size());
+ for (int i = 0; i < 100; ++i) {
+ ASSERT_EQ(i, vec[i]);
+ }
}
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index ef65b74..5ca49ac 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -16,6 +16,8 @@
#include <gtest/gtest.h>
+#include <thread>
+
#include "socket.h"
#include "sysdeps.h"
@@ -51,18 +53,18 @@
size_t GetAdditionalLocalSocketCount() {
#if ADB_HOST
- // dummy socket installed in PrepareThread()
- return 1;
-#else
- // dummy socket and one more socket installed in fdevent_subproc_setup()
+ // dummy socket installed in PrepareThread() + fdevent_run_on_main_thread socket
return 2;
+#else
+ // dummy socket + fdevent_run_on_main_thread + fdevent_subproc_setup() sockets
+ return 3;
#endif
}
- void TerminateThread(adb_thread_t thread) {
+ void TerminateThread(std::thread& thread) {
fdevent_terminate_loop();
ASSERT_TRUE(WriteFdExactly(dummy, "", 1));
- ASSERT_TRUE(adb_thread_join(thread));
+ thread.join();
ASSERT_EQ(0, adb_close(dummy));
}
};
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 22bd2f2..26f8d83 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -441,7 +441,7 @@
syncsendbuf sbuf;
sbuf.id = ID_DATA;
while (true) {
- int bytes_read = adb_read(lfd, sbuf.data, max);
+ int bytes_read = adb_read(lfd, sbuf.data, max - sizeof(SyncRequest));
if (bytes_read == -1) {
Error("reading '%s' locally failed: %s", lpath, strerror(errno));
adb_close(lfd);
@@ -674,11 +674,22 @@
return true;
}
-static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
- unsigned mtime, mode_t mode)
-{
+static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, unsigned mtime,
+ mode_t mode, bool sync) {
std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
+ if (sync) {
+ struct stat st;
+ if (sync_lstat(sc, rpath, &st)) {
+ // For links, we cannot update the atime/mtime.
+ if ((S_ISREG(mode & st.st_mode) && st.st_mtime == static_cast<time_t>(mtime)) ||
+ (S_ISLNK(mode & st.st_mode) && st.st_mtime >= static_cast<time_t>(mtime))) {
+ sc.RecordFilesSkipped(1);
+ return true;
+ }
+ }
+ }
+
if (S_ISLNK(mode)) {
#if !defined(_WIN32)
char buf[PATH_MAX];
@@ -902,7 +913,7 @@
if (list_only) {
sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
} else {
- if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode)) {
+ if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode, false)) {
return false;
}
}
@@ -916,7 +927,7 @@
return true;
}
-bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync) {
SyncConnection sc;
if (!sc.IsValid()) return false;
@@ -981,7 +992,7 @@
dst_dir.append(android::base::Basename(src_path));
}
- success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), false, false);
+ success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), sync, false);
continue;
} else if (!should_push_file(st.st_mode)) {
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
@@ -1002,7 +1013,7 @@
sc.NewTransfer();
sc.SetExpectedTotalBytes(st.st_size);
- success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
+ success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync);
sc.ReportTransferRate(src_path, TransferDirection::push);
}
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 2acf661..c6f3e66 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -206,6 +206,12 @@
__android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+
+ if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED) <
+ 0) {
+ D("[ Failed to fadvise: %d ]", errno);
+ }
+
if (fd < 0 && errno == ENOENT) {
if (!secure_mkdirs(android::base::Dirname(path))) {
SendSyncFailErrno(s, "secure_mkdirs failed");
@@ -283,25 +289,25 @@
// reading and throwing away ID_DATA packets until the other side notices
// that we've reported an error.
while (true) {
- if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
+ if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) break;
if (msg.data.id == ID_DONE) {
- goto abort;
+ break;
} else if (msg.data.id != ID_DATA) {
char id[5];
memcpy(id, &msg.data.id, sizeof(msg.data.id));
id[4] = '\0';
D("handle_send_fail received unexpected id '%s' during failure", id);
- goto abort;
+ break;
}
if (msg.data.size > buffer.size()) {
D("handle_send_fail received oversized packet of length '%u' during failure",
msg.data.size);
- goto abort;
+ break;
}
- if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
+ if (!ReadFdExactly(s, &buffer[0], msg.data.size)) break;
}
abort:
@@ -413,10 +419,14 @@
return false;
}
+ if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE) < 0) {
+ D("[ Failed to fadvise: %d ]", errno);
+ }
+
syncmsg msg;
msg.data.id = ID_DATA;
while (true) {
- int r = adb_read(fd, &buffer[0], buffer.size());
+ int r = adb_read(fd, &buffer[0], buffer.size() - sizeof(msg.data));
if (r <= 0) {
if (r == 0) break;
SendSyncFailErrno(s, "read failed");
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 90f1965..6606efd 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -81,7 +81,7 @@
void file_sync_service(int fd, void* cookie);
bool do_sync_ls(const char* path);
-bool do_sync_push(const std::vector<const char*>& srcs, const char* dst);
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync);
bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
bool copy_attrs, const char* name=nullptr);
diff --git a/adb/services.cpp b/adb/services.cpp
index c9ea039..ca34556 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -31,6 +31,8 @@
#include <unistd.h>
#endif
+#include <thread>
+
#include <android-base/file.h>
#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
@@ -148,6 +150,7 @@
sync();
+ if (!reboot_arg || !reboot_arg[0]) reboot_arg = "adb";
std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg);
if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
WriteFdFmt(fd, "reboot (%s) failed\n", reboot_string.c_str());
@@ -185,7 +188,7 @@
return -1;
}
VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
- if (handle_forward_request(command, kTransportAny, nullptr, s[1]) < 0) {
+ if (handle_forward_request(command, kTransportAny, nullptr, 0, s[1]) < 0) {
SendFail(s[1], "not a reverse forwarding command");
}
adb_close(s[1]);
@@ -259,13 +262,7 @@
sti->cookie = cookie;
sti->fd = s[1];
- if (!adb_thread_create(service_bootstrap_func, sti)) {
- free(sti);
- adb_close(s[0]);
- adb_close(s[1]);
- printf("cannot create service thread\n");
- return -1;
- }
+ std::thread(service_bootstrap_func, sti).detach();
D("service thread started, %d:%d",s[0], s[1]);
return s[0];
@@ -338,6 +335,7 @@
struct state_info {
TransportType transport_type;
std::string serial;
+ TransportId transport_id;
ConnectionState state;
};
@@ -350,8 +348,9 @@
bool is_ambiguous = false;
std::string error = "unknown error";
const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
- atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error);
- if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->connection_state)) {
+ atransport* t = acquire_one_transport(sinfo->transport_type, serial, sinfo->transport_id,
+ &is_ambiguous, &error);
+ if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
SendOkay(fd);
break;
} else if (!is_ambiguous) {
@@ -441,9 +440,11 @@
#endif
#if ADB_HOST
-asocket* host_service_to_socket(const char* name, const char* serial) {
+asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id) {
if (!strcmp(name,"track-devices")) {
- return create_device_tracker();
+ return create_device_tracker(false);
+ } else if (!strcmp(name, "track-devices-l")) {
+ return create_device_tracker(true);
} else if (android::base::StartsWith(name, "wait-for-")) {
name += strlen("wait-for-");
@@ -454,6 +455,7 @@
}
if (serial) sinfo->serial = serial;
+ sinfo->transport_id = transport_id;
if (android::base::StartsWith(name, "local")) {
name += strlen("local");
@@ -482,11 +484,17 @@
return nullptr;
}
- int fd = create_service_thread(wait_for_state, sinfo.release());
+ int fd = create_service_thread(wait_for_state, sinfo.get());
+ if (fd != -1) {
+ sinfo.release();
+ }
return create_local_socket(fd);
} else if (!strncmp(name, "connect:", 8)) {
char* host = strdup(name + 8);
int fd = create_service_thread(connect_service, host);
+ if (fd == -1) {
+ free(host);
+ }
return create_local_socket(fd);
}
return NULL;
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
index d4dd256..253d14a 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/set_verity_enable_state_service.cpp
@@ -20,6 +20,7 @@
#include <fcntl.h>
#include <inttypes.h>
+#include <libavb_user/libavb_user.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/stat.h>
@@ -45,13 +46,12 @@
#endif
/* Turn verity on/off */
-static int set_verity_enabled_state(int fd, const char *block_device,
- const char* mount_point, bool enable)
-{
+static bool set_verity_enabled_state(int fd, const char* block_device, const char* mount_point,
+ bool enable) {
if (!make_block_device_writable(block_device)) {
WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
block_device, strerror(errno));
- return -1;
+ return false;
}
fec::io fh(block_device, O_RDWR);
@@ -59,70 +59,142 @@
if (!fh) {
WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
WriteFdFmt(fd, "Maybe run adb root?\n");
- return -1;
+ return false;
}
fec_verity_metadata metadata;
if (!fh.get_verity_metadata(metadata)) {
WriteFdFmt(fd, "Couldn't find verity metadata!\n");
- return -1;
+ return false;
}
if (!enable && metadata.disabled) {
WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
- return -1;
+ return false;
}
if (enable && !metadata.disabled) {
WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
- return -1;
+ return false;
}
if (!fh.set_verity_status(enable)) {
WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
enable ? "enabled" : "disabled",
block_device, strerror(errno));
- return -1;
+ return false;
}
WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
- return 0;
+ return true;
+}
+
+/* Helper function to get A/B suffix, if any. If the device isn't
+ * using A/B the empty string is returned. Otherwise either "_a",
+ * "_b", ... is returned.
+ *
+ * Note that since sometime in O androidboot.slot_suffix is deprecated
+ * and androidboot.slot should be used instead. Since bootloaders may
+ * be out of sync with the OS, we check both and for extra safety
+ * prepend a leading underscore if there isn't one already.
+ */
+static std::string get_ab_suffix() {
+ std::string ab_suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
+ if (ab_suffix == "") {
+ ab_suffix = android::base::GetProperty("ro.boot.slot", "");
+ }
+ if (ab_suffix.size() > 0 && ab_suffix[0] != '_') {
+ ab_suffix = std::string("_") + ab_suffix;
+ }
+ return ab_suffix;
+}
+
+/* Use AVB to turn verity on/off */
+static bool set_avb_verity_enabled_state(int fd, AvbOps* ops, bool enable_verity) {
+ std::string ab_suffix = get_ab_suffix();
+
+ bool verity_enabled;
+ if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
+ WriteFdFmt(fd, "Error getting verity state\n");
+ return false;
+ }
+
+ if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
+ WriteFdFmt(fd, "verity is already %s\n", verity_enabled ? "enabled" : "disabled");
+ return false;
+ }
+
+ if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
+ WriteFdFmt(fd, "Error setting verity\n");
+ return false;
+ }
+
+ WriteFdFmt(fd, "Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
+ return true;
}
void set_verity_enabled_state_service(int fd, void* cookie) {
unique_fd closer(fd);
+ bool any_changed = false;
bool enable = (cookie != NULL);
- if (!kAllowDisableVerity) {
- WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
- enable ? "enable" : "disable");
+
+ // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
+ // contract, androidboot.vbmeta.digest is set by the bootloader
+ // when using AVB).
+ bool using_avb = !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
+
+ // If using AVB, dm-verity is used on any build so we want it to
+ // be possible to disable/enable on any build (except USER). For
+ // VB1.0 dm-verity is only enabled on certain builds.
+ if (!using_avb) {
+ if (!kAllowDisableVerity) {
+ WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
+ enable ? "enable" : "disable");
+ }
+
+ if (!android::base::GetBoolProperty("ro.secure", false)) {
+ WriteFdFmt(fd, "verity not enabled - ENG build\n");
+ return;
+ }
}
- if (!android::base::GetBoolProperty("ro.secure", false)) {
- WriteFdFmt(fd, "verity not enabled - ENG build\n");
- return;
- }
+ // Should never be possible to disable dm-verity on a USER build
+ // regardless of using AVB or VB1.0.
if (!__android_log_is_debuggable()) {
WriteFdFmt(fd, "verity cannot be disabled/enabled - USER build\n");
return;
}
- // read all fstab entries at once from all sources
- fstab = fs_mgr_read_fstab_default();
- if (!fstab) {
- WriteFdFmt(fd, "Failed to read fstab\nMaybe run adb root?\n");
- return;
- }
+ if (using_avb) {
+ // Yep, the system is using AVB.
+ AvbOps* ops = avb_ops_user_new();
+ if (ops == nullptr) {
+ WriteFdFmt(fd, "Error getting AVB ops\n");
+ return;
+ }
+ if (set_avb_verity_enabled_state(fd, ops, enable)) {
+ any_changed = true;
+ }
+ avb_ops_user_free(ops);
+ } else {
+ // Not using AVB - assume VB1.0.
- // Loop through entries looking for ones that vold manages.
- bool any_changed = false;
- for (int i = 0; i < fstab->num_entries; i++) {
- if (fs_mgr_is_verified(&fstab->recs[i])) {
- if (!set_verity_enabled_state(fd, fstab->recs[i].blk_device,
- fstab->recs[i].mount_point,
- enable)) {
- any_changed = true;
+ // read all fstab entries at once from all sources
+ fstab = fs_mgr_read_fstab_default();
+ if (!fstab) {
+ WriteFdFmt(fd, "Failed to read fstab\nMaybe run adb root?\n");
+ return;
+ }
+
+ // Loop through entries looking for ones that vold manages.
+ for (int i = 0; i < fstab->num_entries; i++) {
+ if (fs_mgr_is_verified(&fstab->recs[i])) {
+ if (set_verity_enabled_state(fd, fstab->recs[i].blk_device,
+ fstab->recs[i].mount_point, enable)) {
+ any_changed = true;
+ }
}
}
}
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index d4f334b..ee821f8 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -90,6 +90,7 @@
#include <memory>
#include <string>
+#include <thread>
#include <unordered_map>
#include <vector>
@@ -392,12 +393,7 @@
bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
Subprocess* raw = subprocess.release();
- if (!adb_thread_create(ThreadHandler, raw)) {
- *error =
- android::base::StringPrintf("failed to create subprocess thread: %s", strerror(errno));
- kill(raw->pid_, SIGKILL);
- return false;
- }
+ std::thread(ThreadHandler, raw).detach();
return true;
}
diff --git a/adb/socket.h b/adb/socket.h
index 4acdf4a..64d05a9 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -19,84 +19,83 @@
#include <stddef.h>
+#include <memory>
+
#include "fdevent.h"
struct apacket;
class atransport;
/* An asocket represents one half of a connection between a local and
-** remote entity. A local asocket is bound to a file descriptor. A
-** remote asocket is bound to the protocol engine.
-*/
+ * remote entity. A local asocket is bound to a file descriptor. A
+ * remote asocket is bound to the protocol engine.
+ */
struct asocket {
- /* chain pointers for the local/remote list of
- ** asockets that this asocket lives in
- */
- asocket *next;
- asocket *prev;
+ /* chain pointers for the local/remote list of
+ * asockets that this asocket lives in
+ */
+ asocket* next;
+ asocket* prev;
- /* the unique identifier for this asocket
- */
+ /* the unique identifier for this asocket
+ */
unsigned id;
- /* flag: set when the socket's peer has closed
- ** but packets are still queued for delivery
- */
- int closing;
+ /* flag: set when the socket's peer has closed
+ * but packets are still queued for delivery
+ */
+ int closing;
// flag: set when the socket failed to write, so the socket will not wait to
// write packets and close directly.
bool has_write_error;
- /* flag: quit adbd when both ends close the
- ** local service socket
- */
- int exit_on_close;
+ /* flag: quit adbd when both ends close the
+ * local service socket
+ */
+ int exit_on_close;
- /* the asocket we are connected to
- */
+ // the asocket we are connected to
+ asocket* peer;
- asocket *peer;
-
- /* For local asockets, the fde is used to bind
- ** us to our fd event system. For remote asockets
- ** these fields are not used.
- */
+ /* For local asockets, the fde is used to bind
+ * us to our fd event system. For remote asockets
+ * these fields are not used.
+ */
fdevent fde;
int fd;
- /* queue of apackets waiting to be written
- */
- apacket *pkt_first;
- apacket *pkt_last;
+ // queue of apackets waiting to be written
+ apacket* pkt_first;
+ apacket* pkt_last;
- /* enqueue is called by our peer when it has data
- ** for us. It should return 0 if we can accept more
- ** data or 1 if not. If we return 1, we must call
- ** peer->ready() when we once again are ready to
- ** receive data.
- */
- int (*enqueue)(asocket *s, apacket *pkt);
+ /* enqueue is called by our peer when it has data
+ * for us. It should return 0 if we can accept more
+ * data or 1 if not. If we return 1, we must call
+ * peer->ready() when we once again are ready to
+ * receive data.
+ */
+ int (*enqueue)(asocket* s, apacket* pkt);
- /* ready is called by the peer when it is ready for
- ** us to send data via enqueue again
- */
- void (*ready)(asocket *s);
+ /* ready is called by the peer when it is ready for
+ * us to send data via enqueue again
+ */
+ void (*ready)(asocket* s);
- /* shutdown is called by the peer before it goes away.
- ** the socket should not do any further calls on its peer.
- ** Always followed by a call to close. Optional, i.e. can be NULL.
- */
- void (*shutdown)(asocket *s);
+ /* shutdown is called by the peer before it goes away.
+ * the socket should not do any further calls on its peer.
+ * Always followed by a call to close. Optional, i.e. can be NULL.
+ */
+ void (*shutdown)(asocket* s);
- /* close is called by the peer when it has gone away.
- ** we are not allowed to make any further calls on the
- ** peer once our close method is called.
- */
- void (*close)(asocket *s);
+ /* close is called by the peer when it has gone away.
+ * we are not allowed to make any further calls on the
+ * peer once our close method is called.
+ */
+ void (*close)(asocket* s);
- /* A socket is bound to atransport */
- atransport *transport;
+ /* A socket is bound to atransport */
+ atransport* transport;
size_t get_max_payload() const;
};
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index f56f7f7..f7c66db 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -42,10 +42,6 @@
class LocalSocketTest : public FdeventTest {};
-static void FdEventThreadFunc(void*) {
- fdevent_loop();
-}
-
constexpr auto SLEEP_FOR_FDEVENT = 100ms;
TEST_F(LocalSocketTest, smoke) {
@@ -88,8 +84,7 @@
connect(prev_tail, end);
PrepareThread();
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(FdEventThreadFunc, nullptr, &thread));
+ std::thread thread(fdevent_loop);
for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
std::string read_buffer = MESSAGE;
@@ -152,9 +147,7 @@
arg.cause_close_fd = cause_close_fd[1];
PrepareThread();
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
- &arg, &thread));
+ std::thread thread(CloseWithPacketThreadFunc, &arg);
// Wait until the fdevent_loop() starts.
std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
ASSERT_EQ(0, adb_close(cause_close_fd[0]));
@@ -177,9 +170,7 @@
arg.cause_close_fd = cause_close_fd[1];
PrepareThread();
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
- &arg, &thread));
+ std::thread thread(CloseWithPacketThreadFunc, &arg);
// Wait until the fdevent_loop() starts.
std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
ASSERT_EQ(0, adb_close(cause_close_fd[0]));
@@ -211,10 +202,7 @@
arg.cause_close_fd = cause_close_fd[1];
PrepareThread();
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
- &arg, &thread));
-
+ std::thread thread(CloseWithPacketThreadFunc, &arg);
// Wait until the fdevent_loop() starts.
std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
EXPECT_EQ(2u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
@@ -252,9 +240,7 @@
int listen_fd = network_inaddr_any_server(5038, SOCK_STREAM, &error);
ASSERT_GE(listen_fd, 0);
- adb_thread_t client_thread;
- ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(ClientThreadFunc), nullptr,
- &client_thread));
+ std::thread client_thread(ClientThreadFunc);
int accept_fd = adb_socket_accept(listen_fd, nullptr, nullptr);
ASSERT_GE(accept_fd, 0);
@@ -262,16 +248,14 @@
arg.socket_fd = accept_fd;
PrepareThread();
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseRdHupSocketThreadFunc),
- &arg, &thread));
+ std::thread thread(CloseRdHupSocketThreadFunc, &arg);
// Wait until the fdevent_loop() starts.
std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
EXPECT_EQ(1u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
// Wait until the client closes its socket.
- ASSERT_TRUE(adb_thread_join(client_thread));
+ client_thread.join();
std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 59a48f5..c53fbb4 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -96,7 +96,7 @@
}
void remove_socket(asocket* s) {
- // socket_list_lock should already be held
+ std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
if (s->prev && s->next) {
s->prev->next = s->next;
s->next->prev = s->prev;
@@ -430,10 +430,11 @@
}
#if ADB_HOST
-static asocket* create_host_service_socket(const char* name, const char* serial) {
+static asocket* create_host_service_socket(const char* name, const char* serial,
+ TransportId transport_id) {
asocket* s;
- s = host_service_to_socket(name, serial);
+ s = host_service_to_socket(name, serial, transport_id);
if (s != NULL) {
D("LS(%d) bound to '%s'", s->id, name);
@@ -658,6 +659,7 @@
#if ADB_HOST
char* service = nullptr;
char* serial = nullptr;
+ TransportId transport_id = 0;
TransportType type = kTransportAny;
#endif
@@ -686,7 +688,7 @@
}
len = unhex(p->data, 4);
- if ((len < 1) || (len > MAX_PAYLOAD_V1)) {
+ if ((len < 1) || (len > MAX_PAYLOAD)) {
D("SS(%d): bad size (%d)", s->id, len);
goto fail;
}
@@ -715,6 +717,14 @@
serial = service;
service = serial_end + 1;
}
+ } else if (!strncmp(service, "host-transport-id:", strlen("host-transport-id:"))) {
+ service += strlen("host-transport-id:");
+ transport_id = strtoll(service, &service, 10);
+
+ if (*service != ':') {
+ return -1;
+ }
+ service++;
} else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
type = kTransportUsb;
service += strlen("host-usb:");
@@ -736,7 +746,7 @@
** the OKAY or FAIL message and all we have to do
** is clean up.
*/
- if (handle_host_request(service, type, serial, s->peer->fd, s) == 0) {
+ if (handle_host_request(service, type, serial, transport_id, s->peer->fd, s) == 0) {
/* XXX fail message? */
D("SS(%d): handled host service '%s'", s->id, service);
goto fail;
@@ -751,7 +761,7 @@
** if no such service exists, we'll fail out
** and tear down here.
*/
- s2 = create_host_service_socket(service, serial);
+ s2 = create_host_service_socket(service, serial, transport_id);
if (s2 == 0) {
D("SS(%d): couldn't create host service '%s'", s->id, service);
SendFail(s->peer->fd, "unknown host service");
@@ -783,7 +793,7 @@
#else /* !ADB_HOST */
if (s->transport == nullptr) {
std::string error_msg = "unknown failure";
- s->transport = acquire_one_transport(kTransportAny, nullptr, nullptr, &error_msg);
+ s->transport = acquire_one_transport(kTransportAny, nullptr, 0, nullptr, &error_msg);
if (s->transport == nullptr) {
SendFail(s->peer->fd, error_msg);
goto fail;
@@ -794,7 +804,7 @@
if (!s->transport) {
SendFail(s->peer->fd, "device offline (no transport)");
goto fail;
- } else if (s->transport->connection_state == kCsOffline) {
+ } else if (s->transport->GetConnectionState() == kCsOffline) {
/* if there's no remote we fail the connection
** right here and terminate it
*/
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index f195b4e..0abb680 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -35,6 +35,7 @@
#include <android-base/utf8.h>
#include "sysdeps/errno.h"
+#include "sysdeps/network.h"
#include "sysdeps/stat.h"
/*
@@ -98,64 +99,6 @@
return c == '\\' || c == '/';
}
-typedef void (*adb_thread_func_t)(void* arg);
-typedef HANDLE adb_thread_t;
-
-struct adb_winthread_args {
- adb_thread_func_t func;
- void* arg;
-};
-
-static unsigned __stdcall adb_winthread_wrapper(void* heap_args) {
- // Move the arguments from the heap onto the thread's stack.
- adb_winthread_args thread_args = *static_cast<adb_winthread_args*>(heap_args);
- delete static_cast<adb_winthread_args*>(heap_args);
- thread_args.func(thread_args.arg);
- return 0;
-}
-
-static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg,
- adb_thread_t* thread = nullptr) {
- adb_winthread_args* args = new adb_winthread_args{.func = func, .arg = arg};
- uintptr_t handle = _beginthreadex(nullptr, 0, adb_winthread_wrapper, args, 0, nullptr);
- if (handle != static_cast<uintptr_t>(0)) {
- if (thread) {
- *thread = reinterpret_cast<HANDLE>(handle);
- } else {
- CloseHandle(thread);
- }
- return true;
- }
- return false;
-}
-
-static __inline__ bool adb_thread_join(adb_thread_t thread) {
- switch (WaitForSingleObject(thread, INFINITE)) {
- case WAIT_OBJECT_0:
- CloseHandle(thread);
- return true;
-
- case WAIT_FAILED:
- fprintf(stderr, "adb_thread_join failed: %s\n",
- android::base::SystemErrorCodeToString(GetLastError()).c_str());
- break;
-
- default:
- abort();
- }
-
- return false;
-}
-
-static __inline__ bool adb_thread_detach(adb_thread_t thread) {
- CloseHandle(thread);
- return true;
-}
-
-static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
- _endthreadex(0);
-}
-
static __inline__ int adb_thread_setname(const std::string& name) {
// TODO: See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for how to set
// the thread name in Windows. Unfortunately, it only works during debugging, but
@@ -163,14 +106,6 @@
return 0;
}
-static __inline__ adb_thread_t adb_thread_self() {
- return GetCurrentThread();
-}
-
-static __inline__ bool adb_thread_equal(adb_thread_t lhs, adb_thread_t rhs) {
- return GetThreadId(lhs) == GetThreadId(rhs);
-}
-
static __inline__ unsigned long adb_thread_id()
{
return GetCurrentThreadId();
@@ -248,8 +183,6 @@
int unix_isatty(int fd);
#define isatty ___xxx_isatty
-int network_loopback_client(int port, int type, std::string* error);
-int network_loopback_server(int port, int type, std::string* error);
int network_inaddr_any_server(int port, int type, std::string* error);
inline int network_local_client(const char* name, int namespace_id, int type, std::string* error) {
@@ -268,10 +201,6 @@
#undef accept
#define accept ___xxx_accept
-int adb_getsockname(int fd, struct sockaddr* sockaddr, socklen_t* optlen);
-#undef getsockname
-#define getsockname(...) ___xxx_getsockname(__VA__ARGS__)
-
// Returns the local port number of a bound socket, or -1 on failure.
int adb_socket_get_local_port(int fd);
@@ -591,17 +520,6 @@
return fd;
}
-inline int network_loopback_client(int port, int type, std::string* error) {
- return _fd_set_error_str(socket_network_client("localhost", port, type), error);
-}
-
-inline int network_loopback_server(int port, int type, std::string* error) {
- int fd = socket_loopback_server(port, type);
- if (fd < 0 && errno == EAFNOSUPPORT)
- return _fd_set_error_str(socket_loopback_server6(port, type), error);
- return _fd_set_error_str(fd, error);
-}
-
inline int network_inaddr_any_server(int port, int type, std::string* error) {
return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
}
@@ -660,71 +578,16 @@
#define unix_write adb_write
#define unix_close adb_close
-// Win32 is limited to DWORDs for thread return values; limit the POSIX systems to this as well to
-// ensure compatibility.
-typedef void (*adb_thread_func_t)(void* arg);
-typedef pthread_t adb_thread_t;
-
-struct adb_pthread_args {
- adb_thread_func_t func;
- void* arg;
-};
-
-static void* adb_pthread_wrapper(void* heap_args) {
- // Move the arguments from the heap onto the thread's stack.
- adb_pthread_args thread_args = *reinterpret_cast<adb_pthread_args*>(heap_args);
- delete static_cast<adb_pthread_args*>(heap_args);
- thread_args.func(thread_args.arg);
- return nullptr;
-}
-
-static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg,
- adb_thread_t* thread = nullptr) {
- pthread_t temp;
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, thread ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED);
- auto* pthread_args = new adb_pthread_args{.func = start, .arg = arg};
- errno = pthread_create(&temp, &attr, adb_pthread_wrapper, pthread_args);
- if (errno == 0) {
- if (thread) {
- *thread = temp;
- }
- return true;
- }
- return false;
-}
-
-static __inline__ bool adb_thread_join(adb_thread_t thread) {
- errno = pthread_join(thread, nullptr);
- return errno == 0;
-}
-
-static __inline__ bool adb_thread_detach(adb_thread_t thread) {
- errno = pthread_detach(thread);
- return errno == 0;
-}
-
-static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
- pthread_exit(nullptr);
-}
-
static __inline__ int adb_thread_setname(const std::string& name) {
#ifdef __APPLE__
return pthread_setname_np(name.c_str());
#else
- const char *s = name.c_str();
-
- // pthread_setname_np fails rather than truncating long strings.
- const int max_task_comm_len = 16; // including the null terminator
- if (name.length() > (max_task_comm_len - 1)) {
- char buf[max_task_comm_len];
- strncpy(buf, name.c_str(), sizeof(buf) - 1);
- buf[sizeof(buf) - 1] = '\0';
- s = buf;
- }
-
- return pthread_setname_np(pthread_self(), s) ;
+ // Both bionic and glibc's pthread_setname_np fails rather than truncating long strings.
+ // glibc doesn't have strlcpy, so we have to fake it.
+ char buf[16]; // MAX_TASK_COMM_LEN, but that's not exported by the kernel headers.
+ strncpy(buf, name.c_str(), sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ return pthread_setname_np(pthread_self(), buf);
#endif
}
diff --git a/init/signal_handler.h b/adb/sysdeps/network.h
similarity index 71%
copy from init/signal_handler.h
copy to adb/sysdeps/network.h
index 449b4af..83ce371 100644
--- a/init/signal_handler.h
+++ b/adb/sysdeps/network.h
@@ -1,5 +1,7 @@
+#pragma once
+
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * 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.
@@ -14,9 +16,7 @@
* limitations under the License.
*/
-#ifndef _INIT_SIGNAL_HANDLER_H_
-#define _INIT_SIGNAL_HANDLER_H_
+#include <string>
-void signal_handler_init(void);
-
-#endif
+int network_loopback_client(int port, int type, std::string* error);
+int network_loopback_server(int port, int type, std::string* error);
diff --git a/adb/sysdeps/posix/network.cpp b/adb/sysdeps/posix/network.cpp
new file mode 100644
index 0000000..45da5af
--- /dev/null
+++ b/adb/sysdeps/posix/network.cpp
@@ -0,0 +1,127 @@
+/*
+ * 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 "sysdeps/network.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include <string>
+
+#include "adb_unique_fd.h"
+
+static void set_error(std::string* error) {
+ if (error) {
+ *error = strerror(errno);
+ }
+}
+
+static sockaddr* loopback_addr4(sockaddr_storage* addr, socklen_t* addrlen, int port) {
+ struct sockaddr_in* addr4 = reinterpret_cast<sockaddr_in*>(addr);
+ *addrlen = sizeof(*addr4);
+
+ addr4->sin_family = AF_INET;
+ addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr4->sin_port = htons(port);
+ return reinterpret_cast<sockaddr*>(addr);
+}
+
+static sockaddr* loopback_addr6(sockaddr_storage* addr, socklen_t* addrlen, int port) {
+ struct sockaddr_in6* addr6 = reinterpret_cast<sockaddr_in6*>(addr);
+ *addrlen = sizeof(*addr6);
+
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_addr = in6addr_loopback;
+ addr6->sin6_port = htons(port);
+ return reinterpret_cast<sockaddr*>(addr);
+}
+
+static int _network_loopback_client(bool ipv6, int port, int type, std::string* error) {
+ unique_fd s(socket(ipv6 ? AF_INET6 : AF_INET, type, 0));
+ if (s == -1) {
+ set_error(error);
+ return -1;
+ }
+
+ struct sockaddr_storage addr_storage = {};
+ socklen_t addrlen = sizeof(addr_storage);
+ sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, 0);
+
+ if (bind(s.get(), addr, addrlen) != 0) {
+ set_error(error);
+ return -1;
+ }
+
+ addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);
+
+ if (connect(s.get(), addr, addrlen) != 0) {
+ set_error(error);
+ return -1;
+ }
+
+ return s.release();
+}
+
+int network_loopback_client(int port, int type, std::string* error) {
+ // Try IPv4 first, use IPv6 as a fallback.
+ int rc = _network_loopback_client(false, port, type, error);
+ if (rc == -1) {
+ return _network_loopback_client(true, port, type, error);
+ }
+ return rc;
+}
+
+static int _network_loopback_server(bool ipv6, int port, int type, std::string* error) {
+ unique_fd s(socket(ipv6 ? AF_INET6 : AF_INET, type, 0));
+ if (s == -1) {
+ set_error(error);
+ return -1;
+ }
+
+ int n = 1;
+ setsockopt(s.get(), SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ struct sockaddr_storage addr_storage = {};
+ socklen_t addrlen = sizeof(addr_storage);
+ sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);
+
+ if (bind(s, addr, addrlen) != 0) {
+ set_error(error);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
+ // Arbitrarily selected value, ported from libcutils.
+ if (listen(s, 4) != 0) {
+ set_error(error);
+ return -1;
+ }
+ }
+
+ return s.release();
+}
+
+int network_loopback_server(int port, int type, std::string* error) {
+ int rc = _network_loopback_server(false, port, type, error);
+
+ // Only attempt to listen on IPv6 if IPv4 is unavailable.
+ // We don't want to start an IPv6 server if there's already an IPv4 one running.
+ if (rc == -1 && (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT)) {
+ return _network_loopback_server(true, port, type, error);
+ }
+ return rc;
+}
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
index 9007e75..fd19882 100644
--- a/adb/sysdeps_test.cpp
+++ b/adb/sysdeps_test.cpp
@@ -25,54 +25,6 @@
#include "sysdeps.h"
#include "sysdeps/chrono.h"
-static void increment_atomic_int(void* c) {
- std::this_thread::sleep_for(1s);
- reinterpret_cast<std::atomic<int>*>(c)->fetch_add(1);
-}
-
-TEST(sysdeps_thread, smoke) {
- std::atomic<int> counter(0);
-
- for (int i = 0; i < 100; ++i) {
- ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter));
- }
-
- std::this_thread::sleep_for(2s);
- ASSERT_EQ(100, counter.load());
-}
-
-TEST(sysdeps_thread, join) {
- std::atomic<int> counter(0);
- std::vector<adb_thread_t> threads(500);
- for (size_t i = 0; i < threads.size(); ++i) {
- ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter, &threads[i]));
- }
-
- int current = counter.load();
- ASSERT_GE(current, 0);
- // Make sure that adb_thread_create actually creates threads, and doesn't do something silly
- // like synchronously run the function passed in. The sleep in increment_atomic_int should be
- // enough to keep this from being flakey.
- ASSERT_LT(current, 500);
-
- for (const auto& thread : threads) {
- ASSERT_TRUE(adb_thread_join(thread));
- }
-
- ASSERT_EQ(500, counter.load());
-}
-
-TEST(sysdeps_thread, exit) {
- adb_thread_t thread;
- ASSERT_TRUE(adb_thread_create(
- [](void*) {
- adb_thread_exit();
- for (;;) continue;
- },
- nullptr, &thread));
- ASSERT_TRUE(adb_thread_join(thread));
-}
-
TEST(sysdeps_socketpair, smoke) {
int fds[2];
ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
@@ -249,65 +201,22 @@
}
}
-TEST(sysdeps_mutex, mutex_smoke) {
- static std::atomic<bool> finished(false);
- static std::mutex &m = *new std::mutex();
- m.lock();
- ASSERT_FALSE(m.try_lock());
- adb_thread_create([](void*) {
- ASSERT_FALSE(m.try_lock());
- m.lock();
- finished.store(true);
- std::this_thread::sleep_for(200ms);
- m.unlock();
- }, nullptr);
-
- ASSERT_FALSE(finished.load());
- std::this_thread::sleep_for(100ms);
- ASSERT_FALSE(finished.load());
- m.unlock();
- std::this_thread::sleep_for(100ms);
- m.lock();
- ASSERT_TRUE(finished.load());
- m.unlock();
-}
-
-TEST(sysdeps_mutex, recursive_mutex_smoke) {
- static std::recursive_mutex &m = *new std::recursive_mutex();
-
- m.lock();
- ASSERT_TRUE(m.try_lock());
- m.unlock();
-
- adb_thread_create([](void*) {
- ASSERT_FALSE(m.try_lock());
- m.lock();
- std::this_thread::sleep_for(500ms);
- m.unlock();
- }, nullptr);
-
- std::this_thread::sleep_for(100ms);
- m.unlock();
- std::this_thread::sleep_for(100ms);
- ASSERT_FALSE(m.try_lock());
- m.lock();
- m.unlock();
-}
-
TEST(sysdeps_condition_variable, smoke) {
static std::mutex &m = *new std::mutex;
static std::condition_variable &cond = *new std::condition_variable;
static volatile bool flag = false;
std::unique_lock<std::mutex> lock(m);
- adb_thread_create([](void*) {
+ std::thread thread([]() {
m.lock();
flag = true;
cond.notify_one();
m.unlock();
- }, nullptr);
+ });
while (!flag) {
cond.wait(lock);
}
+
+ thread.join();
}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index f997e6b..5873b2b 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -984,7 +984,7 @@
return -1;
}
- int result = (getsockname)(fh->fh_socket, sockaddr, optlen);
+ int result = getsockname(fh->fh_socket, sockaddr, optlen);
if (result == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd,
@@ -1042,11 +1042,6 @@
int local_port = -1;
std::string error;
- struct sockaddr_storage peer_addr = {};
- struct sockaddr_storage client_addr = {};
- socklen_t peer_socklen = sizeof(peer_addr);
- socklen_t client_socklen = sizeof(client_addr);
-
server = network_loopback_server(0, SOCK_STREAM, &error);
if (server < 0) {
D("adb_socketpair: failed to create server: %s", error.c_str());
@@ -1066,32 +1061,12 @@
goto fail;
}
- // Make sure that the peer that connected to us and the client are the same.
- accepted = adb_socket_accept(server, reinterpret_cast<sockaddr*>(&peer_addr), &peer_socklen);
+ accepted = adb_socket_accept(server, nullptr, nullptr);
if (accepted < 0) {
D("adb_socketpair: failed to accept: %s", strerror(errno));
goto fail;
}
-
- if (adb_getsockname(client, reinterpret_cast<sockaddr*>(&client_addr), &client_socklen) != 0) {
- D("adb_socketpair: failed to getpeername: %s", strerror(errno));
- goto fail;
- }
-
- if (peer_socklen != client_socklen) {
- D("adb_socketpair: client and peer sockaddrs have different lengths");
- errno = EIO;
- goto fail;
- }
-
- if (memcmp(&peer_addr, &client_addr, peer_socklen) != 0) {
- D("adb_socketpair: client and peer sockaddrs don't match");
- errno = EIO;
- goto fail;
- }
-
adb_close(server);
-
sv[0] = client;
sv[1] = accepted;
return 0;
diff --git a/adb/test_adb.py b/adb/test_adb.py
index cb3e0d8..98c8a59 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -60,13 +60,13 @@
stderr=subprocess.STDOUT)
out, _ = p.communicate()
self.assertEqual(1, p.returncode)
- self.assertIn('help message', out)
+ self.assertIn('requires an argument', out)
p = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
out, _ = p.communicate()
self.assertEqual(1, p.returncode)
- self.assertIn('error', out)
+ self.assertIn('invalid port', out)
# Helper method that reads a pipe until it is closed, then sets the event.
def _read_pipe_and_set_event(self, pipe, event):
diff --git a/adb/test_device.py b/adb/test_device.py
index e76aaed..9e1a2ec 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -342,6 +342,13 @@
out = self.device.shell(['echo', 'foo'])[0]
self.assertEqual(out, 'foo' + self.device.linesep)
+ def test_shell_command_length(self):
+ # Devices that have shell_v2 should be able to handle long commands.
+ if self.device.has_shell_protocol():
+ rc, out, err = self.device.shell_nocheck(['echo', 'x' * 16384])
+ self.assertEqual(rc, 0)
+ self.assertTrue(out == ('x' * 16384 + '\n'))
+
def test_shell_nocheck_failure(self):
rc, out, _ = self.device.shell_nocheck(['false'])
self.assertNotEqual(rc, 0)
@@ -1130,8 +1137,18 @@
if host_dir is not None:
shutil.rmtree(host_dir)
+ def verify_sync(self, device, temp_files, device_dir):
+ """Verifies that a list of temp files was synced to the device."""
+ # Confirm that every file on the device mirrors that on the host.
+ for temp_file in temp_files:
+ device_full_path = posixpath.join(
+ device_dir, temp_file.base_name)
+ dev_md5, _ = device.shell(
+ [get_md5_prog(self.device), device_full_path])[0].split()
+ self.assertEqual(temp_file.checksum, dev_md5)
+
def test_sync(self):
- """Sync a randomly generated directory of files to specified device."""
+ """Sync a host directory to the data partition."""
try:
base_dir = tempfile.mkdtemp()
@@ -1141,27 +1158,50 @@
os.makedirs(full_dir_path)
# Create 32 random files within the host mirror.
- temp_files = make_random_host_files(in_dir=full_dir_path, num_files=32)
+ temp_files = make_random_host_files(
+ in_dir=full_dir_path, num_files=32)
- # Clean up any trash on the device.
- device = adb.get_device(product=base_dir)
+ # Clean up any stale files on the device.
+ device = adb.get_device() # pylint: disable=no-member
device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ old_product_out = os.environ.get('ANDROID_PRODUCT_OUT')
+ os.environ['ANDROID_PRODUCT_OUT'] = base_dir
device.sync('data')
+ if old_product_out is None:
+ del os.environ['ANDROID_PRODUCT_OUT']
+ else:
+ os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
- # Confirm that every file on the device mirrors that on the host.
- for temp_file in temp_files:
- device_full_path = posixpath.join(self.DEVICE_TEMP_DIR,
- temp_file.base_name)
- dev_md5, _ = device.shell(
- [get_md5_prog(self.device), device_full_path])[0].split()
- self.assertEqual(temp_file.checksum, dev_md5)
+ self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ #self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
finally:
if base_dir is not None:
shutil.rmtree(base_dir)
+ def test_push_sync(self):
+ """Sync a host directory to a specific path."""
+
+ try:
+ temp_dir = tempfile.mkdtemp()
+ temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)
+
+ device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')
+
+ # Clean up any stale files on the device.
+ device = adb.get_device() # pylint: disable=no-member
+ device.shell(['rm', '-rf', device_dir])
+
+ device.push(temp_dir, device_dir, sync=True)
+
+ self.verify_sync(device, temp_files, device_dir)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if temp_dir is not None:
+ shutil.rmtree(temp_dir)
+
def test_unicode_paths(self):
"""Ensure that we can support non-ASCII paths, even on Windows."""
name = u'로보카 폴리'
@@ -1188,6 +1228,97 @@
self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+class DeviceOfflineTest(DeviceTest):
+ def _get_device_state(self, serialno):
+ output = subprocess.check_output(self.device.adb_cmd + ['devices'])
+ for line in output.split('\n'):
+ m = re.match('(\S+)\s+(\S+)', line)
+ if m and m.group(1) == serialno:
+ return m.group(2)
+ return None
+
+ def test_killed_when_pushing_a_large_file(self):
+ """
+ While running adb push with a large file, kill adb server.
+ Occasionally the device becomes offline. Because the device is still
+ reading data without realizing that the adb server has been restarted.
+ Test if we can bring the device online automatically now.
+ http://b/32952319
+ """
+ serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
+ # 1. Push a large file
+ file_path = 'tmp_large_file'
+ try:
+ fh = open(file_path, 'w')
+ fh.write('\0' * (100 * 1024 * 1024))
+ fh.close()
+ subproc = subprocess.Popen(self.device.adb_cmd + ['push', file_path, '/data/local/tmp'])
+ time.sleep(0.1)
+ # 2. Kill the adb server
+ subprocess.check_call(self.device.adb_cmd + ['kill-server'])
+ subproc.terminate()
+ finally:
+ try:
+ os.unlink(file_path)
+ except:
+ pass
+ # 3. See if the device still exist.
+ # Sleep to wait for the adb server exit.
+ time.sleep(0.5)
+ # 4. The device should be online
+ self.assertEqual(self._get_device_state(serialno), 'device')
+
+ def test_killed_when_pulling_a_large_file(self):
+ """
+ While running adb pull with a large file, kill adb server.
+ Occasionally the device can't be connected. Because the device is trying to
+ send a message larger than what is expected by the adb server.
+ Test if we can bring the device online automatically now.
+ """
+ serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
+ file_path = 'tmp_large_file'
+ try:
+ # 1. Create a large file on device.
+ self.device.shell(['dd', 'if=/dev/zero', 'of=/data/local/tmp/tmp_large_file',
+ 'bs=1000000', 'count=100'])
+ # 2. Pull the large file on host.
+ subproc = subprocess.Popen(self.device.adb_cmd +
+ ['pull','/data/local/tmp/tmp_large_file', file_path])
+ time.sleep(0.1)
+ # 3. Kill the adb server
+ subprocess.check_call(self.device.adb_cmd + ['kill-server'])
+ subproc.terminate()
+ finally:
+ try:
+ os.unlink(file_path)
+ except:
+ pass
+ # 4. See if the device still exist.
+ # Sleep to wait for the adb server exit.
+ time.sleep(0.5)
+ self.assertEqual(self._get_device_state(serialno), 'device')
+
+
+ def test_packet_size_regression(self):
+ """Test for http://b/37783561
+
+ Receiving packets of a length divisible by 512 but not 1024 resulted in
+ the adb client waiting indefinitely for more input.
+ """
+ # The values that trigger things are 507 (512 - 5 bytes from shell protocol) + 1024*n
+ # Probe some surrounding values as well, for the hell of it.
+ for length in [506, 507, 508, 1018, 1019, 1020, 1530, 1531, 1532]:
+ cmd = ['dd', 'if=/dev/zero', 'bs={}'.format(length), 'count=1', '2>/dev/null;'
+ 'echo', 'foo']
+ rc, stdout, _ = self.device.shell_nocheck(cmd)
+
+ self.assertEqual(0, rc)
+
+ # Output should be '\0' * length, followed by "foo\n"
+ self.assertEqual(length, len(stdout) - 4)
+ self.assertEqual(stdout, "\0" * length + "foo\n")
+
+
def main():
random.seed(0)
if len(adb.get_devices()) > 0:
diff --git a/adb/transport.cpp b/adb/transport.cpp
index c951f5b..1b597fd 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -21,6 +21,7 @@
#include <ctype.h>
#include <errno.h>
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -29,29 +30,40 @@
#include <algorithm>
#include <list>
#include <mutex>
+#include <thread>
#include <android-base/logging.h>
#include <android-base/parsenetaddress.h>
+#include <android-base/quick_exit.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
#include "adb.h"
#include "adb_auth.h"
#include "adb_trace.h"
#include "adb_utils.h"
#include "diagnose_usb.h"
+#include "fdevent.h"
static void transport_unref(atransport *t);
+// TODO: unordered_map<TransportId, atransport*>
static auto& transport_list = *new std::list<atransport*>();
static auto& pending_list = *new std::list<atransport*>();
-static std::mutex& transport_lock = *new std::mutex();
+static auto& transport_lock = *new std::recursive_mutex();
const char* const kFeatureShell2 = "shell_v2";
const char* const kFeatureCmd = "cmd";
const char* const kFeatureStat2 = "stat_v2";
const char* const kFeatureLibusb = "libusb";
+const char* const kFeaturePushSync = "push_sync";
+
+TransportId NextTransportId() {
+ static std::atomic<TransportId> next(1);
+ return next++;
+}
static std::string dump_packet(const char* name, const char* func, apacket* p) {
unsigned command = p->msg.command;
@@ -208,6 +220,11 @@
put_apacket(p);
break;
}
+#if ADB_HOST
+ if (p->msg.command == 0) {
+ continue;
+ }
+#endif
}
D("%s: received remote packet, sending to transport", t->serial);
@@ -270,7 +287,11 @@
if (active) {
D("%s: transport got packet, sending to remote", t->serial);
ATRACE_NAME("write_transport write_remote");
- t->write_to_remote(p, t);
+ if (t->Write(p) != 0) {
+ D("%s: remote write failed for transport", t->serial);
+ put_apacket(p);
+ break;
+ }
} else {
D("%s: transport ignoring packet while offline", t->serial);
}
@@ -285,9 +306,11 @@
}
void kick_transport(atransport* t) {
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
// As kick_transport() can be called from threads without guarantee that t is valid,
// check if the transport is in transport_list first.
+ //
+ // TODO(jmgao): WTF? Is this actually true?
if (std::find(transport_list.begin(), transport_list.end(), t) != transport_list.end()) {
t->Kick();
}
@@ -306,7 +329,8 @@
*/
struct device_tracker {
asocket socket;
- int update_needed;
+ bool update_needed;
+ bool long_output;
device_tracker* next;
};
@@ -317,7 +341,7 @@
device_tracker** pnode = &device_tracker_list;
device_tracker* node = *pnode;
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
while (node) {
if (node == tracker) {
*pnode = node->next;
@@ -363,15 +387,15 @@
// We want to send the device list when the tracker connects
// for the first time, even if no update occurred.
- if (tracker->update_needed > 0) {
- tracker->update_needed = 0;
+ if (tracker->update_needed) {
+ tracker->update_needed = false;
- std::string transports = list_transports(false);
+ std::string transports = list_transports(tracker->long_output);
device_tracker_send(tracker, transports);
}
}
-asocket* create_device_tracker(void) {
+asocket* create_device_tracker(bool long_output) {
device_tracker* tracker = reinterpret_cast<device_tracker*>(calloc(1, sizeof(*tracker)));
if (tracker == nullptr) fatal("cannot allocate device tracker");
@@ -380,7 +404,8 @@
tracker->socket.enqueue = device_tracker_enqueue;
tracker->socket.ready = device_tracker_ready;
tracker->socket.close = device_tracker_close;
- tracker->update_needed = 1;
+ tracker->update_needed = true;
+ tracker->long_output = long_output;
tracker->next = device_tracker_list;
device_tracker_list = tracker;
@@ -388,8 +413,27 @@
return &tracker->socket;
}
+// Check if all of the USB transports are connected.
+bool iterate_transports(std::function<bool(const atransport*)> fn) {
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
+ for (const auto& t : transport_list) {
+ if (!fn(t)) {
+ return false;
+ }
+ }
+ for (const auto& t : pending_list) {
+ if (!fn(t)) {
+ return false;
+ }
+ }
+ return true;
+}
+
// Call this function each time the transport list has changed.
void update_transports() {
+ update_transport_status();
+
+ // Notify `adb track-devices` clients.
std::string transports = list_transports(false);
device_tracker* tracker = device_tracker_list;
@@ -475,7 +519,7 @@
adb_close(t->fd);
{
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
transport_list.remove(t);
}
@@ -492,7 +536,7 @@
}
/* don't create transport threads for inaccessible devices */
- if (t->connection_state != kCsNoPerm) {
+ if (t->GetConnectionState() != kCsNoPerm) {
/* initial references are the two threads */
t->ref_count = 2;
@@ -509,17 +553,12 @@
fdevent_set(&(t->transport_fde), FDE_READ);
- if (!adb_thread_create(write_transport_thread, t)) {
- fatal_errno("cannot create write_transport thread");
- }
-
- if (!adb_thread_create(read_transport_thread, t)) {
- fatal_errno("cannot create read_transport thread");
- }
+ std::thread(write_transport_thread, t).detach();
+ std::thread(read_transport_thread, t).detach();
}
{
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
pending_list.remove(t);
transport_list.push_front(t);
}
@@ -544,6 +583,14 @@
fdevent_set(&transport_registration_fde, FDE_READ);
}
+void kick_all_transports() {
+ // To avoid only writing part of a packet to a transport after exit, kick all transports.
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
+ for (auto t : transport_list) {
+ t->Kick();
+ }
+}
+
/* the fdevent select pump is single threaded */
static void register_transport(atransport* transport) {
tmsg m;
@@ -568,7 +615,7 @@
static void transport_unref(atransport* t) {
CHECK(t != nullptr);
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
CHECK_GT(t->ref_count, 0u);
t->ref_count--;
if (t->ref_count == 0) {
@@ -603,11 +650,15 @@
return !*to_test;
}
-atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
- std::string* error_out) {
+atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
+ bool* is_ambiguous, std::string* error_out,
+ bool accept_any_state) {
atransport* result = nullptr;
- if (serial) {
+ if (transport_id != 0) {
+ *error_out =
+ android::base::StringPrintf("no device with transport id '%" PRIu64 "'", transport_id);
+ } else if (serial) {
*error_out = android::base::StringPrintf("device '%s' not found", serial);
} else if (type == kTransportLocal) {
*error_out = "no emulators found";
@@ -617,17 +668,21 @@
*error_out = "no devices found";
}
- std::unique_lock<std::mutex> lock(transport_lock);
+ std::unique_lock<std::recursive_mutex> lock(transport_lock);
for (const auto& t : transport_list) {
- if (t->connection_state == kCsNoPerm) {
+ if (t->GetConnectionState() == kCsNoPerm) {
#if ADB_HOST
*error_out = UsbNoPermissionsLongHelpText();
#endif
continue;
}
- // Check for matching serial number.
- if (serial) {
+ if (transport_id) {
+ if (t->id == transport_id) {
+ result = t;
+ break;
+ }
+ } else if (serial) {
if (t->MatchesTarget(serial)) {
if (result) {
*error_out = "more than one device";
@@ -668,7 +723,7 @@
lock.unlock();
// Don't return unauthorized devices; the caller can't do anything with them.
- if (result && result->connection_state == kCsUnauthorized) {
+ if (result && result->GetConnectionState() == kCsUnauthorized && !accept_any_state) {
*error_out = "device unauthorized.\n";
char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
*error_out += "This adb server's $ADB_VENDOR_KEYS is ";
@@ -680,7 +735,7 @@
}
// Don't return offline devices; the caller can't do anything with them.
- if (result && result->connection_state == kCsOffline) {
+ if (result && result->GetConnectionState() == kCsOffline && !accept_any_state) {
*error_out = "device offline";
result = nullptr;
}
@@ -692,16 +747,38 @@
return result;
}
+int atransport::Write(apacket* p) {
+#if ADB_HOST
+ std::lock_guard<std::mutex> lock(write_msg_lock_);
+#endif
+ return write_func_(p, this);
+}
+
void atransport::Kick() {
if (!kicked_) {
kicked_ = true;
CHECK(kick_func_ != nullptr);
+#if ADB_HOST
+ // On host, adb server should avoid writing part of a packet, so don't
+ // kick a transport whiling writing a packet.
+ std::lock_guard<std::mutex> lock(write_msg_lock_);
+#endif
kick_func_(this);
}
}
+ConnectionState atransport::GetConnectionState() const {
+ return connection_state_;
+}
+
+void atransport::SetConnectionState(ConnectionState state) {
+ check_main_thread();
+ connection_state_ = state;
+}
+
const std::string atransport::connection_state_name() const {
- switch (connection_state) {
+ ConnectionState state = GetConnectionState();
+ switch (state) {
case kCsOffline:
return "offline";
case kCsBootloader:
@@ -832,18 +909,23 @@
#if ADB_HOST
+// We use newline as our delimiter, make sure to never output it.
+static std::string sanitize(std::string str, bool alphanumeric) {
+ auto pred = alphanumeric ? [](const char c) { return !isalnum(c); }
+ : [](const char c) { return c == '\n'; };
+ std::replace_if(str.begin(), str.end(), pred, '_');
+ return str;
+}
+
static void append_transport_info(std::string* result, const char* key, const char* value,
- bool sanitize) {
+ bool alphanumeric) {
if (value == nullptr || *value == '\0') {
return;
}
*result += ' ';
*result += key;
-
- for (const char* p = value; *p; ++p) {
- result->push_back((!sanitize || isalnum(*p)) ? *p : '_');
- }
+ *result += sanitize(value, alphanumeric);
}
static void append_transport(const atransport* t, std::string* result, bool long_listing) {
@@ -863,6 +945,11 @@
append_transport_info(result, "product:", t->product, false);
append_transport_info(result, "model:", t->model, true);
append_transport_info(result, "device:", t->device, false);
+
+ // Put id at the end, so that anyone parsing the output here can always find it by scanning
+ // backwards from newlines, even with hypothetical devices named 'transport_id:1'.
+ *result += " transport_id:";
+ *result += std::to_string(t->id);
}
*result += '\n';
}
@@ -870,7 +957,7 @@
std::string list_transports(bool long_listing) {
std::string result;
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (const auto& t : transport_list) {
append_transport(t, &result, long_listing);
}
@@ -878,7 +965,7 @@
}
void close_usb_devices(std::function<bool(const atransport*)> predicate) {
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (auto& t : transport_list) {
if (predicate(t)) {
t->Kick();
@@ -907,7 +994,7 @@
return -1;
}
- std::unique_lock<std::mutex> lock(transport_lock);
+ std::unique_lock<std::recursive_mutex> lock(transport_lock);
for (const auto& transport : pending_list) {
if (transport->serial && strcmp(serial, transport->serial) == 0) {
VLOG(TRANSPORT) << "socket transport " << transport->serial
@@ -939,7 +1026,7 @@
atransport* find_transport(const char* serial) {
atransport* result = nullptr;
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (auto& t : transport_list) {
if (t->serial && strcmp(serial, t->serial) == 0) {
result = t;
@@ -951,7 +1038,7 @@
}
void kick_all_tcp_devices() {
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (auto& t : transport_list) {
if (t->IsTcpDevice()) {
// Kicking breaks the read_transport thread of this transport out of any read, then
@@ -967,10 +1054,10 @@
void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
unsigned writeable) {
- atransport* t = new atransport();
+ atransport* t = new atransport((writeable ? kCsOffline : kCsNoPerm));
D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
- init_usb_transport(t, usb, (writeable ? kCsOffline : kCsNoPerm));
+ init_usb_transport(t, usb);
if (serial) {
t->serial = strdup(serial);
}
@@ -980,7 +1067,7 @@
}
{
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
pending_list.push_front(t);
}
@@ -989,31 +1076,29 @@
// This should only be used for transports with connection_state == kCsNoPerm.
void unregister_usb_transport(usb_handle* usb) {
- std::lock_guard<std::mutex> lock(transport_lock);
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
transport_list.remove_if(
- [usb](atransport* t) { return t->usb == usb && t->connection_state == kCsNoPerm; });
+ [usb](atransport* t) { return t->usb == usb && t->GetConnectionState() == kCsNoPerm; });
}
-int check_header(apacket* p, atransport* t) {
+bool check_header(apacket* p, atransport* t) {
if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
- VLOG(RWX) << "check_header(): invalid magic";
- return -1;
+ VLOG(RWX) << "check_header(): invalid magic command = " << std::hex << p->msg.command
+ << ", magic = " << p->msg.magic;
+ return false;
}
if (p->msg.data_length > t->get_max_payload()) {
VLOG(RWX) << "check_header(): " << p->msg.data_length
<< " atransport::max_payload = " << t->get_max_payload();
- return -1;
+ return false;
}
- return 0;
+ return true;
}
-int check_data(apacket* p) {
- if (calculate_apacket_checksum(p) != p->msg.data_check) {
- return -1;
- }
- return 0;
+bool check_data(apacket* p) {
+ return calculate_apacket_checksum(p) == p->msg.data_check;
}
#if ADB_HOST
@@ -1024,4 +1109,11 @@
keys_.pop_front();
return result;
}
+bool atransport::SetSendConnectOnError() {
+ if (has_send_connect_on_error_) {
+ return false;
+ }
+ has_send_connect_on_error_ = true;
+ return true;
+}
#endif
diff --git a/adb/transport.h b/adb/transport.h
index 4d97fc7..374bfc3 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -19,10 +19,12 @@
#include <sys/types.h>
+#include <atomic>
#include <deque>
#include <functional>
#include <list>
#include <memory>
+#include <mutex>
#include <string>
#include <unordered_set>
@@ -49,39 +51,45 @@
extern const char* const kFeatureStat2;
// The server is running with libusb enabled.
extern const char* const kFeatureLibusb;
+// The server supports `push --sync`.
+extern const char* const kFeaturePushSync;
+
+TransportId NextTransportId();
class atransport {
-public:
+ public:
// TODO(danalbert): We expose waaaaaaay too much stuff because this was
// historically just a struct, but making the whole thing a more idiomatic
// class in one go is a very large change. Given how bad our testing is,
// it's better to do this piece by piece.
- atransport() {
+ atransport(ConnectionState state = kCsOffline)
+ : id(NextTransportId()), connection_state_(state) {
transport_fde = {};
protocol_version = A_VERSION;
max_payload = MAX_PAYLOAD;
}
-
virtual ~atransport() {}
int (*read_from_remote)(apacket* p, atransport* t) = nullptr;
- int (*write_to_remote)(apacket* p, atransport* t) = nullptr;
void (*close)(atransport* t) = nullptr;
- void SetKickFunction(void (*kick_func)(atransport*)) {
- kick_func_ = kick_func;
- }
- bool IsKicked() {
- return kicked_;
- }
+
+ void SetWriteFunction(int (*write_func)(apacket*, atransport*)) { write_func_ = write_func; }
+ void SetKickFunction(void (*kick_func)(atransport*)) { kick_func_ = kick_func; }
+ bool IsKicked() { return kicked_; }
+ int Write(apacket* p);
void Kick();
+ // ConnectionState can be read by all threads, but can only be written in the main thread.
+ ConnectionState GetConnectionState() const;
+ void SetConnectionState(ConnectionState state);
+
+ const TransportId id;
int fd = -1;
int transport_socket = -1;
fdevent transport_fde;
size_t ref_count = 0;
uint32_t sync_token = 0;
- ConnectionState connection_state = kCsOffline;
bool online = false;
TransportType type = kTransportAny;
@@ -114,11 +122,13 @@
#if ADB_HOST
std::shared_ptr<RSA> NextKey();
+ bool SetSendConnectOnError();
#endif
char token[TOKEN_SIZE] = {};
size_t failed_auth_attempts = 0;
+ const std::string serial_name() const { return serial ? serial : "<unknown>"; }
const std::string connection_state_name() const;
void update_version(int version, size_t payload);
@@ -157,6 +167,7 @@
int local_port_for_emulator_ = -1;
bool kicked_ = false;
void (*kick_func_)(atransport*) = nullptr;
+ int (*write_func_)(apacket*, atransport*) = nullptr;
// A set of features transmitted in the banner with the initial connection.
// This is stored in the banner as 'features=feature0,feature1,etc'.
@@ -167,8 +178,11 @@
// A list of adisconnect callbacks called when the transport is kicked.
std::list<adisconnect*> disconnects_;
+ std::atomic<ConnectionState> connection_state_;
#if ADB_HOST
std::deque<std::shared_ptr<RSA>> keys_;
+ std::mutex write_msg_lock_;
+ bool has_send_connect_on_error_ = false;
#endif
DISALLOW_COPY_AND_ASSIGN(atransport);
@@ -177,20 +191,27 @@
/*
* Obtain a transport from the available transports.
* If serial is non-null then only the device with that serial will be chosen.
+ * If transport_id is non-zero then only the device with that transport ID will be chosen.
* If multiple devices/emulators would match, *is_ambiguous (if non-null)
* is set to true and nullptr returned.
* If no suitable transport is found, error is set and nullptr returned.
*/
-atransport* acquire_one_transport(TransportType type, const char* serial,
- bool* is_ambiguous, std::string* error_out);
+atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
+ bool* is_ambiguous, std::string* error_out,
+ bool accept_any_state = false);
void kick_transport(atransport* t);
void update_transports(void);
+// Iterates across all of the current and pending transports.
+// Stops iteration and returns false if fn returns false, otherwise returns true.
+bool iterate_transports(std::function<bool(const atransport*)> fn);
+
void init_transport_registration(void);
void init_mdns_transport_discovery(void);
std::string list_transports(bool long_listing);
atransport* find_transport(const char* serial);
void kick_all_tcp_devices();
+void kick_all_transports();
void register_usb_transport(usb_handle* h, const char* serial,
const char* devpath, unsigned writeable);
@@ -204,14 +225,14 @@
// This should only be used for transports with connection_state == kCsNoPerm.
void unregister_usb_transport(usb_handle* usb);
-int check_header(apacket* p, atransport* t);
-int check_data(apacket* p);
+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);
void send_packet(apacket* p, atransport* t);
-asocket* create_device_tracker(void);
+asocket* create_device_tracker(bool long_output);
#endif /* __TRANSPORT_H */
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 4198a52..6cedd92 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -62,22 +62,22 @@
static int remote_read(apacket *p, atransport *t)
{
- if(!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))){
+ if (!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))) {
D("remote local: read terminated (message)");
return -1;
}
- if(check_header(p, t)) {
+ if (!check_header(p, t)) {
D("bad header: terminated (data)");
return -1;
}
- if(!ReadFdExactly(t->sfd, p->data, p->msg.data_length)){
+ if (!ReadFdExactly(t->sfd, p->data, p->msg.data_length)) {
D("remote local: terminated (data)");
return -1;
}
- if(check_data(p)) {
+ if (!check_data(p)) {
D("bad data: terminated (data)");
return -1;
}
@@ -199,7 +199,7 @@
std::mutex &retry_ports_lock = *new std::mutex;
std::condition_variable &retry_ports_cond = *new std::condition_variable;
-static void client_socket_thread(void* x) {
+static void client_socket_thread(int) {
adb_thread_setname("client_socket_thread");
D("transport: client_socket_thread() starting");
PollAllLocalPortsForEmulator();
@@ -244,9 +244,8 @@
#else // ADB_HOST
-static void server_socket_thread(void* arg) {
+static void server_socket_thread(int port) {
int serverfd, fd;
- int port = (int) (uintptr_t) arg;
adb_thread_setname("server socket");
D("transport: server_socket_thread() starting");
@@ -325,7 +324,7 @@
* the transport registration is completed. That's why we need to send the
* 'start' request after the transport is registered.
*/
-static void qemu_socket_thread(void* arg) {
+static void qemu_socket_thread(int port) {
/* 'accept' request to the adb QEMUD service. */
static const char _accept_req[] = "accept";
/* 'start' request to the adb QEMUD service. */
@@ -333,7 +332,6 @@
/* 'ok' reply from the adb QEMUD service. */
static const char _ok_resp[] = "ok";
- const int port = (int) (uintptr_t) arg;
int fd;
char tmp[256];
char con_name[32];
@@ -350,7 +348,7 @@
/* This could be an older version of the emulator, that doesn't
* implement adb QEMUD service. Fall back to the old TCP way. */
D("adb service is not available. Falling back to TCP socket.");
- adb_thread_create(server_socket_thread, arg);
+ std::thread(server_socket_thread, port).detach();
return;
}
@@ -390,11 +388,30 @@
D("transport: qemu_socket_thread() exiting");
return;
}
+
+// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka
+// goldfish) as the transport. This can either be explicitly set by the
+// service.adb.transport property, or be inferred from ro.kernel.qemu that is
+// set to "1" for ranchu/goldfish.
+static bool use_qemu_goldfish() {
+ // Legacy way to detect if adbd should use the goldfish pipe is to check for
+ // ro.kernel.qemu, keep that behaviour for backward compatibility.
+ if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+ return true;
+ }
+ // If service.adb.transport is present and is set to "goldfish", use the
+ // QEMUD pipe.
+ if (android::base::GetProperty("service.adb.transport", "") == "goldfish") {
+ return true;
+ }
+ return false;
+}
+
#endif // !ADB_HOST
void local_init(int port)
{
- adb_thread_func_t func;
+ void (*func)(int);
const char* debug_name = "";
#if ADB_HOST
@@ -403,20 +420,12 @@
#else
// For the adbd daemon in the system image we need to distinguish
// between the device, and the emulator.
- if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
- // Running inside the emulator: use QEMUD pipe as the transport.
- func = qemu_socket_thread;
- } else {
- // Running inside the device: use TCP socket as the transport.
- func = server_socket_thread;
- }
+ func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread;
debug_name = "server";
#endif // !ADB_HOST
D("transport: local %s init", debug_name);
- if (!adb_thread_create(func, (void *) (uintptr_t) port)) {
- fatal_errno("cannot create local socket %s thread", debug_name);
- }
+ std::thread(func, port).detach();
}
static void remote_kick(atransport *t)
@@ -519,12 +528,11 @@
int fail = 0;
t->SetKickFunction(remote_kick);
+ t->SetWriteFunction(remote_write);
t->close = remote_close;
t->read_from_remote = remote_read;
- t->write_to_remote = remote_write;
t->sfd = s;
t->sync_token = 1;
- t->connection_state = kCsOffline;
t->type = kTransportLocal;
#if ADB_HOST
diff --git a/adb/transport_mdns.cpp b/adb/transport_mdns.cpp
index e49b1c6..3603f09 100644
--- a/adb/transport_mdns.cpp
+++ b/adb/transport_mdns.cpp
@@ -24,6 +24,8 @@
#include <arpa/inet.h>
#endif
+#include <thread>
+
#include <android-base/stringprintf.h>
#include <dns_sd.h>
@@ -262,19 +264,22 @@
}
}
-void init_mdns_transport_discovery(void) {
- DNSServiceErrorType errorCode =
- DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
- register_mdns_transport, nullptr);
+void init_mdns_transport_discovery_thread(void) {
+ DNSServiceErrorType errorCode = DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
+ register_mdns_transport, nullptr);
if (errorCode != kDNSServiceErr_NoError) {
D("Got %d initiating mDNS browse.", errorCode);
return;
}
- fdevent_install(&service_ref_fde,
- adb_DNSServiceRefSockFD(service_ref),
- pump_service_ref,
- &service_ref);
- fdevent_set(&service_ref_fde, FDE_READ);
+ fdevent_run_on_main_thread([]() {
+ fdevent_install(&service_ref_fde, adb_DNSServiceRefSockFD(service_ref), pump_service_ref,
+ &service_ref);
+ fdevent_set(&service_ref_fde, FDE_READ);
+ });
+}
+
+void init_mdns_transport_discovery(void) {
+ std::thread(init_mdns_transport_discovery_thread).detach();
}
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 8b38e03..68689d4 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -94,12 +94,13 @@
}
TEST(transport, parse_banner_no_features) {
+ set_main_thread();
atransport t;
parse_banner("host::", &t);
ASSERT_EQ(0U, t.features().size());
- ASSERT_EQ(kCsHost, t.connection_state);
+ ASSERT_EQ(kCsHost, t.GetConnectionState());
ASSERT_EQ(nullptr, t.product);
ASSERT_EQ(nullptr, t.model);
@@ -113,7 +114,7 @@
"host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;";
parse_banner(banner, &t);
- ASSERT_EQ(kCsHost, t.connection_state);
+ ASSERT_EQ(kCsHost, t.GetConnectionState());
ASSERT_EQ(0U, t.features().size());
@@ -130,7 +131,7 @@
"features=woodly,doodly";
parse_banner(banner, &t);
- ASSERT_EQ(kCsHost, t.connection_state);
+ ASSERT_EQ(kCsHost, t.GetConnectionState());
ASSERT_EQ(2U, t.features().size());
ASSERT_TRUE(t.has_feature("woodly"));
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 516b4f2..6768d31 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -25,44 +25,137 @@
#include "adb.h"
-static int remote_read(apacket *p, atransport *t)
-{
- if(usb_read(t->usb, &p->msg, sizeof(amessage))){
+#if ADB_HOST
+
+// Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
+// to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
+static int UsbReadMessage(usb_handle* h, amessage* msg) {
+ D("UsbReadMessage");
+
+ size_t usb_packet_size = usb_get_max_packet_size(h);
+ CHECK_GE(usb_packet_size, sizeof(*msg));
+ CHECK_LT(usb_packet_size, 4096ULL);
+
+ char buffer[4096];
+ int n = usb_read(h, buffer, usb_packet_size);
+ if (n != sizeof(*msg)) {
+ D("usb_read returned unexpected length %d (expected %zu)", n, sizeof(*msg));
+ return -1;
+ }
+ memcpy(msg, buffer, sizeof(*msg));
+ return n;
+}
+
+// Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
+// to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
+static int UsbReadPayload(usb_handle* h, apacket* p) {
+ D("UsbReadPayload(%d)", p->msg.data_length);
+
+ size_t usb_packet_size = usb_get_max_packet_size(h);
+ CHECK_EQ(0ULL, sizeof(p->data) % usb_packet_size);
+
+ // Round the data length up to the nearest packet size boundary.
+ // The device won't send a zero packet for packet size aligned payloads,
+ // so don't read any more packets than needed.
+ size_t len = p->msg.data_length;
+ size_t rem_size = len % usb_packet_size;
+ if (rem_size) {
+ len += usb_packet_size - rem_size;
+ }
+ CHECK_LE(len, sizeof(p->data));
+ return usb_read(h, &p->data, len);
+}
+
+static int remote_read(apacket* p, atransport* t) {
+ int n = UsbReadMessage(t->usb, &p->msg);
+ if (n < 0) {
D("remote usb: read terminated (message)");
return -1;
}
+ if (static_cast<size_t>(n) != sizeof(p->msg) || !check_header(p, t)) {
+ D("remote usb: check_header failed, skip it");
+ goto err_msg;
+ }
+ if (t->GetConnectionState() == kCsOffline) {
+ // If we read a wrong msg header declaring a large message payload, don't read its payload.
+ // Otherwise we may miss true messages from the device.
+ if (p->msg.command != A_CNXN && p->msg.command != A_AUTH) {
+ goto err_msg;
+ }
+ }
+ if (p->msg.data_length) {
+ n = UsbReadPayload(t->usb, p);
+ if (n < 0) {
+ D("remote usb: terminated (data)");
+ return -1;
+ }
+ if (static_cast<uint32_t>(n) != p->msg.data_length) {
+ D("remote usb: read payload failed (need %u bytes, give %d bytes), skip it",
+ p->msg.data_length, n);
+ goto err_msg;
+ }
+ }
+ if (!check_data(p)) {
+ D("remote usb: check_data failed, skip it");
+ goto err_msg;
+ }
+ return 0;
- if(check_header(p, t)) {
- D("remote usb: check_header failed");
+err_msg:
+ p->msg.command = 0;
+ if (t->GetConnectionState() == kCsOffline) {
+ // If the data toggle of ep_out on device and ep_in on host are not the same, we may receive
+ // an error message. In this case, resend one A_CNXN message to connect the device.
+ if (t->SetSendConnectOnError()) {
+ SendConnectOnHost(t);
+ }
+ }
+ return 0;
+}
+
+#else
+
+// On Android devices, we rely on the kernel to provide buffered read.
+// So we can recover automatically from EOVERFLOW.
+static int remote_read(apacket *p, atransport *t)
+{
+ if (usb_read(t->usb, &p->msg, sizeof(amessage))) {
+ PLOG(ERROR) << "remote usb: read terminated (message)";
return -1;
}
- if(p->msg.data_length) {
- if(usb_read(t->usb, p->data, p->msg.data_length)){
- D("remote usb: terminated (data)");
+ if (!check_header(p, t)) {
+ LOG(ERROR) << "remote usb: check_header failed";
+ return -1;
+ }
+
+ if (p->msg.data_length) {
+ if (usb_read(t->usb, p->data, p->msg.data_length)) {
+ PLOG(ERROR) << "remote usb: terminated (data)";
return -1;
}
}
- if(check_data(p)) {
- D("remote usb: check_data failed");
+ if (!check_data(p)) {
+ LOG(ERROR) << "remote usb: check_data failed";
return -1;
}
return 0;
}
+#endif
static int remote_write(apacket *p, atransport *t)
{
unsigned size = p->msg.data_length;
- if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
- D("remote usb: 1 - write terminated");
+ if (usb_write(t->usb, &p->msg, sizeof(amessage))) {
+ PLOG(ERROR) << "remote usb: 1 - write terminated";
return -1;
}
- if(p->msg.data_length == 0) return 0;
- if(usb_write(t->usb, &p->data, size)) {
- D("remote usb: 2 - write terminated");
+ if (p->msg.data_length == 0) return 0;
+ if (usb_write(t->usb, &p->data, size)) {
+ PLOG(ERROR) << "remote usb: 2 - write terminated";
return -1;
}
@@ -75,20 +168,17 @@
t->usb = 0;
}
-static void remote_kick(atransport *t)
-{
+static void remote_kick(atransport* t) {
usb_kick(t->usb);
}
-void init_usb_transport(atransport *t, usb_handle *h, ConnectionState state)
-{
+void init_usb_transport(atransport* t, usb_handle* h) {
D("transport: usb");
t->close = remote_close;
t->SetKickFunction(remote_kick);
+ t->SetWriteFunction(remote_write);
t->read_from_remote = remote_read;
- t->write_to_remote = remote_write;
t->sync_token = 1;
- t->connection_state = state;
t->type = kTransportUsb;
t->usb = h;
}
@@ -102,7 +192,7 @@
#if defined(_WIN32) || !ADB_HOST
return false;
#else
- static bool disable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "0") == 0;
- return !disable;
+ static bool enable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "1") == 0;
+ return enable;
#endif
}
diff --git a/adb/usb.h b/adb/usb.h
index ba70de4..f428ede 100644
--- a/adb/usb.h
+++ b/adb/usb.h
@@ -16,14 +16,18 @@
#pragma once
+#include <sys/types.h>
+
// USB host/client interface.
#define ADB_USB_INTERFACE(handle_ref_type) \
void usb_init(); \
+ void usb_cleanup(); \
int usb_write(handle_ref_type h, const void* data, int len); \
int usb_read(handle_ref_type h, void* data, int len); \
int usb_close(handle_ref_type h); \
- void usb_kick(handle_ref_type h)
+ void usb_kick(handle_ref_type h); \
+ size_t usb_get_max_packet_size(handle_ref_type)
#if defined(_WIN32) || !ADB_HOST
// Windows and the daemon have a single implementation.
diff --git a/base/Android.bp b/base/Android.bp
index a8dda5d..82aee2a 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -26,9 +26,6 @@
host_supported: true,
export_include_dirs: ["include"],
- header_libs: ["libutils_headers"],
- export_header_lib_headers: ["libutils_headers"],
-
target: {
linux_bionic: {
enabled: true,
@@ -42,8 +39,11 @@
cc_library {
name: "libbase",
vendor_available: true,
- clang: true,
host_supported: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
srcs: [
"file.cpp",
"logging.cpp",
@@ -54,7 +54,9 @@
"test_utils.cpp",
],
- header_libs: ["libbase_headers"],
+ header_libs: [
+ "libbase_headers",
+ ],
export_header_lib_headers: ["libbase_headers"],
cppflags: libbase_cppflags,
@@ -64,24 +66,36 @@
srcs: [
"errors_unix.cpp",
"properties.cpp",
+ "chrono_utils.cpp",
],
cppflags: ["-Wexit-time-destructors"],
sanitize: {
misc_undefined: ["integer"],
},
+
},
darwin: {
- srcs: ["errors_unix.cpp"],
+ srcs: [
+ "chrono_utils.cpp",
+ "errors_unix.cpp",
+ ],
cppflags: ["-Wexit-time-destructors"],
},
linux_bionic: {
- srcs: ["errors_unix.cpp"],
+ srcs: [
+ "chrono_utils.cpp",
+ "errors_unix.cpp",
+ ],
cppflags: ["-Wexit-time-destructors"],
enabled: true,
},
linux: {
- srcs: ["errors_unix.cpp"],
+ srcs: [
+ "chrono_utils.cpp",
+ "errors_unix.cpp",
+ ],
cppflags: ["-Wexit-time-destructors"],
+ host_ldlibs: ["-lrt"],
},
windows: {
srcs: [
@@ -98,7 +112,6 @@
cc_test {
name: "libbase_test",
host_supported: true,
- clang: true,
srcs: [
"endian_test.cpp",
"errors_test.cpp",
@@ -108,17 +121,25 @@
"parseint_test.cpp",
"parsenetaddress_test.cpp",
"quick_exit_test.cpp",
+ "scopeguard_test.cpp",
"stringprintf_test.cpp",
"strings_test.cpp",
"test_main.cpp",
],
target: {
android: {
- srcs: ["properties_test.cpp"],
+ srcs: [
+ "chrono_utils_test.cpp",
+ "properties_test.cpp"
+ ],
sanitize: {
misc_undefined: ["integer"],
},
},
+ linux: {
+ srcs: ["chrono_utils_test.cpp"],
+ host_ldlibs: ["-lrt"],
+ },
windows: {
srcs: ["utf8_test.cpp"],
enabled: true,
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
new file mode 100644
index 0000000..b6bf701
--- /dev/null
+++ b/base/chrono_utils.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "android-base/chrono_utils.h"
+
+#include <time.h>
+
+namespace android {
+namespace base {
+
+boot_clock::time_point boot_clock::now() {
+#ifdef __ANDROID__
+ timespec ts;
+ clock_gettime(CLOCK_BOOTTIME, &ts);
+ return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
+ std::chrono::nanoseconds(ts.tv_nsec));
+#else
+ // Darwin does not support clock_gettime.
+ return boot_clock::time_point();
+#endif // __ANDROID__
+}
+
+std::ostream& operator<<(std::ostream& os, const Timer& t) {
+ os << t.duration().count() << "ms";
+ return os;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/chrono_utils_test.cpp b/base/chrono_utils_test.cpp
new file mode 100644
index 0000000..da442f4
--- /dev/null
+++ b/base/chrono_utils_test.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 "android-base/chrono_utils.h"
+
+#include <time.h>
+
+#include <chrono>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+std::chrono::seconds GetBootTimeSeconds() {
+ struct timespec now;
+ clock_gettime(CLOCK_BOOTTIME, &now);
+
+ auto now_tp = boot_clock::time_point(std::chrono::seconds(now.tv_sec) +
+ std::chrono::nanoseconds(now.tv_nsec));
+ return std::chrono::duration_cast<std::chrono::seconds>(now_tp.time_since_epoch());
+}
+
+// Tests (at least) the seconds accuracy of the boot_clock::now() method.
+TEST(ChronoUtilsTest, BootClockNowSeconds) {
+ auto now = GetBootTimeSeconds();
+ auto boot_seconds =
+ std::chrono::duration_cast<std::chrono::seconds>(boot_clock::now().time_since_epoch());
+ EXPECT_EQ(now, boot_seconds);
+}
+
+template <typename T>
+void ExpectAboutEqual(T expected, T actual) {
+ auto expected_upper_bound = expected * 1.05f;
+ auto expected_lower_bound = expected * .95;
+ EXPECT_GT(expected_upper_bound, actual);
+ EXPECT_LT(expected_lower_bound, actual);
+}
+
+TEST(ChronoUtilsTest, TimerDurationIsSane) {
+ auto start = boot_clock::now();
+ Timer t;
+ std::this_thread::sleep_for(50ms);
+ auto stop = boot_clock::now();
+ auto stop_timer = t.duration();
+
+ auto expected = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
+ ExpectAboutEqual(expected, stop_timer);
+}
+
+TEST(ChronoUtilsTest, TimerOstream) {
+ Timer t;
+ std::this_thread::sleep_for(50ms);
+ auto stop_timer = t.duration().count();
+ std::stringstream os;
+ os << t;
+ decltype(stop_timer) stop_timer_from_stream;
+ os >> stop_timer_from_stream;
+ EXPECT_NE(0, stop_timer);
+ ExpectAboutEqual(stop_timer, stop_timer_from_stream);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/file.cpp b/base/file.cpp
index d4e5894..2f697a1 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -28,16 +28,18 @@
#include <string>
#include <vector>
-#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
#include "android-base/logging.h"
+#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/unique_fd.h"
#include "android-base/utf8.h"
-#include "utils/Compat.h"
#if defined(__APPLE__)
#include <mach-o/dyld.h>
#endif
#if defined(_WIN32)
#include <windows.h>
+#define O_CLOEXEC O_NOINHERIT
+#define O_NOFOLLOW 0
#endif
namespace android {
@@ -69,13 +71,11 @@
content->clear();
int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
if (fd == -1) {
return false;
}
- bool result = ReadFdToString(fd, content);
- close(fd);
- return result;
+ return ReadFdToString(fd, content);
}
bool WriteStringToFd(const std::string& content, int fd) {
@@ -106,7 +106,7 @@
bool follow_symlinks) {
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
(follow_symlinks ? 0 : O_NOFOLLOW);
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
if (fd == -1) {
PLOG(ERROR) << "android::WriteStringToFile open failed";
return false;
@@ -126,7 +126,6 @@
PLOG(ERROR) << "android::WriteStringToFile write failed";
return CleanUpAfterFailedWrite(path);
}
- close(fd);
return true;
}
#endif
@@ -135,14 +134,11 @@
bool follow_symlinks) {
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
(follow_symlinks ? 0 : O_NOFOLLOW);
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0666)));
if (fd == -1) {
return false;
}
-
- bool result = WriteStringToFd(content, fd);
- close(fd);
- return result || CleanUpAfterFailedWrite(path);
+ return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
}
bool ReadFully(int fd, void* data, size_t byte_count) {
@@ -157,6 +153,37 @@
return true;
}
+#if defined(_WIN32)
+// Windows implementation of pread. Note that this DOES move the file descriptors read position,
+// but it does so atomically.
+static ssize_t pread(int fd, void* data, size_t byte_count, off64_t offset) {
+ DWORD bytes_read;
+ OVERLAPPED overlapped;
+ memset(&overlapped, 0, sizeof(OVERLAPPED));
+ overlapped.Offset = static_cast<DWORD>(offset);
+ overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
+ if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), data, static_cast<DWORD>(byte_count),
+ &bytes_read, &overlapped)) {
+ // In case someone tries to read errno (since this is masquerading as a POSIX call)
+ errno = EIO;
+ return -1;
+ }
+ return static_cast<ssize_t>(bytes_read);
+}
+#endif
+
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
+ uint8_t* p = reinterpret_cast<uint8_t*>(data);
+ while (byte_count > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(pread(fd, p, byte_count, offset));
+ if (n <= 0) return false;
+ p += n;
+ byte_count -= n;
+ offset += n;
+ }
+ return true;
+}
+
bool WriteFully(int fd, const void* data, size_t byte_count) {
const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
size_t remaining = byte_count;
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
new file mode 100644
index 0000000..7679d4c
--- /dev/null
+++ b/base/include/android-base/chrono_utils.h
@@ -0,0 +1,54 @@
+/*
+ * 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 ANDROID_BASE_CHRONO_UTILS_H
+#define ANDROID_BASE_CHRONO_UTILS_H
+
+#include <chrono>
+#include <sstream>
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace base {
+
+// A std::chrono clock based on CLOCK_BOOTTIME.
+class boot_clock {
+ public:
+ typedef std::chrono::nanoseconds duration;
+ typedef std::chrono::time_point<boot_clock, duration> time_point;
+
+ static time_point now();
+};
+
+class Timer {
+ public:
+ Timer() : start_(boot_clock::now()) {}
+
+ std::chrono::milliseconds duration() const {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_);
+ }
+
+ private:
+ boot_clock::time_point start_;
+};
+
+std::ostream& operator<<(std::ostream& os, const Timer& t);
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_CHRONO_UTILS_H
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 651f529..667d6fb 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -18,12 +18,18 @@
#define ANDROID_BASE_FILE_H
#include <sys/stat.h>
+#include <sys/types.h>
#include <string>
#if !defined(_WIN32) && !defined(O_BINARY)
#define O_BINARY 0
#endif
+#if defined(__APPLE__)
+/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
+typedef off_t off64_t;
+#endif
+
namespace android {
namespace base {
@@ -42,6 +48,17 @@
#endif
bool ReadFully(int fd, void* data, size_t byte_count);
+
+// Reads `byte_count` bytes from the file descriptor at the specified offset.
+// Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes.
+//
+// NOTE: On Linux/Mac, this function wraps pread, which provides atomic read support without
+// modifying the read pointer of the file descriptor. On Windows, however, the read pointer does
+// get modified. This means that ReadFullyAtOffset can be used concurrently with other calls to the
+// same function, but concurrently seeking or reading incrementally can lead to unexpected
+// behavior.
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset);
+
bool WriteFully(int fd, const void* data, size_t byte_count);
bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index e78edbb..548b286 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -164,9 +164,31 @@
using ::android::base::FATAL; \
return (severity); }())
+#ifdef __clang_analyzer__
+// Clang's static analyzer does not see the conditional statement inside
+// LogMessage's destructor that will abort on FATAL severity.
+#define ABORT_AFTER_LOG_FATAL for (;; abort())
+
+struct LogAbortAfterFullExpr {
+ ~LogAbortAfterFullExpr() __attribute__((noreturn)) { abort(); }
+ explicit operator bool() const { return false; }
+};
+// Provides an expression that evaluates to the truthiness of `x`, automatically
+// aborting if `c` is true.
+#define ABORT_AFTER_LOG_EXPR_IF(c, x) (((c) && ::android::base::LogAbortAfterFullExpr()) || (x))
+// Note to the static analyzer that we always execute FATAL logs in practice.
+#define MUST_LOG_MESSAGE(severity) (SEVERITY_LAMBDA(severity) == ::android::base::FATAL)
+#else
+#define ABORT_AFTER_LOG_FATAL
+#define ABORT_AFTER_LOG_EXPR_IF(c, x) (x)
+#define MUST_LOG_MESSAGE(severity) false
+#endif
+#define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x)
+
// Defines whether the given severity will be logged or silently swallowed.
#define WOULD_LOG(severity) \
- UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity())
+ (UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity()) || \
+ MUST_LOG_MESSAGE(severity))
// Get an ostream that can be used for logging at the given severity and to the default
// destination.
@@ -190,54 +212,47 @@
// LOG(FATAL) << "We didn't expect to reach here";
#define LOG(severity) LOG_TO(DEFAULT, severity)
+// Checks if we want to log something, and sets up appropriate RAII objects if
+// so.
+// Note: DO NOT USE DIRECTLY. This is an implementation detail.
+#define LOGGING_PREAMBLE(severity) \
+ (WOULD_LOG(severity) && \
+ ABORT_AFTER_LOG_EXPR_IF((SEVERITY_LAMBDA(severity)) == ::android::base::FATAL, true) && \
+ ::android::base::ErrnoRestorer())
+
// Logs a message to logcat with the specified log ID on Android otherwise to
// stderr. If the severity is FATAL it also causes an abort.
-// Use an if-else statement instead of just an if statement here. So if there is a
-// else statement after LOG() macro, it won't bind to the if statement in the macro.
-// do-while(0) statement doesn't work here. Because we need to support << operator
-// following the macro, like "LOG(DEBUG) << xxx;".
-
-#define LOG_TO(dest, severity) \
- WOULD_LOG(severity) && \
- ::android::base::ErrnoRestorer() && \
- LOG_STREAM_TO(dest, severity)
+// Use an expression here so we can support the << operator following the macro,
+// like "LOG(DEBUG) << xxx;".
+#define LOG_TO(dest, severity) LOGGING_PREAMBLE(severity) && LOG_STREAM_TO(dest, severity)
// A variant of LOG that also logs the current errno value. To be used when
// library calls fail.
#define PLOG(severity) PLOG_TO(DEFAULT, severity)
// Behaves like PLOG, but logs to the specified log ID.
-#define PLOG_TO(dest, severity) \
- WOULD_LOG(SEVERITY_LAMBDA(severity)) && \
- ::android::base::ErrnoRestorer() && \
- ::android::base::LogMessage(__FILE__, __LINE__, \
- ::android::base::dest, \
- SEVERITY_LAMBDA(severity), errno).stream()
+#define PLOG_TO(dest, severity) \
+ LOGGING_PREAMBLE(severity) && \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
+ SEVERITY_LAMBDA(severity), errno) \
+ .stream()
// Marker that code is yet to be implemented.
#define UNIMPLEMENTED(level) \
LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
-#ifdef __clang_analyzer__
-// ClangL static analyzer does not see the conditional statement inside
-// LogMessage's destructor that will abort on FATAL severity.
-#define ABORT_AFTER_LOG_FATAL for (;;abort())
-#else
-#define ABORT_AFTER_LOG_FATAL
-#endif
-
// Check whether condition x holds and LOG(FATAL) if not. The value of the
// expression x is only evaluated once. Extra logging can be appended using <<
// after. For example:
//
// CHECK(false == true) results in a log message of
// "Check failed: false == true".
-#define CHECK(x) \
- LIKELY((x)) || \
- ABORT_AFTER_LOG_FATAL \
- ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
- ::android::base::FATAL, -1).stream() \
- << "Check failed: " #x << " "
+#define CHECK(x) \
+ LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) || \
+ ::android::base::LogMessage( \
+ __FILE__, __LINE__, ::android::base::DEFAULT, ::android::base::FATAL, \
+ -1).stream() \
+ << "Check failed: " #x << " "
// Helper for CHECK_xx(x,y) macros.
#define CHECK_OP(LHS, RHS, OP) \
@@ -304,7 +319,7 @@
// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
// CHECK should be used unless profiling identifies a CHECK as being in
// performance critical code.
-#if defined(NDEBUG)
+#if defined(NDEBUG) && !defined(__clang_analyzer__)
static constexpr bool kEnableDChecks = false;
#else
static constexpr bool kEnableDChecks = true;
@@ -328,7 +343,7 @@
if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
#define DCHECK_STRNE(s1, s2) \
if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
-#if defined(NDEBUG)
+#if defined(NDEBUG) && !defined(__clang_analyzer__)
#define DCHECK_CONSTEXPR(x, out, dummy)
#else
#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index 88bbe8a..25f2ff4 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -179,4 +179,19 @@
} while (0)
#endif
+// Current ABI string
+#if defined(__arm__)
+#define ABI_STRING "arm"
+#elif defined(__aarch64__)
+#define ABI_STRING "arm64"
+#elif defined(__i386__)
+#define ABI_STRING "x86"
+#elif defined(__x86_64__)
+#define ABI_STRING "x86_64"
+#elif defined(__mips__) && !defined(__LP64__)
+#define ABI_STRING "mips"
+#elif defined(__mips__) && defined(__LP64__)
+#define ABI_STRING "mips64"
+#endif
+
#endif // ANDROID_BASE_MACROS_H
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 4de5e57..041586c 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -62,15 +62,14 @@
// Waits for the system property `key` to have the value `expected_value`.
// Times out after `relative_timeout`.
// Returns true on success, false on timeout.
-bool WaitForProperty(const std::string& key,
- const std::string& expected_value,
- std::chrono::milliseconds relative_timeout);
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
+ std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
// Waits for the system property `key` to be created.
// Times out after `relative_timeout`.
// Returns true on success, false on timeout.
-bool WaitForPropertyCreation(const std::string& key,
- std::chrono::milliseconds relative_timeout);
+bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
+ std::chrono::milliseconds::max());
} // namespace base
} // namespace android
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
new file mode 100644
index 0000000..abcf4bc
--- /dev/null
+++ b/base/include/android-base/scopeguard.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_BASE_SCOPEGUARD_H
+#define ANDROID_BASE_SCOPEGUARD_H
+
+#include <utility> // for std::move
+
+namespace android {
+namespace base {
+
+template <typename F>
+class ScopeGuard {
+ public:
+ ScopeGuard(F f) : f_(f), active_(true) {}
+
+ ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
+ that.active_ = false;
+ }
+
+ ~ScopeGuard() {
+ if (active_) f_();
+ }
+
+ ScopeGuard() = delete;
+ ScopeGuard(const ScopeGuard&) = delete;
+ void operator=(const ScopeGuard&) = delete;
+ void operator=(ScopeGuard&& that) = delete;
+
+ void Disable() { active_ = false; }
+
+ bool active() const { return active_; }
+
+ private:
+ F f_;
+ bool active_;
+};
+
+template <typename T>
+ScopeGuard<T> make_scope_guard(T f) {
+ return ScopeGuard<T>(f);
+}
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_SCOPEGUARD_H
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index c0bf0c1..07a5edd 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -26,6 +26,10 @@
TemporaryFile();
~TemporaryFile();
+ // Release the ownership of fd, caller is reponsible for closing the
+ // fd or stream properly.
+ int release();
+
int fd;
char path[1024];
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 6cfcfcd..5d89271 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -17,6 +17,13 @@
#ifndef ANDROID_BASE_UNIQUE_FD_H
#define ANDROID_BASE_UNIQUE_FD_H
+#include <fcntl.h>
+
+#if !defined(_WIN32)
+#include <sys/socket.h>
+#endif
+
+#include <sys/types.h>
#include <unistd.h>
// DO NOT INCLUDE OTHER LIBBASE HEADERS!
@@ -88,6 +95,49 @@
using unique_fd = unique_fd_impl<DefaultCloser>;
+#if !defined(_WIN32)
+
+// Inline functions, so that they can be used header-only.
+inline bool Pipe(unique_fd* read, unique_fd* write) {
+ int pipefd[2];
+
+#if defined(__linux__)
+ if (pipe2(pipefd, O_CLOEXEC) != 0) {
+ return false;
+ }
+#else // defined(__APPLE__)
+ if (pipe(pipefd) != 0) {
+ return false;
+ }
+
+ if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return false;
+ }
+#endif
+
+ read->reset(pipefd[0]);
+ write->reset(pipefd[1]);
+ return true;
+}
+
+inline bool Socketpair(int domain, int type, int protocol, unique_fd* left, unique_fd* right) {
+ int sockfd[2];
+ if (socketpair(domain, type, protocol, sockfd) != 0) {
+ return false;
+ }
+ left->reset(sockfd[0]);
+ right->reset(sockfd[1]);
+ return true;
+}
+
+inline bool Socketpair(int type, unique_fd* left, unique_fd* right) {
+ return Socketpair(AF_UNIX, type, 0, left, right);
+}
+
+#endif // !defined(_WIN32)
+
} // namespace base
} // namespace android
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
index 2d5a6f6..c9cc1ab 100755
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -22,6 +22,8 @@
#else
// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
#include <fcntl.h> // open
+#include <stdio.h> // fopen
+#include <sys/stat.h> // mkdir
#include <unistd.h> // unlink
#endif
@@ -53,6 +55,19 @@
// Convert a UTF-8 std::string (including any embedded NULL characters) to
// UTF-16. Returns whether the conversion was done successfully.
bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
+
+// Convert a file system path, represented as a NULL-terminated string of
+// UTF-8 characters, to a UTF-16 string representing the same file system
+// path using the Windows extended-lengh path representation.
+//
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH:
+// ```The Windows API has many functions that also have Unicode versions to
+// permit an extended-length path for a maximum total path length of 32,767
+// characters. To specify an extended-length path, use the "\\?\" prefix.
+// For example, "\\?\D:\very long path".```
+//
+// Returns whether the conversion was done successfully.
+bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16);
#endif
// The functions in the utf8 namespace take UTF-8 strings. For Windows, these
@@ -73,9 +88,13 @@
namespace utf8 {
#ifdef _WIN32
+FILE* fopen(const char* name, const char* mode);
+int mkdir(const char* name, mode_t mode);
int open(const char* name, int flags, ...);
int unlink(const char* name);
#else
+using ::fopen;
+using ::mkdir;
using ::open;
using ::unlink;
#endif
diff --git a/base/properties.cpp b/base/properties.cpp
index 32c0128..816bca0 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -101,22 +101,24 @@
}
// TODO: chrono_utils?
-static void DurationToTimeSpec(timespec& ts, std::chrono::nanoseconds d) {
+static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) {
auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
ts.tv_sec = s.count();
ts.tv_nsec = ns.count();
}
+// TODO: boot_clock?
using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
-static void UpdateTimeSpec(timespec& ts,
- const AbsTime& timeout) {
+static void UpdateTimeSpec(timespec& ts, std::chrono::milliseconds relative_timeout,
+ const AbsTime& start_time) {
auto now = std::chrono::steady_clock::now();
- auto remaining_timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout - now);
- if (remaining_timeout < 0ns) {
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed >= relative_timeout) {
ts = { 0, 0 };
} else {
+ auto remaining_timeout = relative_timeout - time_elapsed;
DurationToTimeSpec(ts, remaining_timeout);
}
}
@@ -127,11 +129,7 @@
// Returns nullptr on timeout.
static const prop_info* WaitForPropertyCreation(const std::string& key,
const std::chrono::milliseconds& relative_timeout,
- AbsTime& absolute_timeout) {
- // TODO: boot_clock?
- auto now = std::chrono::steady_clock::now();
- absolute_timeout = now + relative_timeout;
-
+ const AbsTime& start_time) {
// Find the property's prop_info*.
const prop_info* pi;
unsigned global_serial = 0;
@@ -139,17 +137,16 @@
// The property doesn't even exist yet.
// Wait for a global change and then look again.
timespec ts;
- UpdateTimeSpec(ts, absolute_timeout);
+ UpdateTimeSpec(ts, relative_timeout, start_time);
if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr;
}
return pi;
}
-bool WaitForProperty(const std::string& key,
- const std::string& expected_value,
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
std::chrono::milliseconds relative_timeout) {
- AbsTime absolute_timeout;
- const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, absolute_timeout);
+ auto start_time = std::chrono::steady_clock::now();
+ const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, start_time);
if (pi == nullptr) return false;
WaitForPropertyData data;
@@ -162,7 +159,7 @@
if (data.done) return true;
// It didn't, so wait for the property to change before checking again.
- UpdateTimeSpec(ts, absolute_timeout);
+ UpdateTimeSpec(ts, relative_timeout, start_time);
uint32_t unused;
if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
}
@@ -170,8 +167,8 @@
bool WaitForPropertyCreation(const std::string& key,
std::chrono::milliseconds relative_timeout) {
- AbsTime absolute_timeout;
- return (WaitForPropertyCreation(key, relative_timeout, absolute_timeout) != nullptr);
+ auto start_time = std::chrono::steady_clock::now();
+ return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
}
} // namespace base
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index 1bbe482..de5f3dc 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -151,6 +151,38 @@
ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
}
+TEST(properties, WaitForProperty_MaxTimeout) {
+ std::atomic<bool> flag{false};
+ std::thread thread([&]() {
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+ while (!flag) std::this_thread::yield();
+ std::this_thread::sleep_for(500ms);
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+ });
+
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+ flag = true;
+ // Test that this does not immediately return false due to overflow issues with the timeout.
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
+ thread.join();
+}
+
+TEST(properties, WaitForProperty_NegativeTimeout) {
+ std::atomic<bool> flag{false};
+ std::thread thread([&]() {
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+ while (!flag) std::this_thread::yield();
+ std::this_thread::sleep_for(500ms);
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+ });
+
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+ flag = true;
+ // Assert that this immediately returns with a negative timeout
+ ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
+ thread.join();
+}
+
TEST(properties, WaitForPropertyCreation) {
std::thread thread([&]() {
std::this_thread::sleep_for(100ms);
diff --git a/base/scopeguard_test.cpp b/base/scopeguard_test.cpp
new file mode 100644
index 0000000..e11154a
--- /dev/null
+++ b/base/scopeguard_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "android-base/scopeguard.h"
+
+#include <utility>
+
+#include <gtest/gtest.h>
+
+TEST(scopeguard, normal) {
+ bool guarded_var = true;
+ {
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ }
+ ASSERT_FALSE(guarded_var);
+}
+
+TEST(scopeguard, disabled) {
+ bool guarded_var = true;
+ {
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ scopeguard.Disable();
+ }
+ ASSERT_TRUE(guarded_var);
+}
+
+TEST(scopeguard, moved) {
+ int guarded_var = true;
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ { decltype(scopeguard) new_guard(std::move(scopeguard)); }
+ EXPECT_FALSE(scopeguard.active());
+ ASSERT_FALSE(guarded_var);
+}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 636477d..1cfa9e6 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -85,10 +85,18 @@
}
TemporaryFile::~TemporaryFile() {
- close(fd);
+ if (fd != -1) {
+ close(fd);
+ }
unlink(path);
}
+int TemporaryFile::release() {
+ int result = fd;
+ fd = -1;
+ return result;
+}
+
void TemporaryFile::init(const std::string& tmp_dir) {
snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(),
OS_PATH_SEPARATOR);
diff --git a/base/utf8.cpp b/base/utf8.cpp
old mode 100755
new mode 100644
index 3cca700..5984fb0
--- a/base/utf8.cpp
+++ b/base/utf8.cpp
@@ -19,7 +19,9 @@
#include "android-base/utf8.h"
#include <fcntl.h>
+#include <stdio.h>
+#include <algorithm>
#include <string>
#include "android-base/logging.h"
@@ -153,12 +155,58 @@
return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
}
+static bool isDriveLetter(wchar_t c) {
+ return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z');
+}
+
+bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16) {
+ if (!UTF8ToWide(utf8, utf16)) {
+ return false;
+ }
+ // Note: Although most Win32 File I/O API are limited to MAX_PATH (260
+ // characters), the CreateDirectory API is limited to 248 characters.
+ if (utf16->length() >= 248) {
+ // If path is of the form "x:\" or "x:/"
+ if (isDriveLetter((*utf16)[0]) && (*utf16)[1] == L':' &&
+ ((*utf16)[2] == L'\\' || (*utf16)[2] == L'/')) {
+ // Append long path prefix, and make sure there are no unix-style
+ // separators to ensure a fully compliant Win32 long path string.
+ utf16->insert(0, LR"(\\?\)");
+ std::replace(utf16->begin(), utf16->end(), L'/', L'\\');
+ }
+ }
+ return true;
+}
+
// Versions of standard library APIs that support UTF-8 strings.
namespace utf8 {
+FILE* fopen(const char* name, const char* mode) {
+ std::wstring name_utf16;
+ if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
+ return nullptr;
+ }
+
+ std::wstring mode_utf16;
+ if (!UTF8ToWide(mode, &mode_utf16)) {
+ return nullptr;
+ }
+
+ return _wfopen(name_utf16.c_str(), mode_utf16.c_str());
+}
+
+int mkdir(const char* name, mode_t mode) {
+ std::wstring name_utf16;
+ if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
+ return -1;
+ }
+
+ return _wmkdir(name_utf16.c_str());
+}
+
int open(const char* name, int flags, ...) {
std::wstring name_utf16;
- if (!UTF8ToWide(name, &name_utf16)) {
+ if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
return -1;
}
@@ -175,7 +223,7 @@
int unlink(const char* name) {
std::wstring name_utf16;
- if (!UTF8ToWide(name, &name_utf16)) {
+ if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
return -1;
}
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
old mode 100755
new mode 100644
index ae8fc8c..fcb25c3
--- a/base/utf8_test.cpp
+++ b/base/utf8_test.cpp
@@ -18,7 +18,12 @@
#include <gtest/gtest.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
#include "android-base/macros.h"
+#include "android-base/test_utils.h"
+#include "android-base/unique_fd.h"
namespace android {
namespace base {
@@ -408,5 +413,76 @@
EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
}
+TEST(UTF8PathToWindowsLongPathTest, DontAddPrefixIfShorterThanMaxPath) {
+ std::string utf8 = "c:\\mypath\\myfile.txt";
+
+ std::wstring wide;
+ EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+ EXPECT_EQ(std::string::npos, wide.find(LR"(\\?\)"));
+}
+
+TEST(UTF8PathToWindowsLongPathTest, AddPrefixIfLongerThanMaxPath) {
+ std::string utf8 = "c:\\mypath";
+ while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+ utf8 += "\\mypathsegment";
+ }
+
+ std::wstring wide;
+ EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+ EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
+ EXPECT_EQ(std::string::npos, wide.find(L"/"));
+}
+
+TEST(UTF8PathToWindowsLongPathTest, AddPrefixAndFixSeparatorsIfLongerThanMaxPath) {
+ std::string utf8 = "c:/mypath";
+ while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+ utf8 += "/mypathsegment";
+ }
+
+ std::wstring wide;
+ EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+ EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
+ EXPECT_EQ(std::string::npos, wide.find(L"/"));
+}
+
+namespace utf8 {
+
+TEST(Utf8FilesTest, CanCreateOpenAndDeleteFileWithLongPath) {
+ TemporaryDir td;
+
+ // Create long directory path
+ std::string utf8 = td.path;
+ while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+ utf8 += "\\mypathsegment";
+ EXPECT_EQ(0, mkdir(utf8.c_str(), 0));
+ }
+
+ // Create file
+ utf8 += "\\test-file.bin";
+ int flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
+ int mode = 0666;
+ android::base::unique_fd fd(open(utf8.c_str(), flags, mode));
+ EXPECT_NE(-1, fd.get());
+
+ // Close file
+ fd.reset();
+ EXPECT_EQ(-1, fd.get());
+
+ // Open file with fopen
+ FILE* file = fopen(utf8.c_str(), "rb");
+ EXPECT_NE(nullptr, file);
+
+ if (file) {
+ fclose(file);
+ }
+
+ // Delete file
+ EXPECT_EQ(0, unlink(utf8.c_str()));
+}
+
+} // namespace utf8
} // namespace base
} // namespace android
diff --git a/bootstat/.clang-format b/bootstat/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/bootstat/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index f744ad1..6734f4d 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -16,7 +16,6 @@
bootstat_lib_src_files = [
"boot_event_record_store.cpp",
- "uptime_parser.cpp",
]
cc_defaults {
@@ -33,9 +32,6 @@
"liblog",
"libmetricslogger",
],
- whole_static_libs: ["libgtest_prod"],
- // Clang is required because of C++14
- clang: true,
}
// bootstat static library
@@ -67,6 +63,7 @@
name: "bootstat",
defaults: ["bootstat_defaults"],
static_libs: ["libbootstat"],
+ shared_libs: ["liblogcat"],
init_rc: ["bootstat.rc"],
srcs: ["bootstat.cpp"],
}
@@ -75,6 +72,7 @@
// -----------------------------------------------------------------------------
cc_test {
name: "bootstat_tests",
+ test_suites: ["device-tests"],
defaults: ["bootstat_defaults"],
host_supported: true,
static_libs: [
diff --git a/bootstat/AndroidTest.xml b/bootstat/AndroidTest.xml
new file mode 100644
index 0000000..f3783fa
--- /dev/null
+++ b/bootstat/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for bootstat_tests">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="bootstat_tests->/data/local/tmp/bootstat_tests" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="bootstat_tests" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 2648594..99d9405 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -20,13 +20,16 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <utime.h>
+
+#include <chrono>
#include <cstdlib>
#include <string>
#include <utility>
+
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
-#include "uptime_parser.h"
namespace {
@@ -55,7 +58,9 @@
}
void BootEventRecordStore::AddBootEvent(const std::string& event) {
- AddBootEventWithValue(event, bootstat::ParseUptime());
+ auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
+ android::base::boot_clock::now().time_since_epoch());
+ AddBootEventWithValue(event, uptime.count());
}
// The implementation of AddBootEventValue makes use of the mtime file
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 90f6513..d98169b 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -21,15 +21,18 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
+
+#include <chrono>
#include <cstdint>
#include <cstdlib>
+
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
#include <gmock/gmock.h>
-#include "uptime_parser.h"
+#include <gtest/gtest.h>
using testing::UnorderedElementsAreArray;
@@ -89,6 +92,13 @@
rmdir(path.c_str());
}
+// Returns the time in seconds since boot.
+time_t GetUptimeSeconds() {
+ return std::chrono::duration_cast<std::chrono::seconds>(
+ android::base::boot_clock::now().time_since_epoch())
+ .count();
+}
+
class BootEventRecordStoreTest : public ::testing::Test {
public:
BootEventRecordStoreTest() {
@@ -126,7 +136,7 @@
BootEventRecordStore store;
store.SetStorePath(GetStorePathForTesting());
- time_t uptime = bootstat::ParseUptime();
+ time_t uptime = GetUptimeSeconds();
ASSERT_NE(-1, uptime);
store.AddBootEvent("cenozoic");
@@ -141,7 +151,7 @@
BootEventRecordStore store;
store.SetStorePath(GetStorePathForTesting());
- time_t uptime = bootstat::ParseUptime();
+ time_t uptime = GetUptimeSeconds();
ASSERT_NE(-1, uptime);
store.AddBootEvent("cretaceous");
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
new file mode 100755
index 0000000..d789808
--- /dev/null
+++ b/bootstat/boot_reason_test.sh
@@ -0,0 +1,721 @@
+#! /bin/bash
+#
+# Bootstat boot reason tests
+#
+# throughout testing:
+# - manual tests can only run on eng/userdebug builds
+# - watch adb logcat -b all -d -s bootstat
+# - watch adb logcat -b all -d | audit2allow
+# - wait until screen is up, boot has completed, can mean wait for
+# sys.boot_completed=1 and sys.logbootcomplete=1 to be true
+#
+# All test frames, and nothing else, must be function names prefixed and
+# specifiged with the pattern 'test_<test>() {' as this is also how the
+# script discovers the full list of tests by inspecting its own code.
+#
+
+# Helper variables
+
+SPACE=" "
+ESCAPE=""
+TAB=" "
+GREEN="${ESCAPE}[38;5;40m"
+RED="${ESCAPE}[38;5;196m"
+NORMAL="${ESCAPE}[0m"
+
+# Helper functions
+
+[ "USAGE: inFastboot
+
+Returns: true if device is in fastboot mode" ]
+inFastboot() {
+ fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: format_duration <seconds>
+
+human readable output whole seconds, whole minutes or mm:ss" ]
+format_duration() {
+ if [ -z "${1}" ]; then
+ echo unknown
+ return
+ fi
+ seconds=`expr ${1} % 60`
+ minutes=`expr ${1} / 60`
+ if [ 0 -eq ${minutes} ]; then
+ if [ 1 -eq ${1} ]; then
+ echo 1 second
+ return
+ fi
+ echo ${1} seconds
+ return
+ elif [ 60 -eq ${1} ]; then
+ echo 1 minute
+ return
+ elif [ 0 -eq ${seconds} ]; then
+ echo ${minutes} minutes
+ return
+ fi
+ echo ${minutes}:`expr ${seconds} / 10``expr ${seconds} % 10`
+}
+
+wait_for_screen_timeout=900
+[ "USAGE: wait_for_screen [-n] [TIMEOUT]
+
+-n - echo newline at exit
+TIMEOUT - default `format_duration ${wait_for_screen_timeout}`" ]
+wait_for_screen() {
+ exit_function=true
+ if [ X"-n" = X"${1}" ]; then
+ exit_function=echo
+ shift
+ fi
+ timeout=${wait_for_screen_timeout}
+ if [ ${#} -gt 0 ]; then
+ timeout=${1}
+ shift
+ fi
+ counter=0
+ while true; do
+ [ 0 = ${counter} ] ||
+ adb wait-for-device </dev/null >/dev/null 2>/dev/null
+ vals=`adb shell getprop </dev/null 2>/dev/null |
+ sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
+ [ 0 = ${counter} ] ||
+ sleep 1
+ if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]; then
+ break
+ fi
+ if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]; then
+ break
+ fi
+ counter=`expr ${counter} + 1`
+ if [ ${counter} -gt ${timeout} ]; then
+ ${exit_function}
+ echo "ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)" >&2
+ return 1
+ fi
+ done
+ ${exit_function}
+}
+
+[ "USAGE: EXPECT_EQ <lval> <rval> [message]
+
+Returns true if (regex) lval matches rval" ]
+EXPECT_EQ() {
+ lval="${1}"
+ rval="${2}"
+ shift 2
+ if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
+ echo "ERROR: expected \"${lval}\" got \"${rval}\"" >&2
+ if [ -n "${*}" ] ; then
+ echo " ${*}" >&2
+ fi
+ return 1
+ fi
+ if [ -n "${*}" ] ; then
+ if [ X"${lval}" != X"${rval}" ]; then
+ echo "INFO: ok \"${lval}\"(=\"${rval}\") ${*}" >&2
+ else
+ echo "INFO: ok \"${lval}\" ${*}" >&2
+ fi
+ fi
+ return 0
+}
+
+[ "USAGE: EXPECT_PROPERTY <prop> <value>
+
+Returns true if current return (regex) value is true and the result matches" ]
+EXPECT_PROPERTY() {
+ save_ret=${?}
+ property="${1}"
+ value="${2}"
+ shift 2
+ val=`adb shell getprop ${property} 2>&1`
+ EXPECT_EQ "${value}" "${val}" for Android property ${property} ||
+ save_ret=${?}
+ return ${save_ret}
+}
+
+[ "USAGE: report_bootstat_logs <expected> ...
+
+if not prefixed with a minus (-), <expected> will become a series of expected
+matches:
+
+ bootstat: Canonical boot reason: <expected_property_value>
+
+If prefixed with a minus, <expected> will look for an exact match after
+removing the minux prefix. All expected content is _dropped_ from the output
+and in essence forms a known blacklist, unexpected content will show.
+
+Report any logs, minus a known blacklist, preserve the current exit status" ]
+report_bootstat_logs() {
+ save_ret=${?}
+ match=
+ for i in ${*}; do
+ if [ X"${i}" != X"${i#-}" ] ; then
+ match="${match}
+${i#-}"
+ else
+ match="${match}
+bootstat: Canonical boot reason: ${i}"
+ fi
+ done
+ adb logcat -b all -d |
+ grep bootstat |
+ grep -v -F "bootstat: Service started: /system/bin/bootstat --record_boot_complete${match}
+bootstat: Failed to read /data/misc/bootstat/post_decrypt_time_elapsed: No such file or directory
+bootstat: Failed to parse boot time record: /data/misc/bootstat/post_decrypt_time_elapsed
+bootstat: Service started: /system/bin/bootstat --record_boot_reason
+bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
+bootstat: Service started: /system/bin/bootstat -l
+bootstat: Battery level at shutdown 100%
+bootstat: Battery level at startup 100%
+init : Parsing file /system/etc/init/bootstat.rc...
+init : processing action (post-fs-data) from (/system/etc/init/bootstat.rc
+init : processing action (boot) from (/system/etc/init/bootstat.rc
+init : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc
+init : processing action (sys.boot_completed=1 && sys.logbootcomplete=1) from (/system/etc/init/bootstat.rc
+init : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+init : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+init : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+ (/system/bin/bootstat --record_boot_complete)'...
+ (/system/bin/bootstat --record_boot_complete)' (pid${SPACE}
+ (/system/bin/bootstat --record_boot_reason)'...
+ (/system/bin/bootstat --record_boot_reason)' (pid${SPACE}
+ (/system/bin/bootstat --record_time_since_factory_reset)'...
+ (/system/bin/bootstat --record_time_since_factory_reset)' (pid${SPACE}
+ (/system/bin/bootstat -l)'...
+ (/system/bin/bootstat -l)' (pid " |
+ grep -v 'bootstat: Unknown boot reason: $' # Hikey Special
+ return ${save_ret}
+}
+
+[ "USAGE: start_test [message]
+
+Record start of test, preserve exit status" ]
+start_test() {
+ save_ret=${?}
+ START=`date +%s`
+ echo "${GREEN}[ RUN ]${NORMAL} ${TEST} ${*}"
+ return ${save_ret}
+}
+
+[ "USAGE: end_test [message]
+
+Document duration and success of test, preserve exit status" ]
+end_test() {
+ save_ret=${?}
+ END=`date +%s`
+ duration=`expr ${END} - ${START} 2>/dev/null`
+ [ 0 = ${duration} ] ||
+ echo INFO: ${TEST} test duration `format_duration ${duration}` >&2
+ if [ ${save_ret} = 0 ]; then
+ echo "${GREEN}[ OK ]${NORMAL} ${TEST} ${*}"
+ else
+ echo "${RED}[ FAILED ]${NORMAL} ${TEST} ${*}"
+ fi
+ return ${save_ret}
+}
+
+[ "USAGE: wrap_test <test> [message]
+
+All tests below are wrapped with this helper" ]
+wrap_test() {
+ if [ -z "${1}" -o X"nothing" = X"${1}" ]; then
+ return
+ fi
+ TEST=${1}
+ shift
+ start_test ${1}
+ eval test_${TEST}
+ end_test ${2}
+}
+
+[ "USAGE: validate_property <property>
+
+Check property for CTS compliance with our expectations. Return a cleansed
+string representing what is acceptable.
+
+NB: must roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
+validate_property() {
+ var=`adb shell getprop ${1} 2>&1`
+ var=`echo -n ${var} |
+ tr '[A-Z]' '[a-z]' |
+ tr ' \f\t\r\n' '_____'`
+ case ${var} in
+ watchdog) ;;
+ watchdog,?*) ;;
+ kernel_panic) ;;
+ kernel_panic,?*) ;;
+ recovery) ;;
+ recovery,?*) ;;
+ bootloader) ;;
+ bootloader,?*) ;;
+ cold) ;;
+ cold,?*) ;;
+ hard) ;;
+ hard,?*) ;;
+ warm) ;;
+ warm,?*) ;;
+ shutdown) ;;
+ shutdown,?*) ;;
+ reboot) ;;
+ reboot,?*) ;;
+ # Aliases
+ *wdog* | *watchdog* ) var="watchdog" ;;
+ *powerkey* ) var="cold,powerkey" ;;
+ *panic* | *kernel_panic*) var="kernel_panic" ;;
+ *thermal*) var="shutdown,thermal" ;;
+ *s3_wakeup*) var="warm,s3_wakeup" ;;
+ *hw_reset*) var="hard,hw_reset" ;;
+ *bootloader*) var="bootloader" ;;
+ ?*) var="reboot,${var}" ;;
+ *) var="reboot" ;;
+ esac
+ echo ${var}
+}
+
+#
+# Actual test frames
+#
+
+[ "USAGE: test_properties
+
+properties test
+- (wait until screen is up, boot has completed)
+- adb shell getprop ro.boot.bootreason (bootloader reason)
+- adb shell getprop persist.sys.boot.reason (last reason)
+- adb shell getprop sys.boot.reason (system reason)
+- NB: all should have a value that is compliant with our known set." ]
+test_properties() {
+ wait_for_screen
+ retval=0
+ check_set="ro.boot.bootreason persist.sys.boot.reason sys.boot.reason"
+ bootloader=""
+ # NB: this test could fail if performed _after_ optional_factory_reset test
+ # and will report
+ # ERROR: expected "reboot" got ""
+ # for Android property persist.sys.boot.reason
+ # following is mitigation for the persist.sys.boot.reason, skip it
+ if [ "reboot,factory_reset" = `validate_property ro.boot_bootreason` ]; then
+ check_set="ro.boot.bootreason sys.boot.reason"
+ bootloader="bootloader"
+ fi
+ for prop in ${check_set}; do
+ reason=`validate_property ${prop}`
+ EXPECT_PROPERTY ${prop} ${reason} || retval=${?}
+ done
+ # sys.boot.reason is last for a reason
+ report_bootstat_logs ${reason} ${bootloader}
+ return ${retval}
+}
+
+[ "USAGE: test_ota
+
+ota test
+- rm out/.kati_stamp-* out/build_date.txt out/build_number.txt
+- rm out/target/product/*/*/*.prop
+- rm -r out/target/product/*/obj/ETC/system_build_prop_intermediates
+- m
+- NB: ro.build.date.utc should update
+- fastboot flashall
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report ota
+
+Decision to change the build itself rather than trick bootstat by
+rummaging through its data files was made." ]
+test_ota() {
+ echo "INFO: expected duration of ${TEST} test about 5 minutes or more" >&2
+ echo " extended by build and flashing times" >&2
+ if [ -z "${TARGET_PRODUCT}" -o \
+ -z "${ANDROID_PRODUCT_OUT}" -o \
+ -z "${ANDROID_BUILD_TOP}" -o \
+ -z "${TARGET_BUILD_VARIANT}" ]; then
+ echo "ERROR: Missing envsetup.sh and lunch" >&2
+ return 1
+ fi
+ rm ${ANDROID_PRODUCT_OUT%/out/*}/out/.kati_stamp-* ||
+ true
+ rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_date.txt ||
+ true
+ rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_number.txt ||
+ true
+ rm ${ANDROID_PRODUCT_OUT}/*/*.prop ||
+ true
+ rm -r ${ANDROID_PRODUCT_OUT}/obj/ETC/system_build_prop_intermediates ||
+ true
+ pushd ${ANDROID_BUILD_TOP} >&2
+ make -j50 >&2
+ if [ ${?} != 0 ]; then
+ popd >&2
+ return 1
+ fi
+ if ! inFastboot; then
+ adb reboot-bootloader >&2
+ fi
+ fastboot flashall >&2
+ popd >&2
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,bootloader
+ report_bootstat_logs reboot,ota bootloader
+}
+
+[ "USAGE: test_optional_ota
+
+fast and fake (touch build_date on device to make it different)" ]
+test_optional_ota() {
+ echo "INFO: expected duration of ${TEST} test about 45 seconds" >&2
+ adb shell su root touch /data/misc/bootstat/build_date >&2
+ adb reboot ota
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,ota
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,ota
+ report_bootstat_logs reboot,ota
+}
+
+[ "USAGE: [TEST=<test>] blind_reboot_test [<match>]
+
+Simple tests helper
+- adb reboot <test>
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report <test>, or overriden <match>
+
+We interleave the simple reboot tests between the hard/complex ones
+as a means of checking sanity and any persistent side effect of the
+other tests." ]
+blind_reboot_test() {
+ if [ -z "${1}" ]; then
+ set ${TEST}
+ fi
+ echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+ adb reboot ${TEST}
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason ${1}
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,${TEST}
+ report_bootstat_logs ${1}
+}
+
+[ "USAGE: test_cold
+
+cold test
+- adb reboot cold
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report cold" ]
+test_cold() {
+ blind_reboot_test
+}
+
+[ "USAGE: test_factory_reset
+
+factory_reset test
+- adb shell su root rm /data/misc/bootstat/build_date
+- adb reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report factory_reset
+
+Decision to rummage through bootstat data files was made as
+a _real_ factory_reset is too destructive to the device." ]
+test_factory_reset() {
+ echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+ adb shell su root rm /data/misc/bootstat/build_date >&2
+ adb reboot >&2
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
+ EXPECT_PROPERTY persist.sys.boot.reason "reboot,.*"
+ report_bootstat_logs reboot,factory_reset reboot, reboot,adb \
+ "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
+ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date"
+}
+
+[ "USAGE: test_optional_factory_reset
+
+factory_reset test
+- adb reboot-bootloader
+- fastboot format userdata
+- fastboot reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report factory_reset
+
+For realz, and disruptive" ]
+test_optional_factory_reset() {
+ echo "INFO: expected duration of ${TEST} test roughly a minute" >&2
+ if ! inFastboot; then
+ adb reboot-bootloader
+ fi
+ fastboot format userdata >&2
+ fastboot reboot >&2
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
+ EXPECT_PROPERTY persist.sys.boot.reason ""
+ report_bootstat_logs reboot,factory_reset bootloader \
+ "-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
+ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/last_boot_time_utc" \
+ "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
+ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date" \
+ "-bootstat: Failed to read /data/misc/bootstat/factory_reset: No such file or directory" \
+ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/factory_reset"
+}
+
+[ "USAGE: test_hard
+
+hard test:
+- adb reboot hard
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report hard" ]
+test_hard() {
+ blind_reboot_test
+}
+
+[ "USAGE: test_battery
+
+battery test (trick):
+- echo healthd: battery l=2 | adb shell su root tee /dev/kmsg ; adb reboot warm
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,battery, unless healthd managed to log
+ before reboot in above trick.
+
+- Bonus points (manual extras)
+- Make sure the following is added to the /init.rc file in post-fs
+ section before logd is started:
+ + setprop logd.kernel false
+ + rm /sys/fs/pstore/console-ramoops
+ + rm /sys/fs/pstore/console-ramoops-0
+ + write /dev/kmsg \"healthd: battery l=2
+ +\"
+- adb reboot fs
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,battery
+- (replace set logd.kernel true to the above, and retry test)" ]
+test_battery() {
+ echo "INFO: expected duration of ${TEST} test roughly two minutes" >&2
+ echo healthd: battery l=2 | adb shell su root tee /dev/kmsg >/dev/null
+ adb reboot warm >&2
+ wait_for_screen
+ adb shell su root \
+ cat /proc/fs/pstore/console-ramoops \
+ /proc/fs/pstore/console-ramoops-0 2>/dev/null |
+ grep 'healthd: battery l=' |
+ tail -1 |
+ grep 'healthd: battery l=2' >/dev/null || (
+ if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then
+ # retry
+ echo healthd: battery l=2 | adb shell su root tee /dev/kmsg >/dev/null
+ adb reboot warm >&2
+ wait_for_screen
+ fi
+ )
+
+ EXPECT_PROPERTY sys.boot.reason shutdown,battery
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,warm
+ report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
+}
+
+[ "USAGE: test_unknown
+
+unknown test
+- adb reboot unknown
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,unknown
+- NB: expect log \"... I bootstat: Unknown boot reason: reboot,unknown\"" ]
+test_unknown() {
+ blind_reboot_test reboot,unknown
+}
+
+[ "USAGE: test_kernel_panic
+
+kernel_panic test:
+- echo c | adb shell su root tee /proc/sysrq-trigger
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report kernel_panic,sysrq" ]
+test_kernel_panic() {
+ echo "INFO: expected duration of ${TEST} test > 2 minutes" >&2
+ echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason kernel_panic,sysrq
+ EXPECT_PROPERTY persist.sys.boot.reason kernel_panic,sysrq
+ report_bootstat_logs kernel_panic,sysrq
+}
+
+[ "USAGE: test_warm
+
+warm test
+- adb reboot warm
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report warm" ]
+test_warm() {
+ blind_reboot_test
+}
+
+[ "USAGE: test_thermal_shutdown
+
+thermal shutdown test:
+- adb shell setprop sys.powerctl shutdown,thermal
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,thermal" ]
+test_thermal_shutdown() {
+ echo "INFO: expected duration of ${TEST} test roughly a minute plus" >&2
+ echo " power on request" >&2
+ adb shell setprop sys.powerctl shutdown,thermal
+ sleep 5
+ echo -n "WARNING: Please power device back up, waiting ... " >&2
+ wait_for_screen -n >&2
+ EXPECT_PROPERTY sys.boot.reason shutdown,thermal
+ EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal
+ report_bootstat_logs shutdown,thermal
+}
+
+[ "USAGE: test_userrequested_shutdown
+
+userrequested shutdown test:
+- adb shell setprop sys.powerctl shutdown,userrequested
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,userrequested" ]
+test_userrequested_shutdown() {
+ echo "INFO: expected duration of ${TEST} test roughly a minute plus" >&2
+ echo " power on request" >&2
+ adb shell setprop sys.powerctl shutdown,userrequested
+ sleep 5
+ echo -n "WARNING: Please power device back up, waiting ... " >&2
+ wait_for_screen -n >&2
+ EXPECT_PROPERTY sys.boot.reason shutdown,userrequested
+ EXPECT_PROPERTY persist.sys.boot.reason shutdown,userrequested
+ report_bootstat_logs shutdown,userrequested
+}
+
+[ "USAGE: test_shell_reboot
+
+shell reboot test:
+- adb shell reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,shell" ]
+test_shell_reboot() {
+ echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+ adb shell reboot
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,shell
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,shell
+ report_bootstat_logs reboot,shell
+}
+
+[ "USAGE: test_adb_reboot
+
+adb reboot test:
+- adb reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,adb" ]
+test_adb_reboot() {
+ echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+ adb reboot
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,adb
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,adb
+ report_bootstat_logs reboot,adb
+}
+
+[ "USAGE: ${0##*/} [-s SERIAL] [tests]
+
+Mainline executive to run the above tests" ]
+
+# Rudimentary argument parsing
+
+if [ ${#} -ge 2 -a X"-s" = X"${1}" ]; then
+ export ANDROID_SERIAL="${2}"
+ shift 2
+fi
+
+if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
+ echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
+ echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+ exit 0
+fi
+
+# Check if all conditions for the script are sane
+
+if [ -z "${ANDROID_SERIAL}" ]; then
+ ndev=`(
+ adb devices | grep -v 'List of devices attached'
+ fastboot devices
+ ) |
+ grep -v "^[${SPACE}${TAB}]*\$" |
+ wc -l`
+ if [ ${ndev} -gt 1 ]; then
+ echo "ERROR: no target device specified, ${ndev} connected" >&2
+ echo "${RED}[ FAILED ]${NORMAL}"
+ exit 1
+ fi
+ echo "WARNING: no target device specified" >&2
+fi
+
+ret=0
+
+# Test Series
+if [ X"all" = X"${*}" ]; then
+ # automagically pick up all test_<function>s.
+ eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+ if [ X"nothing" = X"${1}" ]; then
+ shift 1
+ fi
+fi
+if [ -z "$*" ]; then
+ # automagically pick up all test_<function>, except test_optional_<function>.
+ eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null |
+ grep -v '^optional_'`
+ if [ -z "${2}" ]; then
+ # Hard coded should shell fail to find them above (search/permission issues)
+ eval set ota cold factory_reset hard battery unknown kernel_panic warm \
+ thermal_shutdown userrequested_shutdown shell_reboot adb_reboot
+ fi
+ if [ X"nothing" = X"${1}" ]; then
+ shift 1
+ fi
+fi
+echo "INFO: selected test(s): ${@}" >&2
+echo
+failures=
+successes=
+for t in "${@}"; do
+ wrap_test ${t}
+ retval=${?}
+ if [ 0 = ${retval} ]; then
+ if [ -z "${successes}" ]; then
+ successes=${t}
+ else
+ successes="${successes} ${t}"
+ fi
+ else
+ ret=${retval}
+ if [ -z "${failures}" ]; then
+ failures=${t}
+ else
+ failures="${failures} ${t}"
+ fi
+ fi
+ echo
+done
+
+if [ -n "${successes}" ]; then
+ echo "${GREEN}[ PASSED ]${NORMAL} ${successes}"
+fi
+if [ -n "${failures}" ]; then
+ echo "${RED}[ FAILED ]${NORMAL} ${failures}"
+fi
+exit ${ret}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a4cc5f2..a0a9307 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -20,7 +20,9 @@
#include <getopt.h>
#include <unistd.h>
+#include <sys/klog.h>
+#include <chrono>
#include <cmath>
#include <cstddef>
#include <cstdio>
@@ -30,15 +32,18 @@
#include <string>
#include <vector>
-#include <android/log.h>
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <android/log.h>
+#include <cutils/android_reboot.h>
#include <cutils/properties.h>
+#include <log/logcat.h>
#include <metricslogger/metrics_logger.h>
#include "boot_event_record_store.h"
-#include "uptime_parser.h"
namespace {
@@ -84,12 +89,13 @@
fprintf(stderr, "Usage: %s [options]\n", cmd);
fprintf(stderr,
"options include:\n"
- " -h, --help Show this help\n"
- " -l, --log Log all metrics to logstorage\n"
- " -p, --print Dump the boot event records to the console\n"
- " -r, --record Record the timestamp of a named boot event\n"
- " --value Optional value to associate with the boot event\n"
- " --record_boot_reason Record the reason why the device booted\n"
+ " -h, --help Show this help\n"
+ " -l, --log Log all metrics to logstorage\n"
+ " -p, --print Dump the boot event records to the console\n"
+ " -r, --record Record the timestamp of a named boot event\n"
+ " --value Optional value to associate with the boot event\n"
+ " --record_boot_complete Record metrics related to the time for the device boot\n"
+ " --record_boot_reason Record the reason why the device booted\n"
" --record_time_since_factory_reset Record the time since the device was reset\n");
}
@@ -116,6 +122,14 @@
return std::string(&temp[0], len);
}
+void SetProperty(const char* key, const std::string& val) {
+ property_set(key, val.c_str());
+}
+
+void SetProperty(const char* key, const char* val) {
+ property_set(key, val);
+}
+
constexpr int32_t kUnknownBootReason = 1;
// A mapping from boot reason string, as read from the ro.boot.bootreason
@@ -167,6 +181,29 @@
{"wdog_bark", 42},
{"wdog_bite", 43},
{"wdog_reset", 44},
+ {"shutdown,", 45}, // Trailing comma is intentional.
+ {"shutdown,userrequested", 46},
+ {"reboot,bootloader", 47},
+ {"reboot,cold", 48},
+ {"reboot,recovery", 49},
+ {"thermal_shutdown", 50},
+ {"s3_wakeup", 51},
+ {"kernel_panic,sysrq", 52},
+ {"kernel_panic,NULL", 53},
+ {"kernel_panic,BUG", 54},
+ {"bootloader", 55},
+ {"cold", 56},
+ {"hard", 57},
+ {"warm", 58},
+ {"recovery", 59},
+ {"thermal-shutdown", 60},
+ {"shutdown,thermal", 61},
+ {"shutdown,battery", 62},
+ {"reboot,ota", 63},
+ {"reboot,factory_reset", 64},
+ {"reboot,", 65},
+ {"reboot,shell", 66},
+ {"reboot,adb", 67},
};
// Converts a string value representing the reason the system booted to an
@@ -182,6 +219,323 @@
return kUnknownBootReason;
}
+// Canonical list of supported primary reboot reasons.
+const std::vector<const std::string> knownReasons = {
+ // kernel
+ "watchdog",
+ "kernel_panic",
+ // strong
+ "recovery", // Should not happen from ro.boot.bootreason
+ "bootloader", // Should not happen from ro.boot.bootreason
+ // blunt
+ "cold",
+ "hard",
+ "warm",
+ "shutdown", // Can not happen from ro.boot.bootreason
+ "reboot", // Default catch-all for anything unknown
+};
+
+// Returns true if the supplied reason prefix is considered detailed enough.
+bool isStrongRebootReason(const std::string& r) {
+ for (auto &s : knownReasons) {
+ if (s == "cold") break;
+ // Prefix defined as terminated by a nul or comma (,).
+ if (android::base::StartsWith(r, s.c_str()) &&
+ ((r.length() == s.length()) || (r[s.length()] == ','))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Returns true if the supplied reason prefix is associated with the kernel.
+bool isKernelRebootReason(const std::string& r) {
+ for (auto &s : knownReasons) {
+ if (s == "recovery") break;
+ // Prefix defined as terminated by a nul or comma (,).
+ if (android::base::StartsWith(r, s.c_str()) &&
+ ((r.length() == s.length()) || (r[s.length()] == ','))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Returns true if the supplied reason prefix is considered known.
+bool isKnownRebootReason(const std::string& r) {
+ for (auto &s : knownReasons) {
+ // Prefix defined as terminated by a nul or comma (,).
+ if (android::base::StartsWith(r, s.c_str()) &&
+ ((r.length() == s.length()) || (r[s.length()] == ','))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// If the reboot reason should be improved, report true if is too blunt.
+bool isBluntRebootReason(const std::string& r) {
+ if (isStrongRebootReason(r)) return false;
+
+ if (!isKnownRebootReason(r)) return true; // Can not support unknown as detail
+
+ size_t pos = 0;
+ while ((pos = r.find(',', pos)) != std::string::npos) {
+ ++pos;
+ std::string next(r.substr(pos));
+ if (next.length() == 0) break;
+ if (next[0] == ',') continue;
+ if (!isKnownRebootReason(next)) return false; // Unknown subreason is good.
+ if (isStrongRebootReason(next)) return false; // eg: reboot,reboot
+ }
+ return true;
+}
+
+// std::transform Helper callback functions:
+// Converts a string value representing the reason the system booted to a
+// string complying with Android system standard reason.
+char tounderline(char c) { return ::isblank(c) ? '_' : c; }
+char toprintable(char c) { return ::isprint(c) ? c : '?'; }
+
+const char system_reboot_reason_property[] = "sys.boot.reason";
+const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
+const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
+
+// Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
+std::string BootReasonStrToReason(const std::string& boot_reason) {
+ std::string ret(GetProperty(system_reboot_reason_property));
+ std::string reason(boot_reason);
+ // If sys.boot.reason == ro.boot.bootreason, let's re-evaluate
+ if (reason == ret) ret = "";
+
+ // Cleanup boot_reason regarding acceptable character set
+ std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
+ std::transform(reason.begin(), reason.end(), reason.begin(), tounderline);
+ std::transform(reason.begin(), reason.end(), reason.begin(), toprintable);
+
+ // Is the current system boot reason sys.boot.reason valid?
+ if (!isKnownRebootReason(ret)) ret = "";
+
+ if (ret == "") {
+ // Is the bootloader boot reason ro.boot.bootreason known?
+ std::vector<std::string> words(android::base::Split(reason, ",_-"));
+ for (auto &s : knownReasons) {
+ std::string blunt;
+ for (auto &r : words) {
+ if (r == s) {
+ if (isBluntRebootReason(s)) {
+ blunt = s;
+ } else {
+ ret = s;
+ break;
+ }
+ }
+ }
+ if (ret == "") ret = blunt;
+ if (ret != "") break;
+ }
+ }
+
+ if (ret == "") {
+ // A series of checks to take some officially unsupported reasons
+ // reported by the bootloader and find some logical and canonical
+ // sense. In an ideal world, we would require those bootloaders
+ // to behave and follow our standards.
+ static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
+ {"watchdog", "wdog"},
+ {"cold,powerkey", "powerkey"},
+ {"kernel_panic", "panic"},
+ {"shutdown,thermal", "thermal"},
+ {"warm,s3_wakeup", "s3_wakeup"},
+ {"hard,hw_reset", "hw_reset"},
+ {"bootloader", ""},
+ };
+
+ // Either the primary or alias is found _somewhere_ in the reason string.
+ for (auto &s : aliasReasons) {
+ if (reason.find(s.first) != std::string::npos) {
+ ret = s.first;
+ break;
+ }
+ if (s.second.size() && (reason.find(s.second) != std::string::npos)) {
+ ret = s.first;
+ break;
+ }
+ }
+ }
+
+ // If watchdog is the reason, see if there is a security angle?
+ if (ret == "watchdog") {
+ if (reason.find("sec") != std::string::npos) {
+ ret += ",security";
+ }
+ }
+
+ // Check the other reason resources if the reason is still blunt.
+ if (isBluntRebootReason(ret)) {
+ // Check to see if last klog has some refinement hints.
+ std::string content;
+ if (!android::base::ReadFileToString("/sys/fs/pstore/console-ramoops-0",
+ &content)) {
+ android::base::ReadFileToString("/sys/fs/pstore/console-ramoops",
+ &content);
+ }
+
+ // The toybox reboot command used directly (unlikely)? But also
+ // catches init's response to the Android's more controlled reboot command.
+ if (content.rfind("reboot: Power down") != std::string::npos) {
+ ret = "shutdown"; // Still too blunt, but more accurate.
+ // ToDo: init should record the shutdown reason to kernel messages ala:
+ // init: shutdown system with command 'last_reboot_reason'
+ // so that if pstore has persistence we can get some details
+ // that could be missing in last_reboot_reason_property.
+ }
+
+ static const char cmd[] = "reboot: Restarting system with command '";
+ size_t pos = content.rfind(cmd);
+ if (pos != std::string::npos) {
+ pos += strlen(cmd);
+ std::string subReason(content.substr(pos));
+ pos = subReason.find('\'');
+ if (pos != std::string::npos) subReason.erase(pos);
+ if (subReason != "") { // Will not land "reboot" as that is too blunt.
+ if (isKernelRebootReason(subReason)) {
+ ret = "reboot," + subReason; // User space can't talk kernel reasons.
+ } else {
+ ret = subReason;
+ }
+ }
+ }
+
+ // Check for kernel panics, but allowed to override reboot command.
+ if (content.rfind("sysrq: SysRq : Trigger a crash") != std::string::npos) {
+ // Can not happen, except on userdebug, during testing/debugging.
+ ret = "kernel_panic,sysrq";
+ } else if (content.rfind(
+ "Unable to handle kernel NULL pointer dereference at virtual address")
+ != std::string::npos) {
+ ret = "kernel_panic,NULL";
+ } else if (content.rfind("Kernel BUG at ") != std::string::npos) {
+ ret = "kernel_panic,BUG";
+ } else if ((content.rfind("Power held for ") != std::string::npos) ||
+ (content.rfind("charger: [") != std::string::npos)) {
+ ret = "cold";
+ }
+
+ // The following battery test should migrate to a default system health HAL
+
+ // Let us not worry if the reboot command was issued, for the cases of
+ // reboot -p, reboot <no reason>, reboot cold, reboot warm and reboot hard.
+ // Same for bootloader and ro.boot.bootreasons of this set, but a dead
+ // battery could conceivably lead to these, so worthy of override.
+ if (isBluntRebootReason(ret)) {
+ // Heuristic to determine if shutdown possibly because of a dead battery?
+ // Really a hail-mary pass to find it in last klog content ...
+ static const int battery_dead_threshold = 2; // percent
+ static const char battery[] = "healthd: battery l=";
+ pos = content.rfind(battery); // last one
+ if (pos != std::string::npos) {
+ int level = atoi(content.substr(pos + strlen(battery)).c_str());
+ LOG(INFO) << "Battery level at shutdown " << level << "%";
+ if (level <= battery_dead_threshold) {
+ ret = "shutdown,battery";
+ }
+ } else { // Most likely
+ // Content buffer no longer will have console data. Beware if more
+ // checks added below, that depend on parsing console content.
+ content = "";
+
+ LOG(DEBUG) << "Can not find last low battery in last console messages";
+ android_logcat_context ctx = create_android_logcat();
+ FILE *fp = android_logcat_popen(&ctx, "logcat -b kernel -v brief -d");
+ if (fp != nullptr) {
+ android::base::ReadFdToString(fileno(fp), &content);
+ }
+ android_logcat_pclose(&ctx, fp);
+ android_logcat_destroy(&ctx);
+ static const char logcat_battery[] = "W/healthd ( 0): battery l=";
+ const char* match = logcat_battery;
+
+ if (content == "") {
+ // Service logd.klog not running, go to smaller buffer in the kernel.
+ int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);
+ if (rc > 0) {
+ ssize_t len = rc + 1024; // 1K Margin should it grow between calls.
+ std::unique_ptr<char[]> buf(new char[len]);
+ rc = klogctl(KLOG_READ_ALL, buf.get(), len);
+ if (rc < len) {
+ len = rc + 1;
+ }
+ buf[--len] = '\0';
+ content = buf.get();
+ }
+ match = battery;
+ }
+
+ pos = content.find(match); // The first one it finds.
+ if (pos != std::string::npos) {
+ pos += strlen(match);
+ int level = atoi(content.substr(pos).c_str());
+ LOG(INFO) << "Battery level at startup " << level << "%";
+ if (level <= battery_dead_threshold) {
+ ret = "shutdown,battery";
+ }
+ } else {
+ LOG(DEBUG) << "Can not find first battery level in dmesg or logcat";
+ }
+ }
+ }
+
+ // Is there a controlled shutdown hint in last_reboot_reason_property?
+ if (isBluntRebootReason(ret)) {
+ // Content buffer no longer will have console data. Beware if more
+ // checks added below, that depend on parsing console content.
+ content = GetProperty(last_reboot_reason_property);
+
+ // String is either "reboot,<reason>" or "shutdown,<reason>".
+ // We will set if default reasons, only override with detail if thermal.
+ if (!isBluntRebootReason(content)) {
+ // Ok, we want it, let's squash it if secondReason is known.
+ pos = content.find(',');
+ if (pos != std::string::npos) {
+ ++pos;
+ std::string secondReason(content.substr(pos));
+ ret = isKnownRebootReason(secondReason) ? secondReason : content;
+ } else {
+ ret = content;
+ }
+ }
+ }
+
+ // Other System Health HAL reasons?
+
+ // ToDo: /proc/sys/kernel/boot_reason needs a HAL interface to
+ // possibly offer hardware-specific clues from the PMIC.
+ }
+
+ // If unknown left over from above, make it "reboot,<boot_reason>"
+ if (ret == "") {
+ ret = "reboot";
+ if (android::base::StartsWith(reason, "reboot")) {
+ reason = reason.substr(strlen("reboot"));
+ while (reason[0] == ',') {
+ reason = reason.substr(1);
+ }
+ }
+ if (reason != "") {
+ ret += ",";
+ ret += reason;
+ }
+ }
+
+ LOG(INFO) << "Canonical boot reason: " << ret;
+ if (isKernelRebootReason(ret) && (GetProperty(last_reboot_reason_property) != "")) {
+ // Rewrite as it must be old news, kernel reasons trump user space.
+ SetProperty(last_reboot_reason_property, ret);
+ }
+ return ret;
+}
+
// Returns the appropriate metric key prefix for the boot_complete metric such
// that boot metrics after a system update are labeled as ota_boot_complete;
// otherwise, they are labeled as boot_complete. This method encapsulates the
@@ -200,10 +554,22 @@
BootEventRecordStore boot_event_store;
BootEventRecordStore::BootEventRecord record;
- if (!boot_event_store.GetBootEvent(kBuildDateKey, &record) ||
- build_date != record.second) {
+ if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {
+ boot_complete_prefix = "factory_reset_" + boot_complete_prefix;
+ boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+ LOG(INFO) << "Canonical boot reason: " << "reboot,factory_reset";
+ SetProperty(system_reboot_reason_property, "reboot,factory_reset");
+ if (GetProperty(bootloader_reboot_reason_property) == "") {
+ SetProperty(bootloader_reboot_reason_property, "reboot,factory_reset");
+ }
+ } else if (build_date != record.second) {
boot_complete_prefix = "ota_" + boot_complete_prefix;
boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+ LOG(INFO) << "Canonical boot reason: " << "reboot,ota";
+ SetProperty(system_reboot_reason_property, "reboot,ota");
+ if (GetProperty(bootloader_reboot_reason_property) == "") {
+ SetProperty(bootloader_reboot_reason_property, "reboot,ota");
+ }
}
return boot_complete_prefix;
@@ -220,42 +586,78 @@
}
}
-// Parses and records the set of bootloader stages and associated boot times
-// from the ro.boot.boottime system property.
-void RecordBootloaderTimings(BootEventRecordStore* boot_event_store) {
- // |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN'.
+// A map from bootloader timing stage to the time that stage took during boot.
+typedef std::map<std::string, int32_t> BootloaderTimingMap;
+
+// Returns a mapping from bootloader stage names to the time those stages
+// took to boot.
+const BootloaderTimingMap GetBootLoaderTimings() {
+ BootloaderTimingMap timings;
+
+ // |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN',
+ // where timeN is in milliseconds.
std::string value = GetProperty("ro.boot.boottime");
if (value.empty()) {
// ro.boot.boottime is not reported on all devices.
- return;
+ return BootloaderTimingMap();
}
- int32_t total_time = 0;
auto stages = android::base::Split(value, ",");
- for (auto const &stageTiming : stages) {
+ for (const auto& stageTiming : stages) {
// |stageTiming| is of the form 'stage:time'.
auto stageTimingValues = android::base::Split(stageTiming, ":");
- DCHECK_EQ(2, stageTimingValues.size());
+ DCHECK_EQ(2U, stageTimingValues.size());
std::string stageName = stageTimingValues[0];
int32_t time_ms;
if (android::base::ParseInt(stageTimingValues[1], &time_ms)) {
- total_time += time_ms;
- boot_event_store->AddBootEventWithValue(
- "boottime.bootloader." + stageName, time_ms);
+ timings[stageName] = time_ms;
}
}
+ return timings;
+}
+
+// Parses and records the set of bootloader stages and associated boot times
+// from the ro.boot.boottime system property.
+void RecordBootloaderTimings(BootEventRecordStore* boot_event_store,
+ const BootloaderTimingMap& bootloader_timings) {
+ int32_t total_time = 0;
+ for (const auto& timing : bootloader_timings) {
+ total_time += timing.second;
+ boot_event_store->AddBootEventWithValue("boottime.bootloader." + timing.first, timing.second);
+ }
+
boot_event_store->AddBootEventWithValue("boottime.bootloader.total", total_time);
}
+// Records the closest estimation to the absolute device boot time, i.e.,
+// from power on to boot_complete, including bootloader times.
+void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,
+ const BootloaderTimingMap& bootloader_timings,
+ std::chrono::milliseconds uptime) {
+ int32_t bootloader_time_ms = 0;
+
+ for (const auto& timing : bootloader_timings) {
+ if (timing.first.compare("SW") != 0) {
+ bootloader_time_ms += timing.second;
+ }
+ }
+
+ auto bootloader_duration = std::chrono::milliseconds(bootloader_time_ms);
+ auto absolute_total =
+ std::chrono::duration_cast<std::chrono::seconds>(bootloader_duration + uptime);
+ boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total.count());
+}
+
// Records several metrics related to the time it takes to boot the device,
// including disambiguating boot time on encrypted or non-encrypted devices.
void RecordBootComplete() {
BootEventRecordStore boot_event_store;
BootEventRecordStore::BootEventRecord record;
- time_t uptime = bootstat::ParseUptime();
+ auto time_since_epoch = android::base::boot_clock::now().time_since_epoch();
+ auto uptime = std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch);
time_t current_time_utc = time(nullptr);
if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
@@ -282,36 +684,52 @@
// Log the amount of time elapsed until the device is decrypted, which
// includes the variable amount of time the user takes to enter the
// decryption password.
- boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime);
+ boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime.count());
// Subtract the decryption time to normalize the boot cycle timing.
- time_t boot_complete = uptime - record.second;
+ std::chrono::seconds boot_complete = std::chrono::seconds(uptime.count() - record.second);
boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt",
- boot_complete);
-
-
+ boot_complete.count());
} else {
- boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
- uptime);
+ boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
+ uptime.count());
}
// Record the total time from device startup to boot complete, regardless of
// encryption state.
- boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime);
+ boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime.count());
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init");
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.selinux");
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait");
- RecordBootloaderTimings(&boot_event_store);
+ const BootloaderTimingMap bootloader_timings = GetBootLoaderTimings();
+ RecordBootloaderTimings(&boot_event_store, bootloader_timings);
+
+ auto uptime_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_since_epoch);
+ RecordAbsoluteBootTime(&boot_event_store, bootloader_timings, uptime_ms);
}
// Records the boot_reason metric by querying the ro.boot.bootreason system
// property.
void RecordBootReason() {
- int32_t boot_reason = BootReasonStrToEnum(GetProperty("ro.boot.bootreason"));
+ const std::string reason(GetProperty(bootloader_reboot_reason_property));
+
+ // Log the raw bootloader_boot_reason property value.
+ int32_t boot_reason = BootReasonStrToEnum(reason);
BootEventRecordStore boot_event_store;
boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
+
+ // Log the scrubbed system_boot_reason.
+ const std::string system_reason(BootReasonStrToReason(reason));
+ int32_t system_boot_reason = BootReasonStrToEnum(system_reason);
+ boot_event_store.AddBootEventWithValue("system_boot_reason", system_boot_reason);
+
+ // Record the scrubbed system_boot_reason to the property
+ SetProperty(system_reboot_reason_property, system_reason);
+ if (reason == "") {
+ SetProperty(bootloader_reboot_reason_property, system_reason);
+ }
}
// Records two metrics related to the user resetting a device: the time at
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index f4756d5..410b854 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -1,7 +1,43 @@
# This file is the LOCAL_INIT_RC file for the bootstat command.
+# mirror bootloader boot reason to system boot reason
+on property:ro.boot.bootreason=*
+ setprop sys.boot.reason ${ro.boot.bootreason}
+
on post-fs-data
- mkdir /data/misc/bootstat 0700 root root
+ mkdir /data/misc/bootstat 0700 system log
+ # To deal with ota transition resulting from a change in DAC from
+ # root.root to system.log, may be deleted after ota has settled.
+ chown system log /data/misc/bootstat/absolute_boot_time
+ chown system log /data/misc/bootstat/boot_complete
+ chown system log /data/misc/bootstat/boot_complete_no_encryption
+ chown system log /data/misc/bootstat/boot_reason
+ chown system log /data/misc/bootstat/bootime.bootloader.1BLE
+ chown system log /data/misc/bootstat/bootime.bootloader.1BLL
+ chown system log /data/misc/bootstat/bootime.bootloader.2BLE
+ chown system log /data/misc/bootstat/bootime.bootloader.2BLL
+ chown system log /data/misc/bootstat/bootime.bootloader.AVB
+ chown system log /data/misc/bootstat/bootime.bootloader.KD
+ chown system log /data/misc/bootstat/bootime.bootloader.KL
+ chown system log /data/misc/bootstat/bootime.bootloader.ODT
+ chown system log /data/misc/bootstat/bootime.bootloader.SW
+ chown system log /data/misc/bootstat/bootime.bootloader.total
+ chown system log /data/misc/bootstat/build_date
+ chown system log /data/misc/bootstat/factory_reset
+ chown system log /data/misc/bootstat/factory_reset_boot_complete
+ chown system log /data/misc/bootstat/factory_reset_boot_complete_no_encryption
+ chown system log /data/misc/bootstat/factory_reset_current_time
+ chown system log /data/misc/bootstat/factory_reset_record_value
+ chown system log /data/misc/bootstat/last_boot_time_utc
+ chown system log /data/misc/bootstat/ota_boot_complete
+ chown system log /data/misc/bootstat/ota_boot_complete_no_encryption
+ chown system log /data/misc/bootstat/post_decrypt_time_elapsed
+ chown system log /data/misc/bootstat/ro.boottime.init
+ chown system log /data/misc/bootstat/ro.boottime.init.cold_boot_wait
+ chown system log /data/misc/bootstat/ro.boottime.init.selinux
+ chown system log /data/misc/bootstat/time_since_factory_reset
+ chown system log /data/misc/bootstat/time_since_last_boot
+ # end ota transitional support
# Record the time at which the user has successfully entered the pin to decrypt
# the device, /data is decrypted, and the system is entering the main boot phase.
@@ -10,7 +46,7 @@
# property:init.svc.bootanim=running: The boot animation is running
# property:ro.crypto.type=block: FDE device
on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block
- exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed
+ exec - system log -- /system/bin/bootstat -r post_decrypt_time_elapsed
# sys.logbootcomplete is a signal to enable the bootstat logging mechanism.
# This signaling is necessary to prevent logging boot metrics after a runtime
@@ -33,13 +69,13 @@
# Record boot complete metrics.
on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
# Record boot_complete and related stats (decryption, etc).
- exec - root root -- /system/bin/bootstat --record_boot_complete
+ exec - system log -- /system/bin/bootstat --record_boot_complete
# Record the boot reason.
- exec - root root -- /system/bin/bootstat --record_boot_reason
+ exec - system log -- /system/bin/bootstat --record_boot_reason
# Record time since factory reset.
- exec - root root -- /system/bin/bootstat --record_time_since_factory_reset
+ exec - system log -- /system/bin/bootstat --record_time_since_factory_reset
# Log all boot events.
- exec - root root -- /system/bin/bootstat -l
+ exec - system log -- /system/bin/bootstat -l
diff --git a/bootstat/uptime_parser.cpp b/bootstat/uptime_parser.cpp
deleted file mode 100644
index 7c2034c..0000000
--- a/bootstat/uptime_parser.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2016 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 "uptime_parser.h"
-
-#include <time.h>
-#include <cstdlib>
-#include <string>
-#include <android-base/file.h>
-#include <android-base/logging.h>
-
-namespace bootstat {
-
-time_t ParseUptime() {
- std::string uptime_str;
- if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
- PLOG(ERROR) << "Failed to read /proc/uptime";
- return -1;
- }
-
- // Cast intentionally rounds down.
- return static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
-}
-
-} // namespace bootstat
\ No newline at end of file
diff --git a/bootstat/uptime_parser.h b/bootstat/uptime_parser.h
deleted file mode 100644
index 756ae9b..0000000
--- a/bootstat/uptime_parser.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2016 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 UPTIME_PARSER_H_
-#define UPTIME_PARSER_H_
-
-#include <time.h>
-
-namespace bootstat {
-
-// Returns the number of seconds the system has been on since reboot.
-time_t ParseUptime();
-
-} // namespace bootstat
-
-#endif // UPTIME_PARSER_H_
\ No newline at end of file
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 4783d6e..2b5f4f6 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -1,6 +1,5 @@
cc_defaults {
name: "debuggerd_defaults",
- defaults: ["linux_bionic_supported"],
cflags: [
"-Wall",
"-Wextra",
@@ -9,27 +8,56 @@
"-Os",
],
- // util.cpp gets async signal safe logging via libc_logging,
- // which defines its interface in bionic private headers.
- include_dirs: ["bionic/libc"],
-
local_include_dirs: ["include"],
}
+cc_library_headers {
+ name: "libdebuggerd_common_headers",
+ export_include_dirs: ["common/include"]
+}
+
+cc_library_shared {
+ name: "libtombstoned_client",
+ defaults: ["debuggerd_defaults"],
+ srcs: [
+ "tombstoned/tombstoned_client.cpp",
+ "util.cpp",
+ ],
+
+ header_libs: ["libdebuggerd_common_headers"],
+
+ static_libs: [
+ "libasync_safe",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ ],
+
+ export_header_lib_headers: ["libdebuggerd_common_headers"],
+ export_include_dirs: ["tombstoned/include"]
+}
+
// Utility library to tombstoned and get an output fd.
cc_library_static {
- name: "libtombstoned_client",
+ name: "libtombstoned_client_static",
defaults: ["debuggerd_defaults"],
srcs: [
- "tombstoned_client.cpp",
+ "tombstoned/tombstoned_client.cpp",
"util.cpp",
],
+ header_libs: ["libdebuggerd_common_headers"],
+
whole_static_libs: [
- "libc_logging",
+ "libasync_safe",
"libcutils",
"libbase",
],
+
+ export_header_lib_headers: ["libdebuggerd_common_headers"],
+ export_include_dirs: ["tombstoned/include"]
}
// Core implementation, linked into libdebuggerd_handler and the dynamic linker.
@@ -38,11 +66,17 @@
defaults: ["debuggerd_defaults"],
srcs: ["handler/debuggerd_handler.cpp"],
+ header_libs: [
+ "libbase_headers",
+ "libdebuggerd_common_headers",
+ ],
+
whole_static_libs: [
- "libc_logging",
+ "libasync_safe",
"libdebuggerd",
],
+ export_header_lib_headers: ["libdebuggerd_common_headers"],
export_include_dirs: ["include"],
}
@@ -69,11 +103,13 @@
whole_static_libs: [
"libdebuggerd_handler_core",
- "libtombstoned_client",
+ "libtombstoned_client_static",
+ "libasync_safe",
"libbase",
"libdebuggerd",
"libbacktrace",
"libunwind",
+ "libunwindstack",
"liblzma",
"libcutils",
],
@@ -89,11 +125,14 @@
"util.cpp",
],
+ header_libs: ["libdebuggerd_common_headers"],
+
shared_libs: [
"libbase",
"libcutils",
],
+ export_header_lib_headers: ["libdebuggerd_common_headers"],
export_include_dirs: ["include"],
}
@@ -136,6 +175,7 @@
static_libs: [
"libbacktrace",
"libunwind",
+ "libunwindstack",
"liblzma",
"libbase",
"libcutils",
@@ -163,9 +203,8 @@
srcs: [
"client/debuggerd_client_test.cpp",
"debuggerd_test.cpp",
- "tombstoned_client.cpp",
- "util.cpp"
],
+ static_libs: ["libasync_safe", "libtombstoned_client_static"],
},
},
@@ -174,11 +213,12 @@
"libbase",
"libcutils",
"libdebuggerd_client",
+ "liblog",
+ "libnativehelper"
],
static_libs: [
"libdebuggerd",
- "libc_logging",
],
local_include_dirs: [
@@ -215,7 +255,7 @@
},
static_libs: [
- "libtombstoned_client",
+ "libtombstoned_client_static",
"libdebuggerd",
"libcutils",
],
@@ -225,7 +265,6 @@
"libbase",
"liblog",
"libprocinfo",
- "libselinux",
],
}
@@ -240,7 +279,7 @@
"libbase",
"libdebuggerd_client",
"liblog",
- "libselinux",
+ "libprocinfo",
],
local_include_dirs: ["include"],
@@ -255,6 +294,8 @@
],
defaults: ["debuggerd_defaults"],
+ header_libs: ["libdebuggerd_common_headers"],
+
static_libs: [
"libbase",
"libcutils",
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index d1df088..cb7cbbe 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -33,18 +33,21 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
-#include <debuggerd/handler.h>
-#include <debuggerd/protocol.h>
-#include <debuggerd/util.h>
+
+#include "debuggerd/handler.h"
+#include "protocol.h"
+#include "util.h"
using namespace std::chrono_literals;
using android::base::unique_fd;
-static bool send_signal(pid_t pid, bool backtrace) {
+static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
+ const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : DEBUGGER_SIGNAL;
sigval val;
- val.sival_int = backtrace;
- if (sigqueue(pid, DEBUGGER_SIGNAL, val) != 0) {
+ val.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0;
+
+ if (sigqueue(pid, signal, val) != 0) {
PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid;
return false;
}
@@ -59,8 +62,8 @@
tv->tv_usec = static_cast<long>(microseconds.count());
}
-bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
- unsigned int timeout_ms) {
+bool debuggerd_trigger_dump(pid_t pid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
+ unique_fd output_fd) {
LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
unique_fd sockfd;
const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
@@ -103,7 +106,7 @@
return false;
}
- InterceptRequest req = {.pid = pid };
+ InterceptRequest req = {.pid = pid, .dump_type = dump_type};
if (!set_timeout(sockfd)) {
PLOG(ERROR) << "libdebugger_client: failed to set timeout";
return false;
@@ -155,8 +158,9 @@
return false;
}
- bool backtrace = dump_type == kDebuggerdBacktrace;
- send_signal(pid, backtrace);
+ if (!send_signal(pid, dump_type)) {
+ return false;
+ }
rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
if (rc == 0) {
@@ -223,15 +227,16 @@
return true;
}
-int dump_backtrace_to_file(pid_t tid, int fd) {
- return dump_backtrace_to_file_timeout(tid, fd, 0);
+int dump_backtrace_to_file(pid_t tid, DebuggerdDumpType dump_type, int fd) {
+ return dump_backtrace_to_file_timeout(tid, dump_type, 0, fd);
}
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
+int dump_backtrace_to_file_timeout(pid_t tid, DebuggerdDumpType dump_type, int timeout_secs,
+ int fd) {
android::base::unique_fd copy(dup(fd));
if (copy == -1) {
return -1;
}
int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
- return debuggerd_trigger_dump(tid, std::move(copy), kDebuggerdBacktrace, timeout_ms) ? 0 : -1;
+ return debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
}
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index aff03e5..9c2f0d6 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -27,17 +27,30 @@
#include <gtest/gtest.h>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <debuggerd/util.h>
+#include "util.h"
using namespace std::chrono_literals;
using android::base::unique_fd;
+static int getThreadCount() {
+ int threadCount = 1024;
+ std::vector<std::string> characteristics =
+ android::base::Split(android::base::GetProperty("ro.build.characteristics", ""), ",");
+ if (std::find(characteristics.begin(), characteristics.end(), "embedded")
+ != characteristics.end()) {
+ // 128 is the realistic number for iot devices.
+ threadCount = 128;
+ }
+ return threadCount;
+}
+
TEST(debuggerd_client, race) {
- static constexpr int THREAD_COUNT = 1024;
+ static int THREAD_COUNT = getThreadCount();
pid_t forkpid = fork();
ASSERT_NE(-1, forkpid);
@@ -67,7 +80,8 @@
// Wait for a bit to let the child spawn all of its threads.
std::this_thread::sleep_for(250ms);
- ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(pipe_write), kDebuggerdBacktrace, 10000));
+ ASSERT_TRUE(
+ debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 10000, std::move(pipe_write)));
// Immediately kill the forked child, to make sure that the dump didn't return early.
ASSERT_EQ(0, kill(forkpid, SIGKILL)) << strerror(errno);
@@ -107,5 +121,6 @@
unique_fd output_read, output_write;
ASSERT_TRUE(Pipe(&output_read, &output_write));
- ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(output_write), kDebuggerdBacktrace, 0));
+ ASSERT_TRUE(
+ debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 0, std::move(output_write)));
}
diff --git a/debuggerd/common/include/dump_type.h b/debuggerd/common/include/dump_type.h
new file mode 100644
index 0000000..203269e
--- /dev/null
+++ b/debuggerd/common/include/dump_type.h
@@ -0,0 +1,49 @@
+#pragma once
+
+/*
+ * Copyright 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 <sys/types.h>
+
+#include <ostream>
+
+enum DebuggerdDumpType : uint8_t {
+ kDebuggerdNativeBacktrace,
+ kDebuggerdTombstone,
+ kDebuggerdJavaBacktrace,
+ kDebuggerdAnyIntercept
+};
+
+inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) {
+ switch (rhs) {
+ case kDebuggerdNativeBacktrace:
+ stream << "kDebuggerdNativeBacktrace";
+ break;
+ case kDebuggerdTombstone:
+ stream << "kDebuggerdTombstone";
+ break;
+ case kDebuggerdJavaBacktrace:
+ stream << "kDebuggerdJavaBacktrace";
+ break;
+ case kDebuggerdAnyIntercept:
+ stream << "kDebuggerdAnyIntercept";
+ break;
+ default:
+ stream << "[unknown]";
+ }
+
+ return stream;
+}
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 0a29844..6ef3ed6 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -43,16 +43,19 @@
#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <procinfo/process.h>
-#include <selinux/selinux.h>
-#include "backtrace.h"
-#include "tombstone.h"
-#include "utility.h"
+#define ATRACE_TAG ATRACE_TAG_BIONIC
+#include <utils/Trace.h>
+
+#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/tombstone.h"
+#include "libdebuggerd/utility.h"
#include "debuggerd/handler.h"
-#include "debuggerd/protocol.h"
-#include "debuggerd/tombstoned.h"
-#include "debuggerd/util.h"
+#include "tombstoned/tombstoned.h"
+
+#include "protocol.h"
+#include "util.h"
using android::base::unique_fd;
using android::base::ReadFileToString;
@@ -77,9 +80,27 @@
return fstatat(pid_proc_fd, task_path.c_str(), &st, 0) == 0;
}
+static pid_t get_tracer(pid_t tracee) {
+ // Check to see if the thread is being ptraced by another process.
+ android::procinfo::ProcessInfo process_info;
+ if (android::procinfo::GetProcessInfo(tracee, &process_info)) {
+ return process_info.tracer;
+ }
+ return -1;
+}
+
// 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) {
+ if (errno == EPERM) {
+ pid_t tracer = get_tracer(tid);
+ if (tracer != -1) {
+ *error = StringPrintf("failed to attach to thread %d, already traced by %d (%s)", tid,
+ tracer, get_process_name(tracer).c_str());
+ return false;
+ }
+ }
+
*error = StringPrintf("failed to attach to thread %d: %s", tid, strerror(errno));
return false;
}
@@ -102,6 +123,7 @@
}
static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data) {
+ ATRACE_CALL();
android::base::unique_fd amfd(socket_local_client(
"/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
if (amfd.get() == -1) {
@@ -153,13 +175,13 @@
_exit(1);
}
-static void abort_handler(pid_t target, const bool& tombstoned_connected,
+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)) {
+ 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);
@@ -177,6 +199,7 @@
}
static void drop_capabilities() {
+ ATRACE_CALL();
__user_cap_header_struct capheader;
memset(&capheader, 0, sizeof(capheader));
capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -195,6 +218,8 @@
}
int main(int argc, char** argv) {
+ atrace_begin(ATRACE_TAG, "before reparent");
+
pid_t target = getppid();
bool tombstoned_connected = false;
unique_fd tombstoned_socket;
@@ -210,18 +235,19 @@
action.sa_handler = signal_handler;
debuggerd_register_handlers(&action);
- if (argc != 3) {
- return 1;
- }
-
sigset_t mask;
sigemptyset(&mask);
if (sigprocmask(SIG_SETMASK, &mask, nullptr) != 0) {
PLOG(FATAL) << "failed to set signal mask";
}
+ if (argc != 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];
@@ -231,6 +257,10 @@
LOG(FATAL) << "invalid pseudothread tid: " << argv[2];
}
+ if (!android::base::ParseInt(argv[3], &dump_type, 0, 1)) {
+ LOG(FATAL) << "invalid requested dump type: " << argv[3];
+ }
+
if (target == 1) {
LOG(FATAL) << "target died before we could attach (received main tid = " << main_tid << ")";
}
@@ -257,6 +287,8 @@
PLOG(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.
pid_t forkpid = fork();
@@ -266,6 +298,8 @@
exit(0);
}
+ ATRACE_NAME("after reparent");
+
// Die if we take too long.
//
// Note: processes with many threads and minidebug-info can take a bit to
@@ -274,42 +308,58 @@
std::string attach_error;
- // Seize the main thread.
- if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
- LOG(FATAL) << attach_error;
- }
-
- // Seize the siblings.
std::map<pid_t, std::string> threads;
+
{
- std::set<pid_t> siblings;
- if (!android::procinfo::GetProcessTids(target, &siblings)) {
- PLOG(FATAL) << "failed to get process siblings";
+ ATRACE_NAME("ptrace");
+ // Seize the main thread.
+ if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
+ LOG(FATAL) << attach_error;
}
- // but not the already attached main thread.
- siblings.erase(main_tid);
- // or the handler pseudothread.
- siblings.erase(pseudothread_tid);
+ // Seize the siblings.
+ {
+ std::set<pid_t> siblings;
+ if (!android::procinfo::GetProcessTids(target, &siblings)) {
+ PLOG(FATAL) << "failed to get process siblings";
+ }
- 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));
+ // 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(BacktraceMap::Create(main_tid));
- if (!backtrace_map) {
- LOG(FATAL) << "failed to create backtrace map";
+ 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";
+ }
+ }
+ std::unique_ptr<BacktraceMap> backtrace_map_new;
+ backtrace_map_new.reset(BacktraceMap::CreateNew(main_tid));
+ if (!backtrace_map_new) {
+ LOG(FATAL) << "failed to create backtrace map new";
}
// Collect the list of open files.
OpenFilesList open_files;
- populate_open_files_list(target, &open_files);
+ {
+ ATRACE_NAME("open files");
+ populate_open_files_list(target, &open_files);
+ }
std::string process_name = get_process_name(main_tid);
threads.emplace(main_tid, get_thread_name(main_tid));
@@ -317,8 +367,12 @@
// Drop our capabilities now that we've attached to the threads we care about.
drop_capabilities();
- LOG(INFO) << "obtaining output fd from tombstoned";
- tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd);
+ {
+ 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);
+ }
// Write a '\1' to stdout to tell the crashing process to resume.
// It also restores the value of PR_SET_DUMPABLE at this point.
@@ -347,9 +401,12 @@
}
siginfo_t siginfo = {};
- if (!wait_for_signal(main_tid, &siginfo)) {
- printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
- exit(1);
+ {
+ 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);
+ }
}
int signo = siginfo.si_signo;
@@ -371,10 +428,13 @@
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);
} else {
- engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
- process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr);
+ ATRACE_NAME("engrave_tombstone");
+ engrave_tombstone(output_fd.get(), backtrace_map.get(), backtrace_map_new.get(), &open_files,
+ target, main_tid, process_name, threads, abort_address,
+ fatal_signal ? &amfd_data : nullptr);
}
// We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
@@ -402,14 +462,14 @@
if (wait_for_gdb) {
// Use ALOGI to line up with output from engrave_tombstone.
ALOGI(
- "***********************************************************\n"
- "* Process %d has been suspended while crashing.\n"
- "* To attach gdbserver and start gdb, run this on the host:\n"
- "*\n"
- "* gdbclient.py -p %d\n"
- "*\n"
- "***********************************************************",
- target, main_tid);
+ "***********************************************************\n"
+ "* Process %d has been suspended while crashing.\n"
+ "* To attach gdbserver and start gdb, run this on the host:\n"
+ "*\n"
+ "* gdbclient.py -p %d\n"
+ "*\n"
+ "***********************************************************",
+ target, target);
}
if (fatal_signal) {
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index f73f672..b7b1938 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -2,7 +2,6 @@
name: "crasher-defaults",
cppflags: [
- "-std=gnu++14",
"-W",
"-Wall",
"-Wextra",
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 6970201..f57349b 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -134,6 +134,14 @@
free(buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
}
+noinline void leak() {
+ while (true) {
+ void* mapping =
+ mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ static_cast<volatile char*>(mapping)[0] = 'a';
+ }
+}
+
noinline void sigsegv_non_null() {
int* a = (int *)(&do_action);
*a = 42;
@@ -160,8 +168,8 @@
fprintf(stderr, " stack-overflow recurse until the stack overflows\n");
fprintf(stderr, " nostack crash with a NULL stack pointer\n");
fprintf(stderr, "\n");
- fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n");
fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n");
+ fprintf(stderr, " leak leak memory until we get OOM-killed\n");
fprintf(stderr, "\n");
fprintf(stderr, " abort call abort()\n");
fprintf(stderr, " assert call assert() without a function\n");
@@ -265,6 +273,8 @@
return pthread_join(0, nullptr);
} else if (!strcasecmp(arg, "heap-usage")) {
abuse_heap();
+ } else if (!strcasecmp(arg, "leak")) {
+ leak();
} else if (!strcasecmp(arg, "SIGSEGV-unmapped")) {
char* map = reinterpret_cast<char*>(mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0));
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 492e9f0..b016e23 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -27,8 +27,8 @@
#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
#include <debuggerd/client.h>
-#include <debuggerd/util.h>
-#include <selinux/selinux.h>
+#include <procinfo/process.h>
+#include "util.h"
using android::base::unique_fd;
@@ -66,14 +66,32 @@
usage(1);
}
+ if (getuid() != 0) {
+ errx(1, "root is required");
+ }
+
+ // Check to see if the process exists and that we can actually send a signal to it.
+ android::procinfo::ProcessInfo proc_info;
+ if (!android::procinfo::GetProcessInfo(pid, &proc_info)) {
+ err(1, "failed to fetch info for process %d", pid);
+ }
+
+ if (proc_info.state == android::procinfo::kProcessStateZombie) {
+ errx(1, "process %d is a zombie", pid);
+ }
+
+ if (kill(pid, 0) != 0) {
+ err(1, "cannot send signal to process %d", pid);
+ }
+
unique_fd piperead, pipewrite;
if (!Pipe(&piperead, &pipewrite)) {
err(1, "failed to create pipe");
}
std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
- if (!debuggerd_trigger_dump(pid, std::move(pipewrite),
- backtrace_only ? kDebuggerdBacktrace : kDebuggerdTombstone, 0)) {
+ if (!debuggerd_trigger_dump(pid, backtrace_only ? kDebuggerdNativeBacktrace : kDebuggerdTombstone,
+ 0, std::move(pipewrite))) {
redirect_thread.join();
errx(1, "failed to dump process %d", pid);
}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index fa0626b..dbf81a4 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -16,10 +16,11 @@
#include <err.h>
#include <fcntl.h>
-#include <unistd.h>
#include <sys/capability.h>
#include <sys/prctl.h>
+#include <sys/ptrace.h>
#include <sys/types.h>
+#include <unistd.h>
#include <chrono>
#include <regex>
@@ -35,12 +36,13 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
-#include <debuggerd/handler.h>
-#include <debuggerd/protocol.h>
-#include <debuggerd/tombstoned.h>
-#include <debuggerd/util.h>
#include <gtest/gtest.h>
+#include "debuggerd/handler.h"
+#include "protocol.h"
+#include "tombstoned/tombstoned.h"
+#include "util.h"
+
using namespace std::chrono_literals;
using android::base::unique_fd;
@@ -87,14 +89,19 @@
} \
} while (0)
-static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd) {
+#define ASSERT_BACKTRACE_FRAME(result, frame_name) \
+ ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX \
+ R"(/libc.so \()" frame_name R"(\+)")
+
+static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
+ InterceptStatus* status, DebuggerdDumpType intercept_type) {
intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
if (intercept_fd->get() == -1) {
FAIL() << "failed to contact tombstoned: " << strerror(errno);
}
- InterceptRequest req = {.pid = target_pid};
+ InterceptRequest req = {.pid = target_pid, .dump_type = intercept_type};
unique_fd output_pipe_write;
if (!Pipe(output_fd, &output_pipe_write)) {
@@ -134,7 +141,7 @@
<< ", received " << rc;
}
- ASSERT_EQ(InterceptStatus::kRegistered, response.status);
+ *status = response.status;
}
class CrasherTest : public ::testing::Test {
@@ -147,7 +154,7 @@
CrasherTest();
~CrasherTest();
- void StartIntercept(unique_fd* output_fd);
+ void StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type = kDebuggerdTombstone);
// Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
void FinishIntercept(int* result);
@@ -173,12 +180,14 @@
android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
}
-void CrasherTest::StartIntercept(unique_fd* output_fd) {
+void CrasherTest::StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type) {
if (crasher_pid == -1) {
FAIL() << "crasher hasn't been started";
}
- tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd);
+ InterceptStatus status;
+ tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &status, intercept_type);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
}
void CrasherTest::FinishIntercept(int* result) {
@@ -303,7 +312,7 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
- ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+ ASSERT_BACKTRACE_FRAME(result, "abort");
}
TEST_F(CrasherTest, signal) {
@@ -429,7 +438,7 @@
StartProcess([]() {
abort();
});
- StartIntercept(&output_fd);
+ StartIntercept(&output_fd, kDebuggerdNativeBacktrace);
std::this_thread::sleep_for(500ms);
@@ -439,7 +448,7 @@
FinishIntercept(&intercept_result);
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
ConsumeFd(std::move(output_fd), &result);
- ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
+ ASSERT_BACKTRACE_FRAME(result, "read");
int status;
ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
@@ -450,7 +459,7 @@
FinishIntercept(&intercept_result);
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
ConsumeFd(std::move(output_fd), &result);
- ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+ ASSERT_BACKTRACE_FRAME(result, "abort");
}
TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
@@ -470,7 +479,7 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
- ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+ ASSERT_BACKTRACE_FRAME(result, "abort");
}
TEST_F(CrasherTest, capabilities) {
@@ -527,7 +536,7 @@
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
ConsumeFd(std::move(output_fd), &result);
ASSERT_MATCH(result, R"(name: thread_name\s+>>> .+debuggerd_test(32|64) <<<)");
- ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+ ASSERT_BACKTRACE_FRAME(result, "tgkill");
}
TEST_F(CrasherTest, fake_pid) {
@@ -558,7 +567,41 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
- ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+ ASSERT_BACKTRACE_FRAME(result, "tgkill");
+}
+
+TEST_F(CrasherTest, competing_tracer) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ while (true) {
+ }
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+
+ ASSERT_EQ(0, ptrace(PTRACE_SEIZE, crasher_pid, 0, 0));
+ ASSERT_EQ(0, kill(crasher_pid, SIGABRT));
+
+ int status;
+ ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+ ASSERT_EQ(SIGABRT, WSTOPSIG(status));
+
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, crasher_pid, 0, SIGABRT));
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ std::string regex = R"(failed to attach to thread \d+, already traced by )";
+ regex += std::to_string(gettid());
+ regex += R"( \(.+debuggerd_test)";
+ ASSERT_MATCH(result, regex.c_str());
+
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, crasher_pid, 0, SIGABRT));
+ AssertDeath(SIGABRT);
}
TEST(crash_dump, zombie) {
@@ -596,11 +639,13 @@
pid_t pid = 123'456'789 + i;
unique_fd intercept_fd, output_fd;
- tombstoned_intercept(pid, &intercept_fd, &output_fd);
+ InterceptStatus status;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
{
unique_fd tombstoned_socket, input_fd;
- ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+ ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone));
ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
}
@@ -628,7 +673,9 @@
pid_t pid = pid_base + dump;
unique_fd intercept_fd, output_fd;
- tombstoned_intercept(pid, &intercept_fd, &output_fd);
+ InterceptStatus status;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
// Pretend to crash, and then immediately close the socket.
unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
@@ -659,11 +706,13 @@
pid_t pid = pid_base + dump;
unique_fd intercept_fd, output_fd;
- tombstoned_intercept(pid, &intercept_fd, &output_fd);
+ InterceptStatus status;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
{
unique_fd tombstoned_socket, input_fd;
- ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+ ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone));
ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
tombstoned_notify_completion(tombstoned_socket.get());
}
@@ -683,3 +732,65 @@
thread.join();
}
}
+
+TEST(tombstoned, java_trace_intercept_smoke) {
+ // Using a "real" PID is a little dangerous here - if the test fails
+ // or crashes, we might end up getting a bogus / unreliable stack
+ // trace.
+ const pid_t self = getpid();
+
+ unique_fd intercept_fd, output_fd;
+ InterceptStatus status;
+ tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+ // First connect to tombstoned requesting a native backtrace. This
+ // should result in a "regular" FD and not the installed intercept.
+ const char native[] = "native";
+ unique_fd tombstoned_socket, input_fd;
+ ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdNativeBacktrace));
+ ASSERT_TRUE(android::base::WriteFully(input_fd.get(), native, sizeof(native)));
+ tombstoned_notify_completion(tombstoned_socket.get());
+
+ // Then, connect to tombstoned asking for a java backtrace. This *should*
+ // trigger the intercept.
+ const char java[] = "java";
+ ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdJavaBacktrace));
+ ASSERT_TRUE(android::base::WriteFully(input_fd.get(), java, sizeof(java)));
+ tombstoned_notify_completion(tombstoned_socket.get());
+
+ char outbuf[sizeof(java)];
+ ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+ ASSERT_STREQ("java", outbuf);
+}
+
+TEST(tombstoned, multiple_intercepts) {
+ const pid_t fake_pid = 1'234'567;
+ unique_fd intercept_fd, output_fd;
+ InterceptStatus status;
+ tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+ unique_fd intercept_fd_2, output_fd_2;
+ tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &status, kDebuggerdNativeBacktrace);
+ ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, status);
+}
+
+TEST(tombstoned, intercept_any) {
+ const pid_t fake_pid = 1'234'567;
+
+ unique_fd intercept_fd, output_fd;
+ InterceptStatus status;
+ tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdNativeBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+ const char any[] = "any";
+ unique_fd tombstoned_socket, input_fd;
+ ASSERT_TRUE(tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdAnyIntercept));
+ ASSERT_TRUE(android::base::WriteFully(input_fd.get(), any, sizeof(any)));
+ tombstoned_notify_completion(tombstoned_socket.get());
+
+ char outbuf[sizeof(any)];
+ ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+ ASSERT_STREQ("any", outbuf);
+}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 5c6c59c..06d4a9b 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -39,15 +39,14 @@
#include <android-base/file.h>
#include <android-base/unique_fd.h>
+#include <async_safe/log.h>
#include "debuggerd/handler.h"
-#include "debuggerd/tombstoned.h"
-#include "debuggerd/util.h"
+#include "tombstoned/tombstoned.h"
+#include "util.h"
-#include "backtrace.h"
-#include "tombstone.h"
-
-#include "private/libc_logging.h"
+#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/tombstone.h"
using android::base::unique_fd;
@@ -81,7 +80,7 @@
DIR* dir = opendir(buf);
if (!dir) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
return;
}
@@ -145,13 +144,14 @@
static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
int ret = pthread_mutex_trylock(&trace_mutex);
if (ret != 0) {
- __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s", strerror(ret));
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s",
+ strerror(ret));
return;
}
// Fetch output fd from tombstoned.
unique_fd tombstone_socket, output_fd;
- if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd)) {
+ if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdNativeBacktrace)) {
goto exit;
}
@@ -167,7 +167,8 @@
// receiving our signal.
unique_fd pipe_read, pipe_write;
if (!Pipe(&pipe_read, &pipe_write)) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s", strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
+ strerror(errno));
return false;
}
@@ -180,8 +181,8 @@
siginfo.si_uid = getuid();
if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s", tid,
- strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
+ tid, strerror(errno));
return false;
}
@@ -209,12 +210,13 @@
static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
int ret = pthread_mutex_lock(&crash_mutex);
if (ret != 0) {
- __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
return;
}
unique_fd tombstone_socket, output_fd;
- bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd);
+ bool tombstoned_connected =
+ tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdTombstone);
debuggerd_fallback_tombstone(output_fd.get(), ucontext, info, abort_message);
if (tombstoned_connected) {
tombstoned_notify_completion(tombstone_socket.get());
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index d58c73d..d41dc67 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -48,8 +48,12 @@
#include <sys/wait.h>
#include <unistd.h>
-#include "private/bionic_futex.h"
-#include "private/libc_logging.h"
+#include <android-base/unique_fd.h>
+#include <async_safe/log.h>
+
+#include "dump_type.h"
+
+using android::base::unique_fd;
// see man(2) prctl, specifically the section about PR_GET_NAME
#define MAX_TASK_NAME_LEN (16)
@@ -72,6 +76,10 @@
return syscall(__NR_gettid);
}
+static inline void futex_wait(volatile void* ftx, int value) {
+ syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0);
+}
+
class ErrnoRestorer {
public:
ErrnoRestorer() : saved_errno_(errno) {
@@ -92,11 +100,12 @@
// Mutex to ensure only one crashing thread dumps itself.
static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
-// Don't use __libc_fatal because it exits via abort, which might put us back into a signal handler.
+// Don't use async_safe_fatal because it exits via abort, which might put us back into
+// a signal handler.
static void __noreturn __printflike(1, 2) fatal(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
- __libc_format_log_va_list(ANDROID_LOG_FATAL, "libc", fmt, args);
+ async_safe_format_log_va_list(ANDROID_LOG_FATAL, "libc", fmt, args);
_exit(1);
}
@@ -106,10 +115,29 @@
va_start(args, fmt);
char buf[4096];
- __libc_format_buffer_va_list(buf, sizeof(buf), fmt, args);
+ async_safe_format_buffer_va_list(buf, sizeof(buf), fmt, args);
fatal("%s: %s", buf, strerror(err));
}
+static bool get_main_thread_name(char* buf, size_t len) {
+ unique_fd fd(open("/proc/self/comm", O_RDONLY | O_CLOEXEC));
+ if (fd == -1) {
+ return false;
+ }
+
+ ssize_t rc = read(fd, buf, len);
+ if (rc == -1) {
+ return false;
+ } else if (rc == 0) {
+ // Should never happen?
+ return false;
+ }
+
+ // There's a trailing newline, replace it with a NUL.
+ buf[rc - 1] = '\0';
+ return true;
+}
+
/*
* Writes a summary of the signal to the log file. We do this so that, if
* for some reason we're not able to contact debuggerd, there is still some
@@ -130,8 +158,8 @@
}
if (signum == DEBUGGER_SIGNAL) {
- __libc_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
- thread_name);
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
+ thread_name);
return;
}
@@ -176,14 +204,20 @@
char addr_desc[32]; // ", fault addr 0x1234"
addr_desc[0] = code_desc[0] = 0;
if (info != nullptr) {
- __libc_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
+ async_safe_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
if (has_address) {
- __libc_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
+ async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
}
}
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s)", signum,
- signal_name, code_desc, addr_desc, __gettid(), thread_name);
+ char main_thread_name[MAX_TASK_NAME_LEN + 1];
+ if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
+ strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
+ }
+
+ 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);
}
/*
@@ -192,8 +226,8 @@
static bool have_siginfo(int signum) {
struct sigaction old_action;
if (sigaction(signum, nullptr, &old_action) < 0) {
- __libc_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
- strerror(errno));
+ async_safe_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
+ strerror(errno));
return false;
}
return (old_action.sa_flags & SA_SIGINFO) != 0;
@@ -217,7 +251,7 @@
capdata[1].inheritable = capdata[1].permitted;
if (capset(&capheader, &capdata[0]) == -1) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "capset failed: %s", strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "capset failed: %s", strerror(errno));
}
}
@@ -227,8 +261,8 @@
for (unsigned long i = 0; i < 64; ++i) {
if (capmask & (1ULL << i)) {
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) != 0) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to raise ambient capability %lu: %s",
- i, strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "failed to raise ambient capability %lu: %s", i, strerror(errno));
}
}
}
@@ -249,6 +283,14 @@
// process.
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) {
+ return kDebuggerdNativeBacktrace;
+ }
+
+ return kDebuggerdTombstone;
+}
+
static int debuggerd_dispatch_pseudothread(void* arg) {
debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
@@ -262,58 +304,64 @@
TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO));
TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO));
- int pipefds[2];
- if (pipe(pipefds) != 0) {
+ unique_fd pipe_read, pipe_write;
+ if (!android::base::Pipe(&pipe_read, &pipe_write)) {
fatal_errno("failed to create pipe");
}
// Don't use fork(2) to avoid calling pthread_atfork handlers.
int forkpid = clone(nullptr, nullptr, 0, nullptr);
if (forkpid == -1) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to fork in debuggerd signal handler: %s",
- strerror(errno));
+ 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(pipefds[1], STDOUT_FILENO));
- close(pipefds[0]);
- close(pipefds[1]);
+ TEMP_FAILURE_RETRY(dup2(pipe_write.get(), STDOUT_FILENO));
+ pipe_write.reset();
+ pipe_read.reset();
raise_caps();
char main_tid[10];
char pseudothread_tid[10];
- __libc_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid);
- __libc_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d", thread_info->pseudothread_tid);
+ char debuggerd_dump_type[10];
+ async_safe_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid);
+ async_safe_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d",
+ thread_info->pseudothread_tid);
+ async_safe_format_buffer(debuggerd_dump_type, sizeof(debuggerd_dump_type), "%d",
+ get_dump_type(thread_info));
- execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, nullptr);
+ execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
+ nullptr);
fatal_errno("exec failed");
} else {
- close(pipefds[1]);
+ pipe_write.reset();
char buf[4];
- ssize_t rc = TEMP_FAILURE_RETRY(read(pipefds[0], &buf, sizeof(buf)));
+ ssize_t rc = TEMP_FAILURE_RETRY(read(pipe_read.get(), &buf, sizeof(buf)));
if (rc == -1) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno));
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s",
+ strerror(errno));
} else if (rc == 0) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
} else if (rc != 1) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc",
- "read of IPC pipe returned unexpected value: %zd", rc);
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+ "read of IPC pipe returned unexpected value: %zd", rc);
} else {
if (buf[0] != '\1') {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
} else {
thread_info->crash_dump_started = true;
}
}
- close(pipefds[0]);
+ pipe_read.reset();
// Don't leave a zombie child.
int status;
if (TEMP_FAILURE_RETRY(waitpid(forkpid, &status, 0)) == -1) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
- strerror(errno));
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
+ strerror(errno));
} else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
thread_info->crash_dump_started = false;
}
}
@@ -393,7 +441,7 @@
// Only allow one thread to handle a signal at a time.
int ret = pthread_mutex_lock(&crash_mutex);
if (ret != 0) {
- __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
return;
}
@@ -429,10 +477,10 @@
}
// Wait for the child to start...
- __futex_wait(&thread_info.pseudothread_tid, -1, nullptr);
+ futex_wait(&thread_info.pseudothread_tid, -1);
// and then wait for it to finish.
- __futex_wait(&thread_info.pseudothread_tid, child_pid, nullptr);
+ futex_wait(&thread_info.pseudothread_tid, child_pid);
// Restore PR_SET_DUMPABLE to its original value.
if (prctl(PR_SET_DUMPABLE, orig_dumpable) != 0) {
diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h
index 01de57b..b7284b0 100644
--- a/debuggerd/include/debuggerd/client.h
+++ b/debuggerd/include/debuggerd/client.h
@@ -22,15 +22,13 @@
#include <android-base/unique_fd.h>
-enum DebuggerdDumpType {
- kDebuggerdBacktrace,
- kDebuggerdTombstone,
-};
+#include "dump_type.h"
// Trigger a dump of specified process to output_fd.
// output_fd is consumed, timeout of 0 will wait forever.
-bool debuggerd_trigger_dump(pid_t pid, android::base::unique_fd output_fd,
- enum DebuggerdDumpType dump_type, unsigned int timeout_ms);
+bool debuggerd_trigger_dump(pid_t pid, enum DebuggerdDumpType dump_type, unsigned int timeout_ms,
+ android::base::unique_fd output_fd);
-int dump_backtrace_to_file(pid_t tid, int fd);
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
+int dump_backtrace_to_file(pid_t tid, enum DebuggerdDumpType dump_type, int output_fd);
+int dump_backtrace_to_file_timeout(pid_t tid, enum DebuggerdDumpType dump_type, int timeout_secs,
+ int output_fd);
diff --git a/debuggerd/libdebuggerd/arm/machine.cpp b/debuggerd/libdebuggerd/arm/machine.cpp
index ac833ae..bfb5ea4 100644
--- a/debuggerd/libdebuggerd/arm/machine.cpp
+++ b/debuggerd/libdebuggerd/arm/machine.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/machine.h"
+
#include <errno.h>
#include <stdint.h>
#include <string.h>
@@ -25,8 +27,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
pt_regs regs;
diff --git a/debuggerd/libdebuggerd/arm64/machine.cpp b/debuggerd/libdebuggerd/arm64/machine.cpp
index fa73c99a..ad1c951 100644
--- a/debuggerd/libdebuggerd/arm64/machine.cpp
+++ b/debuggerd/libdebuggerd/arm64/machine.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/machine.h"
+
#include <elf.h>
#include <errno.h>
#include <stdint.h>
@@ -27,8 +29,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
struct user_pt_regs regs;
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index 334d97f..f616e1b 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/backtrace.h"
+
#include <errno.h>
#include <dirent.h>
#include <limits.h>
@@ -34,9 +36,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "backtrace.h"
-
-#include "utility.h"
+#include "libdebuggerd/utility.h"
static void dump_process_header(log_t* log, pid_t pid, const char* process_name) {
time_t t = time(NULL);
diff --git a/debuggerd/libdebuggerd/elf_utils.cpp b/debuggerd/libdebuggerd/elf_utils.cpp
index 4e798e2..a35102f 100644
--- a/debuggerd/libdebuggerd/elf_utils.cpp
+++ b/debuggerd/libdebuggerd/elf_utils.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/elf_utils.h"
+
#include <elf.h>
#include <stdint.h>
#include <stdlib.h>
@@ -27,8 +29,6 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "elf_utils.h"
-
#define NOTE_ALIGN(size) (((size) + 3) & ~3)
template <typename HdrType, typename PhdrType, typename NhdrType>
diff --git a/debuggerd/libdebuggerd/include/backtrace.h b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/backtrace.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
diff --git a/debuggerd/libdebuggerd/include/elf_utils.h b/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/elf_utils.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
diff --git a/debuggerd/libdebuggerd/include/machine.h b/debuggerd/libdebuggerd/include/libdebuggerd/machine.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/machine.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/machine.h
diff --git a/debuggerd/libdebuggerd/include/open_files_list.h b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/open_files_list.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
diff --git a/debuggerd/libdebuggerd/include/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
similarity index 82%
rename from debuggerd/libdebuggerd/include/tombstone.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 79743b6..45740df 100644
--- a/debuggerd/libdebuggerd/include/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -35,10 +35,10 @@
int open_tombstone(std::string* path);
/* Creates a tombstone file and writes the crash dump to it. */
-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);
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, BacktraceMap* map_new,
+ 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);
void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
ucontext_t* ucontext);
diff --git a/debuggerd/libdebuggerd/include/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
similarity index 81%
rename from debuggerd/libdebuggerd/include/utility.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index e5e5106..f481b78 100644
--- a/debuggerd/libdebuggerd/include/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -24,26 +24,9 @@
#include <string>
+#include <android-base/macros.h>
#include <backtrace/Backtrace.h>
-// Figure out the abi based on defined macros.
-#if defined(__arm__)
-#define ABI_STRING "arm"
-#elif defined(__aarch64__)
-#define ABI_STRING "arm64"
-#elif defined(__mips__) && !defined(__LP64__)
-#define ABI_STRING "mips"
-#elif defined(__mips__) && defined(__LP64__)
-#define ABI_STRING "mips64"
-#elif defined(__i386__)
-#define ABI_STRING "x86"
-#elif defined(__x86_64__)
-#define ABI_STRING "x86_64"
-#else
-#error "Unsupported ABI"
-#endif
-
-
struct log_t{
// Tombstone file descriptor.
int tfd;
diff --git a/debuggerd/libdebuggerd/mips/machine.cpp b/debuggerd/libdebuggerd/mips/machine.cpp
index cbf272a..1fc690b 100644
--- a/debuggerd/libdebuggerd/mips/machine.cpp
+++ b/debuggerd/libdebuggerd/mips/machine.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/machine.h"
+
#include <errno.h>
#include <inttypes.h>
#include <stdint.h>
@@ -25,8 +27,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
#define R(x) (static_cast<uintptr_t>(x))
diff --git a/debuggerd/libdebuggerd/mips64/machine.cpp b/debuggerd/libdebuggerd/mips64/machine.cpp
index 0a8d532..955e507 100644
--- a/debuggerd/libdebuggerd/mips64/machine.cpp
+++ b/debuggerd/libdebuggerd/mips64/machine.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/machine.h"
+
#include <errno.h>
#include <inttypes.h>
#include <stdint.h>
@@ -25,8 +27,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
#define R(x) (static_cast<uintptr_t>(x))
diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
index 5c7ea70..e199db8 100644
--- a/debuggerd/libdebuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/open_files_list.h"
+
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
@@ -31,9 +33,7 @@
#include <android-base/file.h>
#include <log/log.h>
-#include "open_files_list.h"
-
-#include "utility.h"
+#include "libdebuggerd/utility.h"
void populate_open_files_list(pid_t pid, OpenFilesList* list) {
std::string fd_dir_name = "/proc/" + std::to_string(pid) + "/fd";
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
index 49f3690..0fad2cf 100644
--- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -22,9 +22,10 @@
#include <gtest/gtest.h>
#include <android-base/file.h>
+#include "libdebuggerd/utility.h"
+
#include "BacktraceMock.h"
#include "log_fake.h"
-#include "utility.h"
const char g_expected_full_dump[] =
"\nmemory near r1:\n"
diff --git a/debuggerd/libdebuggerd/test/elf_fake.cpp b/debuggerd/libdebuggerd/test/elf_fake.cpp
index bb52b59..f8cbca7 100644
--- a/debuggerd/libdebuggerd/test/elf_fake.cpp
+++ b/debuggerd/libdebuggerd/test/elf_fake.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "elf_fake.h"
+
#include <stdint.h>
#include <string>
diff --git a/debuggerd/libdebuggerd/test/log_fake.cpp b/debuggerd/libdebuggerd/test/log_fake.cpp
index 3336bcb..68f4013 100644
--- a/debuggerd/libdebuggerd/test/log_fake.cpp
+++ b/debuggerd/libdebuggerd/test/log_fake.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "log_fake.h"
+
#include <errno.h>
#include <stdarg.h>
diff --git a/debuggerd/libdebuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
index 85e0695..acac72c 100644
--- a/debuggerd/libdebuggerd/test/open_files_list_test.cpp
+++ b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
@@ -24,7 +24,7 @@
#include "android-base/test_utils.h"
-#include "open_files_list.h"
+#include "libdebuggerd/open_files_list.h"
// Check that we can produce a list of open files for the current process, and
// that it includes a known open file.
diff --git a/debuggerd/libdebuggerd/test/ptrace_fake.cpp b/debuggerd/libdebuggerd/test/ptrace_fake.cpp
index f40cbd4..0d4080e 100644
--- a/debuggerd/libdebuggerd/test/ptrace_fake.cpp
+++ b/debuggerd/libdebuggerd/test/ptrace_fake.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "ptrace_fake.h"
+
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
@@ -21,8 +23,6 @@
#include <string>
-#include "ptrace_fake.h"
-
siginfo_t g_fake_si = {.si_signo = 0};
void ptrace_set_fake_getsiginfo(const siginfo_t& si) {
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 325210d..e79dd96 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -22,7 +22,7 @@
#include <gtest/gtest.h>
#include <android-base/file.h>
-#include "utility.h"
+#include "libdebuggerd/utility.h"
#include "BacktraceMock.h"
#include "elf_fake.h"
@@ -220,21 +220,21 @@
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
- map.load_base = 0xd000;
+ map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
- map.load_base = 0x2000;
+ map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
- map.load_base = 0;
+ map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
@@ -244,20 +244,20 @@
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:\n"
+ const char* expected_dump =
+ "\nmemory map:\n"
#if defined(__LP64__)
-" 00000000'0a234000-00000000'0a234fff --- 0 1000\n"
-" 00000000'0a334000-00000000'0a334fff r-- f000 1000\n"
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ " 00000000'0a234000-00000000'0a234fff --- 0 1000\n"
+ " 00000000'0a334000-00000000'0a334fff r-- f000 1000\n"
+ " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
#else
-" 0a234000-0a234fff --- 0 1000\n"
-" 0a334000-0a334fff r-- f000 1000\n"
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ " 0a234000-0a234fff --- 0 1000\n"
+ " 0a334000-0a334fff r-- f000 1000\n"
+ " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -274,21 +274,21 @@
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
- map.load_base = 0xd000;
+ map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
- map.load_base = 0x2000;
+ map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
- map.load_base = 0;
+ map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
@@ -304,18 +304,18 @@
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: (fault address prefixed with --->)\n"
+ const char* expected_dump =
+ "\nmemory map: (fault address prefixed with --->)\n"
#if defined(__LP64__)
-"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ "--->Fault address falls at 00000000'00001000 before any mapped regions\n"
+ " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
#else
-"--->Fault address falls at 00001000 before any mapped regions\n"
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ "--->Fault address falls at 00001000 before any mapped regions\n"
+ " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -332,21 +332,21 @@
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
- map.load_base = 0xd000;
+ map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
- map.load_base = 0x2000;
+ map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
- map.load_base = 0;
+ map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
@@ -362,18 +362,18 @@
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: (fault address prefixed with --->)\n"
+ const char* expected_dump =
+ "\nmemory map: (fault address prefixed with --->)\n"
#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-"--->Fault address falls at 00000000'0a533000 between mapped regions\n"
-" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ "--->Fault address falls at 00000000'0a533000 between mapped regions\n"
+ " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
#else
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-"--->Fault address falls at 0a533000 between mapped regions\n"
-" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ "--->Fault address falls at 0a533000 between mapped regions\n"
+ " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -390,21 +390,21 @@
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
- map.load_base = 0xd000;
+ map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
- map.load_base = 0x2000;
+ map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
- map.load_base = 0;
+ map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
@@ -420,16 +420,16 @@
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: (fault address prefixed with --->)\n"
+ const char* expected_dump =
+ "\nmemory map: (fault address prefixed with --->)\n"
#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-"--->00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ "--->00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n";
#else
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-"--->0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
+ " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ "--->0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -446,21 +446,21 @@
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
- map.load_base = 0xd000;
+ map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
- map.load_base = 0x2000;
+ map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
- map.load_base = 0;
+ map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
@@ -480,18 +480,18 @@
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: (fault address prefixed with --->)\n"
+ const char* expected_dump =
+ "\nmemory map: (fault address prefixed with --->)\n"
#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"
-"--->Fault address falls at 00001234'5a534040 after any mapped regions\n";
+ " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"
+ "--->Fault address falls at 00001234'5a534040 after any mapped regions\n";
#else
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"
-" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n"
-" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"
-"--->Fault address falls at 0f534040 after any mapped regions\n";
+ " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n"
+ " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n"
+ " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"
+ "--->Fault address falls at 0f534040 after any mapped regions\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -508,7 +508,7 @@
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
- map.load_base = 0xd000;
+ map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
@@ -520,12 +520,12 @@
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:\n"
+ const char* expected_dump =
+ "\nmemory map:\n"
#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n";
+ " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n";
#else
-" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n";
+ " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index edc7be5..f5ecf48 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/tombstone.h"
+
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -43,22 +45,18 @@
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
+// Needed to get DEBUGGER_SIGNAL.
#include "debuggerd/handler.h"
-#include "backtrace.h"
-#include "elf_utils.h"
-#include "machine.h"
-#include "open_files_list.h"
-#include "tombstone.h"
+#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/elf_utils.h"
+#include "libdebuggerd/machine.h"
+#include "libdebuggerd/open_files_list.h"
using android::base::StringPrintf;
#define STACK_WORDS 16
-#define MAX_TOMBSTONES 10
-#define TOMBSTONE_DIR "/data/tombstones"
-#define TOMBSTONE_TEMPLATE (TOMBSTONE_DIR"/tombstone_%02d")
-
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) {
@@ -168,6 +166,26 @@
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;
}
@@ -446,11 +464,11 @@
line += " (BuildId: " + build_id + ")";
}
}
- if (it->load_base != 0) {
+ if (it->load_bias != 0) {
if (space_needed) {
line += ' ';
}
- line += StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base);
+ line += StringPrintf(" (load bias 0x%" PRIxPTR ")", it->load_bias);
}
_LOG(log, logtype::MAPS, "%s\n", line.c_str());
}
@@ -475,8 +493,55 @@
_LOG(log, logtype::REGISTERS, " register dumping unimplemented on this architecture");
}
-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,
+static bool verify_backtraces_equal(Backtrace* back1, Backtrace* back2) {
+ if (back1->NumFrames() != back2->NumFrames()) {
+ return false;
+ }
+ std::string back1_str;
+ std::string back2_str;
+ for (size_t i = 0; i < back1->NumFrames(); i++) {
+ back1_str += back1->FormatFrameData(i);
+ back2_str += back2->FormatFrameData(i);
+ }
+ return back1_str == back2_str;
+}
+
+static void log_mismatch_data(log_t* log, Backtrace* backtrace) {
+ _LOG(log, logtype::THREAD, "MISMATCH: This unwind is different.\n");
+ if (backtrace->NumFrames() == 0) {
+ _LOG(log, logtype::THREAD, "MISMATCH: No frames in new backtrace.\n");
+ return;
+ }
+ _LOG(log, logtype::THREAD, "MISMATCH: Backtrace from new unwinder.\n");
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ _LOG(log, logtype::THREAD, "MISMATCH: %s\n", backtrace->FormatFrameData(i).c_str());
+ }
+
+ // Get the stack trace up to 8192 bytes.
+ std::vector<uint64_t> buffer(8192 / sizeof(uint64_t));
+ size_t bytes =
+ backtrace->Read(backtrace->GetFrame(0)->sp, reinterpret_cast<uint8_t*>(buffer.data()),
+ buffer.size() * sizeof(uint64_t));
+ std::string log_data;
+ for (size_t i = 0; i < bytes / sizeof(uint64_t); i++) {
+ if ((i % 4) == 0) {
+ if (!log_data.empty()) {
+ _LOG(log, logtype::THREAD, "MISMATCH: stack_data%s\n", log_data.c_str());
+ log_data = "";
+ }
+ }
+ log_data += android::base::StringPrintf(" 0x%016" PRIx64, buffer[i]);
+ }
+
+ if (!log_data.empty()) {
+ _LOG(log, logtype::THREAD, "MISMATCH: data%s\n", log_data.c_str());
+ }
+
+ // If there is any leftover (bytes % sizeof(uint64_t) != 0, ignore it for now.
+}
+
+static bool dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name,
+ const std::string& thread_name, BacktraceMap* map, BacktraceMap* map_new,
uintptr_t abort_msg_address, bool primary_thread) {
log->current_tid = tid;
if (!primary_thread) {
@@ -490,7 +555,18 @@
dump_abort_message(backtrace.get(), log, abort_msg_address);
}
dump_registers(log, tid);
+ bool matches = true;
if (backtrace->Unwind(0)) {
+ // Use the new method and verify it is the same as old.
+ std::unique_ptr<Backtrace> backtrace_new(Backtrace::CreateNew(pid, tid, map_new));
+ if (!backtrace_new->Unwind(0)) {
+ _LOG(log, logtype::THREAD, "Failed to unwind with new unwinder: %s\n",
+ backtrace_new->GetErrorString(backtrace_new->GetError()).c_str());
+ matches = false;
+ } else if (!verify_backtraces_equal(backtrace.get(), backtrace_new.get())) {
+ log_mismatch_data(log, backtrace_new.get());
+ matches = false;
+ }
dump_backtrace_and_stack(backtrace.get(), log);
} else {
ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
@@ -504,6 +580,8 @@
}
log->current_tid = log->crashed_tid;
+
+ return matches;
}
// Reads the contents of the specified log device, filters out the entries
@@ -637,9 +715,10 @@
}
// 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) {
+static void dump_crash(log_t* log, BacktraceMap* map, BacktraceMap* map_new,
+ 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
char value[PROPERTY_VALUE_MAX];
property_get("ro.debuggable", value, "0");
@@ -648,7 +727,8 @@
_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);
+ bool new_unwind_matches = dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map,
+ map_new, abort_msg_address, true);
if (want_logs) {
dump_logs(log, pid, 5);
}
@@ -658,7 +738,9 @@
const std::string& thread_name = it.second;
if (thread_tid != tid) {
- dump_thread(log, pid, thread_tid, process_name, thread_name, map, 0, false);
+ bool match =
+ dump_thread(log, pid, thread_tid, process_name, thread_name, map, map_new, 0, false);
+ new_unwind_matches = new_unwind_matches && match;
}
}
@@ -670,71 +752,26 @@
if (want_logs) {
dump_logs(log, pid, 0);
}
+ if (!new_unwind_matches) {
+ _LOG(log, logtype::THREAD, "MISMATCH: New and old unwinder do not agree.\n");
+ _LOG(log, logtype::THREAD, "MISMATCH: If you see this please file a bug in:\n");
+ _LOG(log, logtype::THREAD,
+ "MISMATCH: Android > Android OS & Apps > Runtime > native > tools "
+ "(debuggerd/gdb/init/simpleperf/strace/valgrind)\n");
+ _LOG(log, logtype::THREAD, "MISMATCH: and attach this tombstone.\n");
+ }
}
-// open_tombstone - find an available tombstone slot, if any, of the
-// form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
-// file is available, we reuse the least-recently-modified file.
-int open_tombstone(std::string* out_path) {
- // In a single pass, find an available slot and, in case none
- // exist, find and record the least-recently-modified file.
- char path[128];
- int fd = -1;
- int oldest = -1;
- struct stat oldest_sb;
- for (int i = 0; i < MAX_TOMBSTONES; i++) {
- snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, i);
-
- struct stat sb;
- if (stat(path, &sb) == 0) {
- if (oldest < 0 || sb.st_mtime < oldest_sb.st_mtime) {
- oldest = i;
- oldest_sb.st_mtime = sb.st_mtime;
- }
- continue;
- }
- if (errno != ENOENT) continue;
-
- fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
- if (fd < 0) continue; // raced ?
-
- if (out_path) {
- *out_path = path;
- }
- fchown(fd, AID_SYSTEM, AID_SYSTEM);
- return fd;
- }
-
- if (oldest < 0) {
- ALOGE("debuggerd: failed to find a valid tombstone, default to using tombstone 0.\n");
- oldest = 0;
- }
-
- // we didn't find an available file, so we clobber the oldest one
- snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest);
- fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
- if (fd < 0) {
- ALOGE("debuggerd: failed to open tombstone file '%s': %s\n", path, strerror(errno));
- return -1;
- }
-
- if (out_path) {
- *out_path = path;
- }
- fchown(fd, AID_SYSTEM, AID_SYSTEM);
- return fd;
-}
-
-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) {
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, BacktraceMap* map_new,
+ 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);
+ dump_crash(&log, map, map_new, 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,
@@ -763,10 +800,22 @@
dump_abort_message(backtrace.get(), &log, abort_msg_address);
dump_registers(&log, ucontext);
- // TODO: Dump registers from the ucontext.
if (backtrace->Unwind(0, ucontext)) {
dump_backtrace_and_stack(backtrace.get(), &log);
} else {
ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
}
+
+ // 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);
+ }
+ }
}
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 22fde5e..1b74652 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -16,22 +16,28 @@
#define LOG_TAG "DEBUG"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/ptrace.h>
+#include <sys/uio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string>
+#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 <backtrace/Backtrace.h>
#include <log/log.h>
+using android::base::unique_fd;
+
// Whitelist output desired in the logcat output.
bool is_allowed_in_logcat(enum logtype ltype) {
if ((ltype == HEADER)
@@ -42,6 +48,19 @@
return false;
}
+static bool should_write_to_kmsg() {
+ // Write to kmsg if tombstoned isn't up, and we're able to do so.
+ if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+ return false;
+ }
+
+ if (android::base::GetProperty("init.svc.tombstoned", "") == "running") {
+ return false;
+ }
+
+ return true;
+}
+
__attribute__((__weak__, visibility("default")))
void _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) {
bool write_to_tombstone = (log->tfd != -1);
@@ -49,6 +68,7 @@
&& log->crashed_tid != -1
&& log->current_tid != -1
&& (log->crashed_tid == log->current_tid);
+ static bool write_to_kmsg = should_write_to_kmsg();
char buf[512];
va_list ap;
@@ -70,6 +90,30 @@
if (log->amfd_data != nullptr) {
*log->amfd_data += buf;
}
+
+ if (write_to_kmsg) {
+ unique_fd kmsg_fd(open("/dev/kmsg_debug", O_WRONLY | O_APPEND | O_CLOEXEC));
+ if (kmsg_fd.get() >= 0) {
+ // Our output might contain newlines which would otherwise be handled by the android logger.
+ // Split the lines up ourselves before sending to the kernel logger.
+ if (buf[len - 1] == '\n') {
+ buf[len - 1] = '\0';
+ }
+
+ std::vector<std::string> fragments = android::base::Split(buf, "\n");
+ for (const std::string& fragment : fragments) {
+ static constexpr char prefix[] = "<3>DEBUG: ";
+ struct iovec iov[3];
+ iov[0].iov_base = const_cast<char*>(prefix);
+ iov[0].iov_len = strlen(prefix);
+ iov[1].iov_base = const_cast<char*>(fragment.c_str());
+ iov[1].iov_len = fragment.length();
+ iov[2].iov_base = const_cast<char*>("\n");
+ iov[2].iov_len = 1;
+ TEMP_FAILURE_RETRY(writev(kmsg_fd.get(), iov, 3));
+ }
+ }
+ }
}
}
@@ -205,7 +249,7 @@
}
void read_with_default(const char* path, char* buf, size_t len, const char* default_value) {
- android::base::unique_fd fd(open(path, O_RDONLY));
+ unique_fd fd(open(path, O_RDONLY | O_CLOEXEC));
if (fd != -1) {
int rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, len - 1));
if (rc != -1) {
diff --git a/debuggerd/libdebuggerd/x86/machine.cpp b/debuggerd/libdebuggerd/x86/machine.cpp
index af10817..09a64cd 100644
--- a/debuggerd/libdebuggerd/x86/machine.cpp
+++ b/debuggerd/libdebuggerd/x86/machine.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/machine.h"
+
#include <errno.h>
#include <stdint.h>
#include <string.h>
@@ -24,8 +26,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
struct pt_regs r;
diff --git a/debuggerd/libdebuggerd/x86_64/machine.cpp b/debuggerd/libdebuggerd/x86_64/machine.cpp
index bf2c2b4..de1c268 100644
--- a/debuggerd/libdebuggerd/x86_64/machine.cpp
+++ b/debuggerd/libdebuggerd/x86_64/machine.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/machine.h"
+
#include <errno.h>
#include <stdint.h>
#include <string.h>
@@ -25,8 +27,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
struct user_regs_struct r;
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/protocol.h
similarity index 85%
rename from debuggerd/include/debuggerd/protocol.h
rename to debuggerd/protocol.h
index 0756876..7e1961e 100644
--- a/debuggerd/include/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -18,9 +18,12 @@
#include <stdint.h>
+#include "dump_type.h"
+
// Sockets in the ANDROID_SOCKET_NAMESPACE_RESERVED namespace.
// Both sockets are SOCK_SEQPACKET sockets, so no explicit length field is needed.
constexpr char kTombstonedCrashSocketName[] = "tombstoned_crash";
+constexpr char kTombstonedJavaTraceSocketName[] = "tombstoned_java_trace";
constexpr char kTombstonedInterceptSocketName[] = "tombstoned_intercept";
enum class CrashPacketType : uint8_t {
@@ -39,6 +42,7 @@
};
struct DumpRequest {
+ DebuggerdDumpType dump_type;
int32_t pid;
};
@@ -53,10 +57,15 @@
// Comes with a file descriptor via SCM_RIGHTS.
// This packet should be sent before an actual dump happens.
struct InterceptRequest {
+ DebuggerdDumpType dump_type;
int32_t pid;
};
enum class InterceptStatus : uint8_t {
+ // Returned when an intercept of a different type has already been
+ // registered (and is active) for a given PID.
+ kFailedAlreadyRegistered,
+ // Returned in all other failure cases.
kFailed,
kStarted,
kRegistered,
diff --git a/debuggerd/include/debuggerd/tombstoned.h b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
similarity index 87%
rename from debuggerd/include/debuggerd/tombstoned.h
rename to debuggerd/tombstoned/include/tombstoned/tombstoned.h
index d158d50..6403dbe 100644
--- a/debuggerd/include/debuggerd/tombstoned.h
+++ b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
@@ -20,7 +20,9 @@
#include <android-base/unique_fd.h>
+#include "dump_type.h"
+
bool tombstoned_connect(pid_t pid, android::base::unique_fd* tombstoned_socket,
- android::base::unique_fd* output_fd);
+ android::base::unique_fd* output_fd, DebuggerdDumpType dump_type);
bool tombstoned_notify_completion(int tombstoned_socket);
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index dff942c..24960bc 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -28,8 +28,8 @@
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
-#include "debuggerd/protocol.h"
-#include "debuggerd/util.h"
+#include "protocol.h"
+#include "util.h"
using android::base::unique_fd;
@@ -61,11 +61,24 @@
reason = "due to input";
}
- LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " terminated " << reason;
+ LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " and type "
+ << intercept->dump_type << " terminated: " << reason;
intercept_manager->intercepts.erase(it);
}
}
+static bool is_intercept_request_valid(const InterceptRequest& request) {
+ if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) {
+ return false;
+ }
+
+ if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) {
+ return false;
+ }
+
+ return true;
+}
+
static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
auto intercept = reinterpret_cast<Intercept*>(arg);
InterceptManager* intercept_manager = intercept->intercept_manager;
@@ -103,23 +116,24 @@
rcv_fd.reset(moved_fd);
// We trust the other side, so only do minimal validity checking.
- if (intercept_request.pid <= 0 || intercept_request.pid > std::numeric_limits<pid_t>::max()) {
+ if (!is_intercept_request_valid(intercept_request)) {
InterceptResponse response = {};
response.status = InterceptStatus::kFailed;
- snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
- intercept_request.pid);
+ snprintf(response.error_message, sizeof(response.error_message), "invalid intercept request");
TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
goto fail;
}
intercept->intercept_pid = intercept_request.pid;
+ intercept->dump_type = intercept_request.dump_type;
// Check if it's already registered.
if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
InterceptResponse response = {};
- response.status = InterceptStatus::kFailed;
+ response.status = InterceptStatus::kFailedAlreadyRegistered;
snprintf(response.error_message, sizeof(response.error_message),
- "pid %" PRId32 " already intercepted", intercept_request.pid);
+ "pid %" PRId32 " already intercepted, type %d", intercept_request.pid,
+ intercept_request.dump_type);
TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
LOG(WARNING) << response.error_message;
goto fail;
@@ -138,7 +152,8 @@
intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
intercept->registered = true;
- LOG(INFO) << "tombstoned registered intercept for pid " << intercept_request.pid;
+ LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type "
+ << intercept_request.dump_type;
// Register a different read event on the socket so that we can remove intercepts if the socket
// closes (e.g. if a user CTRL-C's the process that requested the intercept).
@@ -174,16 +189,27 @@
intercept_socket);
}
-bool InterceptManager::GetIntercept(pid_t pid, android::base::unique_fd* out_fd) {
+bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
+ android::base::unique_fd* out_fd) {
auto it = this->intercepts.find(pid);
if (it == this->intercepts.end()) {
return false;
}
+ if (dump_type == kDebuggerdAnyIntercept) {
+ LOG(INFO) << "found registered intercept of type " << it->second->dump_type
+ << " for requested type kDebuggerdAnyIntercept";
+ } else if (it->second->dump_type != dump_type) {
+ LOG(WARNING) << "found non-matching intercept of type " << it->second->dump_type
+ << " for requested type: " << dump_type;
+ return false;
+ }
+
auto intercept = std::move(it->second);
this->intercepts.erase(it);
- LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
+ LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid
+ << " and type " << intercept->dump_type;
InterceptResponse response = {};
response.status = InterceptStatus::kStarted;
TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
diff --git a/debuggerd/tombstoned/intercept_manager.h b/debuggerd/tombstoned/intercept_manager.h
index cb5db62..a11d565 100644
--- a/debuggerd/tombstoned/intercept_manager.h
+++ b/debuggerd/tombstoned/intercept_manager.h
@@ -25,6 +25,8 @@
#include <android-base/unique_fd.h>
+#include "dump_type.h"
+
struct InterceptManager;
struct Intercept {
@@ -39,6 +41,7 @@
pid_t intercept_pid = -1;
android::base::unique_fd output_fd;
bool registered = false;
+ DebuggerdDumpType dump_type = kDebuggerdNativeBacktrace;
};
struct InterceptManager {
@@ -50,5 +53,5 @@
InterceptManager(InterceptManager& copy) = delete;
InterceptManager(InterceptManager&& move) = delete;
- bool GetIntercept(pid_t pid, android::base::unique_fd* out_fd);
+ bool GetIntercept(pid_t pid, DebuggerdDumpType dump_type, android::base::unique_fd* out_fd);
};
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 0e5dcef..1bf8f14 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -23,23 +23,28 @@
#include <array>
#include <deque>
+#include <string>
#include <unordered_map>
+#include <utility>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/thread.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
#include "debuggerd/handler.h"
-#include "debuggerd/protocol.h"
-#include "debuggerd/util.h"
+#include "dump_type.h"
+#include "protocol.h"
+#include "util.h"
#include "intercept_manager.h"
+using android::base::GetIntProperty;
using android::base::StringPrintf;
using android::base::unique_fd;
@@ -54,82 +59,152 @@
// It's either owned by an active event that must have a timeout, or owned by
// queued_requests, in the case that multiple crashes come in at the same time.
struct Crash {
- ~Crash() {
- event_free(crash_event);
- }
+ ~Crash() { event_free(crash_event); }
unique_fd crash_fd;
pid_t crash_pid;
event* crash_event = nullptr;
std::string crash_path;
+
+ DebuggerdDumpType crash_type;
};
-static constexpr char kTombstoneDirectory[] = "/data/tombstones/";
-static constexpr size_t kTombstoneCount = 10;
-static int tombstone_directory_fd = -1;
-static int next_tombstone = 0;
+class CrashQueue {
+ public:
+ CrashQueue(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,
+ size_t max_concurrent_dumps)
+ : file_name_prefix_(file_name_prefix),
+ dir_path_(dir_path),
+ dir_fd_(open(dir_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)),
+ max_artifacts_(max_artifacts),
+ next_artifact_(0),
+ max_concurrent_dumps_(max_concurrent_dumps),
+ num_concurrent_dumps_(0) {
+ if (dir_fd_ == -1) {
+ PLOG(FATAL) << "failed to open directory: " << dir_path;
+ }
-static constexpr size_t kMaxConcurrentDumps = 1;
-static size_t num_concurrent_dumps = 0;
+ // NOTE: If max_artifacts_ <= max_concurrent_dumps_, then theoretically the
+ // same filename could be handed out to multiple processes.
+ CHECK(max_artifacts_ > max_concurrent_dumps_);
-static std::deque<Crash*> queued_requests;
+ find_oldest_artifact();
+ }
+
+ static CrashQueue* for_crash(const Crash* crash) {
+ return (crash->crash_type == kDebuggerdJavaBacktrace) ? for_anrs() : for_tombstones();
+ }
+
+ static CrashQueue* for_tombstones() {
+ static CrashQueue queue("/data/tombstones", "tombstone_" /* file_name_prefix */,
+ GetIntProperty("tombstoned.max_tombstone_count", 10),
+ 1 /* max_concurrent_dumps */);
+ return &queue;
+ }
+
+ static CrashQueue* for_anrs() {
+ static CrashQueue queue("/data/anr", "trace_" /* file_name_prefix */,
+ GetIntProperty("tombstoned.max_anr_count", 64),
+ 4 /* max_concurrent_dumps */);
+ return &queue;
+ }
+
+ std::pair<unique_fd, std::string> get_output() {
+ unique_fd result;
+ std::string file_name = StringPrintf("%s%02d", file_name_prefix_.c_str(), next_artifact_);
+
+ // Unlink and create the file, instead of using O_TRUNC, to avoid two processes
+ // interleaving their output in case we ever get into that situation.
+ if (unlinkat(dir_fd_, file_name.c_str(), 0) != 0 && errno != ENOENT) {
+ PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << "/" << file_name;
+ }
+
+ result.reset(openat(dir_fd_, file_name.c_str(),
+ O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
+ if (result == -1) {
+ PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << "/" << file_name;
+ }
+
+ next_artifact_ = (next_artifact_ + 1) % max_artifacts_;
+ return {std::move(result), dir_path_ + "/" + file_name};
+ }
+
+ bool maybe_enqueue_crash(Crash* crash) {
+ if (num_concurrent_dumps_ == max_concurrent_dumps_) {
+ queued_requests_.push_back(crash);
+ return true;
+ }
+
+ return false;
+ }
+
+ void maybe_dequeue_crashes(void (*handler)(Crash* crash)) {
+ while (!queued_requests_.empty() && num_concurrent_dumps_ < max_concurrent_dumps_) {
+ Crash* next_crash = queued_requests_.front();
+ queued_requests_.pop_front();
+ handler(next_crash);
+ }
+ }
+
+ void on_crash_started() { ++num_concurrent_dumps_; }
+
+ void on_crash_completed() { --num_concurrent_dumps_; }
+
+ private:
+ void find_oldest_artifact() {
+ size_t oldest_tombstone = 0;
+ time_t oldest_time = std::numeric_limits<time_t>::max();
+
+ for (size_t i = 0; i < max_artifacts_; ++i) {
+ std::string path = StringPrintf("%s/%s%02zu", dir_path_.c_str(), file_name_prefix_.c_str(), i);
+ struct stat st;
+ if (stat(path.c_str(), &st) != 0) {
+ if (errno == ENOENT) {
+ oldest_tombstone = i;
+ break;
+ } else {
+ PLOG(ERROR) << "failed to stat " << path;
+ continue;
+ }
+ }
+
+ if (st.st_mtime < oldest_time) {
+ oldest_tombstone = i;
+ oldest_time = st.st_mtime;
+ }
+ }
+
+ next_artifact_ = oldest_tombstone;
+ }
+
+ const std::string file_name_prefix_;
+
+ const std::string dir_path_;
+ const int dir_fd_;
+
+ const size_t max_artifacts_;
+ int next_artifact_;
+
+ const size_t max_concurrent_dumps_;
+ size_t num_concurrent_dumps_;
+
+ std::deque<Crash*> queued_requests_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashQueue);
+};
+
+// Whether java trace dumps are produced via tombstoned.
+static constexpr bool kJavaTraceDumpsEnabled = true;
// Forward declare the callbacks so they can be placed in a sensible order.
static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, void*);
static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg);
static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg);
-static void find_oldest_tombstone() {
- size_t oldest_tombstone = 0;
- time_t oldest_time = std::numeric_limits<time_t>::max();
-
- for (size_t i = 0; i < kTombstoneCount; ++i) {
- std::string path = android::base::StringPrintf("%stombstone_%02zu", kTombstoneDirectory, i);
- struct stat st;
- if (stat(path.c_str(), &st) != 0) {
- if (errno == ENOENT) {
- oldest_tombstone = i;
- break;
- } else {
- PLOG(ERROR) << "failed to stat " << path;
- continue;
- }
- }
-
- if (st.st_mtime < oldest_time) {
- oldest_tombstone = i;
- oldest_time = st.st_mtime;
- }
- }
-
- next_tombstone = oldest_tombstone;
-}
-
-static std::pair<unique_fd, std::string> get_tombstone() {
- // If kMaxConcurrentDumps is greater than 1, then theoretically the same
- // filename could be handed out to multiple processes. Unlink and create the
- // file, instead of using O_TRUNC, to avoid two processes interleaving their
- // output.
- unique_fd result;
- std::string file_name = StringPrintf("tombstone_%02d", next_tombstone);
- if (unlinkat(tombstone_directory_fd, file_name.c_str(), 0) != 0 && errno != ENOENT) {
- PLOG(FATAL) << "failed to unlink tombstone at " << kTombstoneDirectory << "/" << file_name;
- }
-
- result.reset(openat(tombstone_directory_fd, file_name.c_str(),
- O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
- if (result == -1) {
- PLOG(FATAL) << "failed to create tombstone at " << kTombstoneDirectory << "/" << file_name;
- }
-
- next_tombstone = (next_tombstone + 1) % kTombstoneCount;
- return {std::move(result), std::string(kTombstoneDirectory) + "/" + file_name};
-}
-
static void perform_request(Crash* crash) {
unique_fd output_fd;
- if (!intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
- std::tie(output_fd, crash->crash_path) = get_tombstone();
+ if (!intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd)) {
+ std::tie(output_fd, crash->crash_path) = CrashQueue::for_crash(crash)->get_output();
}
TombstonedCrashPacket response = {
@@ -152,26 +227,20 @@
event_add(crash->crash_event, &timeout);
}
- ++num_concurrent_dumps;
+ CrashQueue::for_crash(crash)->on_crash_started();
return;
fail:
delete crash;
}
-static void dequeue_requests() {
- while (!queued_requests.empty() && num_concurrent_dumps < kMaxConcurrentDumps) {
- Crash* next_crash = queued_requests.front();
- queued_requests.pop_front();
- perform_request(next_crash);
- }
-}
-
static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
void*) {
event_base* base = evconnlistener_get_base(listener);
Crash* crash = new Crash();
+ // TODO: Make sure that only java crashes come in on the java socket
+ // and only native crashes on the native socket.
struct timeval timeout = { 1, 0 };
event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
crash->crash_fd.reset(sockfd);
@@ -182,6 +251,7 @@
static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
ssize_t rc;
Crash* crash = static_cast<Crash*>(arg);
+
TombstonedCrashPacket request = {};
if ((ev & EV_TIMEOUT) != 0) {
@@ -208,12 +278,33 @@
goto fail;
}
- crash->crash_pid = request.packet.dump_request.pid;
+ crash->crash_type = request.packet.dump_request.dump_type;
+ if (crash->crash_type < 0 || crash->crash_type > kDebuggerdAnyIntercept) {
+ LOG(WARNING) << "unexpected crash dump type: " << crash->crash_type;
+ goto fail;
+ }
+
+ if (crash->crash_type != kDebuggerdJavaBacktrace) {
+ crash->crash_pid = request.packet.dump_request.pid;
+ } else {
+ // Requests for java traces are sent from untrusted processes, so we
+ // must not trust the PID sent down with the request. Instead, we ask the
+ // kernel.
+ ucred cr = {};
+ socklen_t len = sizeof(cr);
+ int ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
+ if (ret != 0) {
+ PLOG(ERROR) << "Failed to getsockopt(..SO_PEERCRED)";
+ goto fail;
+ }
+
+ crash->crash_pid = cr.pid;
+ }
+
LOG(INFO) << "received crash request for pid " << crash->crash_pid;
- if (num_concurrent_dumps == kMaxConcurrentDumps) {
+ if (CrashQueue::for_crash(crash)->maybe_enqueue_crash(crash)) {
LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
- queued_requests.push_back(crash);
} else {
perform_request(crash);
}
@@ -229,7 +320,7 @@
Crash* crash = static_cast<Crash*>(arg);
TombstonedCrashPacket request = {};
- --num_concurrent_dumps;
+ CrashQueue::for_crash(crash)->on_crash_completed();
if ((ev & EV_READ) == 0) {
goto fail;
@@ -252,14 +343,22 @@
}
if (!crash->crash_path.empty()) {
- LOG(ERROR) << "Tombstone written to: " << crash->crash_path;
+ if (crash->crash_type == kDebuggerdJavaBacktrace) {
+ LOG(ERROR) << "Traces for pid " << crash->crash_pid << " written to: " << crash->crash_path;
+ } else {
+ // NOTE: Several tools parse this log message to figure out where the
+ // tombstone associated with a given native crash was written. Any changes
+ // to this message must be carefully considered.
+ LOG(ERROR) << "Tombstone written to: " << crash->crash_path;
+ }
}
fail:
+ CrashQueue* queue = CrashQueue::for_crash(crash);
delete crash;
// If there's something queued up, let them proceed.
- dequeue_requests();
+ queue->maybe_dequeue_crashes(perform_request);
}
int main(int, char* []) {
@@ -273,13 +372,6 @@
};
debuggerd_register_handlers(&action);
- tombstone_directory_fd = open(kTombstoneDirectory, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
- if (tombstone_directory_fd == -1) {
- PLOG(FATAL) << "failed to open tombstone directory";
- }
-
- find_oldest_tombstone();
-
int intercept_socket = android_get_control_socket(kTombstonedInterceptSocketName);
int crash_socket = android_get_control_socket(kTombstonedCrashSocketName);
@@ -297,10 +389,26 @@
intercept_manager = new InterceptManager(base, intercept_socket);
- evconnlistener* listener =
- evconnlistener_new(base, crash_accept_cb, nullptr, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
- if (!listener) {
- LOG(FATAL) << "failed to create evconnlistener";
+ evconnlistener* tombstone_listener =
+ evconnlistener_new(base, crash_accept_cb, CrashQueue::for_tombstones(), LEV_OPT_CLOSE_ON_FREE,
+ -1 /* backlog */, crash_socket);
+ if (!tombstone_listener) {
+ LOG(FATAL) << "failed to create evconnlistener for tombstones.";
+ }
+
+ if (kJavaTraceDumpsEnabled) {
+ const int java_trace_socket = android_get_control_socket(kTombstonedJavaTraceSocketName);
+ if (java_trace_socket == -1) {
+ PLOG(FATAL) << "failed to get socket from init";
+ }
+
+ evutil_make_socket_nonblocking(java_trace_socket);
+ evconnlistener* java_trace_listener =
+ evconnlistener_new(base, crash_accept_cb, CrashQueue::for_anrs(), LEV_OPT_CLOSE_ON_FREE,
+ -1 /* backlog */, java_trace_socket);
+ if (!java_trace_listener) {
+ LOG(FATAL) << "failed to create evconnlistener for java traces.";
+ }
}
LOG(INFO) << "tombstoned successfully initialized";
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index b8345ca..53ef01c 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -7,4 +7,5 @@
socket tombstoned_crash seqpacket 0666 system system
socket tombstoned_intercept seqpacket 0666 system system
+ socket tombstoned_java_trace seqpacket 0666 system system
writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
similarity index 62%
rename from debuggerd/tombstoned_client.cpp
rename to debuggerd/tombstoned/tombstoned_client.cpp
index 03b4a20..bdb4c1a 100644
--- a/debuggerd/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "debuggerd/tombstoned.h"
+#include "tombstoned/tombstoned.h"
#include <fcntl.h>
#include <unistd.h>
@@ -22,43 +22,47 @@
#include <utility>
#include <android-base/unique_fd.h>
+#include <async_safe/log.h>
#include <cutils/sockets.h>
-#include "debuggerd/protocol.h"
-#include "debuggerd/util.h"
-#include "private/libc_logging.h"
+#include "protocol.h"
+#include "util.h"
using android::base::unique_fd;
-bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd) {
- unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
- ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
+ DebuggerdDumpType dump_type) {
+ unique_fd sockfd(
+ socket_local_client((dump_type != kDebuggerdJavaBacktrace ? kTombstonedCrashSocketName
+ : kTombstonedJavaTraceSocketName),
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
if (sockfd == -1) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s",
- strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s",
+ strerror(errno));
return false;
}
TombstonedCrashPacket packet = {};
packet.packet_type = CrashPacketType::kDumpRequest;
packet.packet.dump_request.pid = pid;
+ packet.packet.dump_request.dump_type = dump_type;
if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to write DumpRequest packet: %s",
- strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write DumpRequest packet: %s",
+ strerror(errno));
return false;
}
unique_fd tmp_output_fd;
ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
if (rc == -1) {
- __libc_format_log(ANDROID_LOG_ERROR, "libc",
- "failed to read response to DumpRequest packet: %s", strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+ "failed to read response to DumpRequest packet: %s", strerror(errno));
return false;
} else if (rc != sizeof(packet)) {
- __libc_format_log(
- ANDROID_LOG_ERROR, "libc",
- "received DumpRequest response packet of incorrect length (expected %zu, got %zd)",
- sizeof(packet), rc);
+ async_safe_format_log(
+ ANDROID_LOG_ERROR, "libc",
+ "received DumpRequest response packet of incorrect length (expected %zu, got %zd)",
+ sizeof(packet), rc);
return false;
}
@@ -67,8 +71,8 @@
// a regular fd, and writing to an fd with O_APPEND).
int flags = fcntl(tmp_output_fd.get(), F_GETFL);
if (fcntl(tmp_output_fd.get(), F_SETFL, flags | O_APPEND) != 0) {
- __libc_format_log(ANDROID_LOG_WARN, "libc", "failed to set output fd flags: %s",
- strerror(errno));
+ async_safe_format_log(ANDROID_LOG_WARN, "libc", "failed to set output fd flags: %s",
+ strerror(errno));
}
*tombstoned_socket = std::move(sockfd);
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 4c015d7..0bb07ac 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "debuggerd/util.h"
+#include "util.h"
#include <sys/socket.h>
@@ -22,9 +22,7 @@
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
-#include <debuggerd/protocol.h>
-
-#include "private/libc_logging.h"
+#include "protocol.h"
using android::base::unique_fd;
@@ -88,13 +86,3 @@
return result;
}
-
-bool Pipe(unique_fd* read, unique_fd* write) {
- int pipefds[2];
- if (pipe(pipefds) != 0) {
- return false;
- }
- read->reset(pipefds[0]);
- write->reset(pipefds[1]);
- return true;
-}
diff --git a/debuggerd/include/debuggerd/util.h b/debuggerd/util.h
similarity index 95%
rename from debuggerd/include/debuggerd/util.h
rename to debuggerd/util.h
index 6051714..171e07a 100644
--- a/debuggerd/include/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -42,5 +42,3 @@
// 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);
-
-bool Pipe(android::base::unique_fd* read, android::base::unique_fd* write);
diff --git a/demangle/Android.bp b/demangle/Android.bp
index 96ab57d..e55c886 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -29,6 +29,7 @@
cc_library {
name: "libdemangle",
defaults: ["libdemangle_defaults"],
+ vendor_available: true,
srcs: [
"Demangler.cpp",
diff --git a/trusty/storage/interface/Android.mk b/demangle/Android.mk
similarity index 65%
rename from trusty/storage/interface/Android.mk
rename to demangle/Android.mk
index 15cb6f3..e3cfc2a 100644
--- a/trusty/storage/interface/Android.mk
+++ b/demangle/Android.mk
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2015 The Android Open-Source Project
+# 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.
@@ -14,12 +14,19 @@
# limitations under the License.
#
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE := libtrustystorageinterface
+LOCAL_MODULE := demangle_fuzzer
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := \
+ Demangler.cpp \
+ demangle_fuzzer.cpp \
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := \
+ -Wall \
+ -Werror \
+ -Wextra \
-include $(BUILD_STATIC_LIBRARY)
+include $(BUILD_FUZZ_TEST)
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
index fb68119..f56a9be 100644
--- a/demangle/DemangleTest.cpp
+++ b/demangle/DemangleTest.cpp
@@ -22,7 +22,14 @@
#include "Demangler.h"
-TEST(DemangleTest, VoidArgumentTest) {
+TEST(DemangleTest, IllegalArgumentModifiers) {
+ Demangler demangler;
+
+ ASSERT_EQ("_Zpp4FUNKK", demangler.Parse("_Zpp4FUNKK"));
+ ASSERT_EQ("_Zpp4FUNVV", demangler.Parse("_Zpp4FUNVV"));
+}
+
+TEST(DemangleTest, VoidArgument) {
Demangler demangler;
ASSERT_EQ("func()", demangler.Parse("_ZN4funcEv"));
@@ -189,6 +196,14 @@
ASSERT_EQ("value(one, signed char)", demangler.Parse("_Z5value3onea"));
}
+TEST(DemangleTest, FunctionStartsWithLPlusNumber) {
+ Demangler demangler;
+
+ ASSERT_EQ("value(char, int)", demangler.Parse("_ZL5valueci"));
+ ASSERT_EQ("abcdefjklmn(signed char)", demangler.Parse("_ZL11abcdefjklmna"));
+ ASSERT_EQ("value(one, signed char)", demangler.Parse("_ZL5value3onea"));
+}
+
TEST(DemangleTest, StdTypes) {
Demangler demangler;
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index 77cfd3b..c0a96aa 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -542,9 +542,8 @@
} else {
suffix = " volatile";
}
- if (name[-1] == 'K' || name[-1] == 'V') {
+ if (!cur_state_.suffixes.empty() && (name[-1] == 'K' || name[-1] == 'V')) {
// Special case, const/volatile apply as a single entity.
- assert(!cur_state_.suffixes.empty());
size_t index = cur_state_.suffixes.size();
cur_state_.suffixes[index-1].insert(0, suffix);
} else {
@@ -699,6 +698,8 @@
if (std::isdigit(*name)) {
name = GetStringFromLength(name, &function_name_);
+ } else if (*name == 'L' && std::isdigit(name[1])) {
+ name = GetStringFromLength(name + 1, &function_name_);
} else {
name = AppendOperatorString(name);
function_name_ = cur_state_.str;
@@ -723,7 +724,8 @@
&& static_cast<size_t>(cur_name - name) < max_length) {
cur_name = (this->*parse_func_)(cur_name);
}
- if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty()) {
+ if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty() ||
+ !cur_state_.suffixes.empty()) {
return name;
}
diff --git a/demangle/demangle.cpp b/demangle/demangle.cpp
index be4d2dd..66e5e58 100644
--- a/demangle/demangle.cpp
+++ b/demangle/demangle.cpp
@@ -20,22 +20,25 @@
#include <string.h>
#include <unistd.h>
+#include <cctype>
#include <string>
#include <demangle.h>
extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
-void usage(const char* prog_name) {
- printf("Usage: %s [-c] <NAME_TO_DEMANGLE>\n", prog_name);
- printf(" -c\n");
- printf(" Compare the results of __cxa_demangle against the current\n");
- printf(" demangler.\n");
+static void Usage(const char* prog_name) {
+ printf("usage: %s [-c] [NAME_TO_DEMANGLE...]\n", prog_name);
+ printf("\n");
+ printf("Demangles C++ mangled names if supplied on the command-line, or found\n");
+ printf("reading from stdin otherwise.\n");
+ printf("\n");
+ printf("-c\tCompare against __cxa_demangle\n");
+ printf("\n");
}
-std::string DemangleWithCxa(const char* name) {
+static std::string DemangleWithCxa(const char* name) {
const char* cxa_demangle = __cxa_demangle(name, nullptr, nullptr, nullptr);
-
if (cxa_demangle == nullptr) {
return name;
}
@@ -54,6 +57,49 @@
return demangled_str;
}
+static void Compare(const char* name, const std::string& demangled_name) {
+ std::string cxa_demangled_name(DemangleWithCxa(name));
+ if (cxa_demangled_name != demangled_name) {
+ printf("\nMismatch!\n");
+ printf("\tmangled name: %s\n", name);
+ printf("\tour demangle: %s\n", demangled_name.c_str());
+ printf("\tcxa demangle: %s\n", cxa_demangled_name.c_str());
+ exit(1);
+ }
+}
+
+static int Filter(bool compare) {
+ char* line = nullptr;
+ size_t line_length = 0;
+
+ while ((getline(&line, &line_length, stdin)) != -1) {
+ char* p = line;
+ char* name;
+ while ((name = strstr(p, "_Z")) != nullptr) {
+ // Output anything before the identifier.
+ *name = 0;
+ printf("%s", p);
+ *name = '_';
+
+ // Extract the identifier.
+ p = name;
+ while (*p && (std::isalnum(*p) || *p == '_' || *p == '.' || *p == '$')) ++p;
+
+ // Demangle and output.
+ std::string identifier(name, p);
+ std::string demangled_name = demangle(identifier.c_str());
+ printf("%s", demangled_name.c_str());
+
+ if (compare) Compare(identifier.c_str(), demangled_name);
+ }
+ // Output anything after the last identifier.
+ printf("%s", p);
+ }
+
+ free(line);
+ return 0;
+}
+
int main(int argc, char** argv) {
#ifdef __BIONIC__
const char* prog_name = getprogname();
@@ -67,31 +113,21 @@
if (opt_char == 'c') {
compare = true;
} else {
- usage(prog_name);
+ Usage(prog_name);
return 1;
}
}
- if (optind >= argc || argc - optind != 1) {
- printf("Must supply a single argument.\n\n");
- usage(prog_name);
- return 1;
- }
- const char* name = argv[optind];
- std::string demangled_name = demangle(name);
+ // With no arguments, act as a filter.
+ if (optind == argc) return Filter(compare);
- printf("%s\n", demangled_name.c_str());
+ // Otherwise demangle each argument.
+ while (optind < argc) {
+ const char* name = argv[optind++];
+ std::string demangled_name = demangle(name);
+ printf("%s\n", demangled_name.c_str());
- if (compare) {
- std::string cxa_demangle_str(DemangleWithCxa(name));
-
- if (cxa_demangle_str != demangled_name) {
- printf("Mismatch\n");
- printf("cxa demangle: %s\n", cxa_demangle_str.c_str());
- return 1;
- } else {
- printf("Match\n");
- }
+ if (compare) Compare(name, demangled_name);
}
return 0;
}
diff --git a/demangle/demangle_fuzzer.cpp b/demangle/demangle_fuzzer.cpp
new file mode 100644
index 0000000..83fafc2
--- /dev/null
+++ b/demangle/demangle_fuzzer.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include "Demangler.h"
+
+extern "C" void LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::vector<char> data_str(size + 1);
+ memcpy(data_str.data(), data, size);
+ data_str[size] = '\0';
+
+ Demangler demangler;
+ std::string demangled_name = demangler.Parse(data_str.data());
+ if (size != 0 && data_str[0] != '\0' && demangled_name.empty()) {
+ abort();
+ }
+}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 5610cc0..dd8bad9 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -14,10 +14,12 @@
LOCAL_PATH:= $(call my-dir)
-fastboot_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
+include $(LOCAL_PATH)/../platform_tools_tool_version.mk
include $(CLEAR_VARS)
+LOCAL_CFLAGS += -DFASTBOOT_VERSION="\"$(tool_version)\""
+
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/../adb \
$(LOCAL_PATH)/../mkbootimg \
@@ -38,8 +40,7 @@
LOCAL_MODULE_TAGS := debug
LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
-
-LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
+LOCAL_REQUIRED_MODULES := mke2fs e2fsdroid
LOCAL_SRC_FILES_linux := usb_linux.cpp
LOCAL_STATIC_LIBRARIES_linux := libselinux
@@ -51,7 +52,7 @@
LOCAL_SRC_FILES_windows := usb_windows.cpp
LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
-LOCAL_REQUIRED_MODULES_windows := AdbWinApi
+LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
LOCAL_LDLIBS_windows := -lws2_32
LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
@@ -85,6 +86,8 @@
include $(BUILD_HOST_EXECUTABLE)
my_dist_files := $(LOCAL_BUILT_MODULE)
+my_dist_files += $(HOST_OUT_EXECUTABLES)/mke2fs$(HOST_EXECUTABLE_SUFFIX)
+my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid$(HOST_EXECUTABLE_SUFFIX)
ifeq ($(HOST_OS),linux)
my_dist_files += $(HOST_LIBRARY_PATH)/libf2fs_fmt_host_dyn$(HOST_SHLIB_SUFFIX)
endif
diff --git a/fastboot/README.md b/fastboot/README.md
index 022d34b..ec7dcb4 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -126,6 +126,16 @@
space in RAM or "FAIL" if not. The size of
the download is remembered.
+ upload Read data from memory which was staged by the last
+ command, e.g. an oem command. The client will reply
+ with "DATA%08x" if it is ready to send %08x bytes of
+ data. If no data was staged in the last command,
+ the client must reply with "FAIL". After the client
+ successfully sends %08x bytes, the client shall send
+ a single packet starting with "OKAY". Clients
+ should not support "upload" unless it supports an
+ oem command that requires "upload" capabilities.
+
verify:%08x Send a digital signature to verify the downloaded
data. Required if the bootloader is "secure"
otherwise "flash" and "boot" will be ignored.
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 728dcb8..7e10cc9 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -44,6 +44,8 @@
#define OP_NOTICE 4
#define OP_DOWNLOAD_SPARSE 5
#define OP_WAIT_FOR_DISCONNECT 6
+#define OP_DOWNLOAD_FD 7
+#define OP_UPLOAD 8
typedef struct Action Action;
@@ -56,6 +58,7 @@
char cmd[CMD_SIZE];
const char* prod;
void* data;
+ int fd;
// The protocol only supports 32-bit sizes, so you'll have to break
// anything larger into chunks.
@@ -142,7 +145,20 @@
a->msg = mkmsg("erasing '%s'", ptn);
}
-void fb_queue_flash(const char *ptn, void *data, unsigned sz)
+void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz)
+{
+ Action *a;
+
+ a = queue_action(OP_DOWNLOAD_FD, "");
+ a->fd = fd;
+ a->size = sz;
+ a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
+
+ a = queue_action(OP_COMMAND, "flash:%s", ptn);
+ a->msg = mkmsg("writing '%s'", ptn);
+}
+
+void fb_queue_flash(const char *ptn, void *data, uint32_t sz)
{
Action *a;
@@ -155,7 +171,7 @@
a->msg = mkmsg("writing '%s'", ptn);
}
-void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, unsigned sz, size_t current,
+void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
size_t total) {
Action *a;
@@ -242,6 +258,12 @@
return cb_check(a, status, resp, 1);
}
+static char* xstrdup(const char* s) {
+ char* result = strdup(s);
+ if (!result) die("out of memory");
+ return result;
+}
+
void fb_queue_require(const char *prod, const char *var,
bool invert, size_t nvalues, const char **value)
{
@@ -260,16 +282,14 @@
fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
return status;
}
- fprintf(stderr, "%s: %s\n", (char*) a->data, resp);
+ fprintf(stderr, "%s: %s\n", static_cast<const char*>(a->data), resp);
+ free(static_cast<char*>(a->data));
return 0;
}
-void fb_queue_display(const char *var, const char *prettyname)
-{
- Action *a;
- a = queue_action(OP_QUERY, "getvar:%s", var);
- a->data = strdup(prettyname);
- if (a->data == nullptr) die("out of memory");
+void fb_queue_display(const char* var, const char* prettyname) {
+ Action* a = queue_action(OP_QUERY, "getvar:%s", var);
+ a->data = xstrdup(prettyname);
a->func = cb_display;
}
@@ -282,11 +302,9 @@
return 0;
}
-void fb_queue_query_save(const char *var, char *dest, unsigned dest_size)
-{
- Action *a;
- a = queue_action(OP_QUERY, "getvar:%s", var);
- a->data = (void *)dest;
+void fb_queue_query_save(const char* var, char* dest, uint32_t dest_size) {
+ Action* a = queue_action(OP_QUERY, "getvar:%s", var);
+ a->data = dest;
a->size = dest_size;
a->func = cb_save;
}
@@ -309,7 +327,7 @@
a->msg = msg;
}
-void fb_queue_download(const char *name, void *data, unsigned size)
+void fb_queue_download(const char *name, void *data, uint32_t size)
{
Action *a = queue_action(OP_DOWNLOAD, "");
a->data = data;
@@ -317,8 +335,22 @@
a->msg = mkmsg("downloading '%s'", name);
}
-void fb_queue_notice(const char *notice)
+void fb_queue_download_fd(const char *name, int fd, uint32_t sz)
{
+ Action *a;
+ a = queue_action(OP_DOWNLOAD_FD, "");
+ a->fd = fd;
+ a->size = sz;
+ a->msg = mkmsg("sending '%s' (%d KB)", name, sz / 1024);
+}
+
+void fb_queue_upload(const char* outfile) {
+ Action* a = queue_action(OP_UPLOAD, "");
+ a->data = xstrdup(outfile);
+ a->msg = mkmsg("uploading '%s'", outfile);
+}
+
+void fb_queue_notice(const char* notice) {
Action *a = queue_action(OP_NOTICE, "");
a->data = (void*) notice;
}
@@ -328,11 +360,11 @@
queue_action(OP_WAIT_FOR_DISCONNECT, "");
}
-int fb_execute_queue(Transport* transport)
+int64_t fb_execute_queue(Transport* transport)
{
Action *a;
char resp[FB_RESPONSE_SZ+1];
- int status = 0;
+ int64_t status = 0;
a = action_list;
if (!a)
@@ -351,6 +383,10 @@
status = fb_download_data(transport, a->data, a->size);
status = a->func(a, status, status ? fb_get_error().c_str() : "");
if (status) break;
+ } else if (a->op == OP_DOWNLOAD_FD) {
+ status = fb_download_data_fd(transport, a->fd, a->size);
+ status = a->func(a, status, status ? fb_get_error().c_str() : "");
+ if (status) break;
} else if (a->op == OP_COMMAND) {
status = fb_command(transport, a->cmd);
status = a->func(a, status, status ? fb_get_error().c_str() : "");
@@ -367,6 +403,9 @@
if (status) break;
} else if (a->op == OP_WAIT_FOR_DISCONNECT) {
transport->WaitForDisconnect();
+ } else if (a->op == OP_UPLOAD) {
+ status = fb_upload_data(transport, reinterpret_cast<char*>(a->data));
+ status = a->func(a, status, status ? fb_get_error().c_str() : "");
} else {
die("bogus action");
}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 2927b16..5f2267c 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -26,8 +26,6 @@
* SUCH DAMAGE.
*/
-#define _LARGEFILE64_SOURCE
-
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
@@ -55,6 +53,8 @@
#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+#include <android-base/unique_fd.h>
#include <sparse/sparse.h>
#include <ziparchive/zip_archive.h>
@@ -67,6 +67,8 @@
#include "udp.h"
#include "usb.h"
+using android::base::unique_fd;
+
#ifndef O_BINARY
#define O_BINARY 0
#endif
@@ -74,10 +76,13 @@
char cur_product[FB_RESPONSE_SZ + 1];
static const char* serial = nullptr;
-static const char* product = nullptr;
static const char* cmdline = nullptr;
static unsigned short vendor_id = 0;
static int long_listing = 0;
+// Don't resparse files in too-big chunks.
+// libsparse will support INT_MAX, but this results in large allocations, so
+// let's keep it at 1GB to avoid memory pressure on the host.
+static constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024;
static int64_t sparse_limit = -1;
static int64_t target_sparse_limit = -1;
@@ -88,10 +93,13 @@
static unsigned second_offset = 0x00f00000;
static unsigned tags_offset = 0x00000100;
+static bool g_disable_verity = false;
+static bool g_disable_verification = false;
+
static const std::string convert_fbe_marker_filename("convert_fbe");
enum fb_buffer_type {
- FB_BUFFER,
+ FB_BUFFER_FD,
FB_BUFFER_SPARSE,
};
@@ -99,63 +107,51 @@
enum fb_buffer_type type;
void* data;
int64_t sz;
+ int fd;
};
static struct {
- char img_name[17];
- char sig_name[17];
- char part_name[9];
+ const char* nickname;
+ const char* img_name;
+ const char* sig_name;
+ const char* part_name;
bool is_optional;
bool is_secondary;
} images[] = {
- {"boot.img", "boot.sig", "boot", false, false},
- {"boot_other.img", "boot.sig", "boot", true, true},
- {"recovery.img", "recovery.sig", "recovery", true, false},
- {"system.img", "system.sig", "system", false, false},
- {"system_other.img", "system.sig", "system", true, true},
- {"vendor.img", "vendor.sig", "vendor", true, false},
- {"vendor_other.img", "vendor.sig", "vendor", true, true},
+ // clang-format off
+ { "boot", "boot.img", "boot.sig", "boot", false, false },
+ { nullptr, "boot_other.img", "boot.sig", "boot", true, true },
+ { "dtbo", "dtbo.img", "dtbo.sig", "dtbo", true, false },
+ { "dts", "dt.img", "dt.sig", "dts", true, false },
+ { "recovery", "recovery.img", "recovery.sig", "recovery", true, false },
+ { "system", "system.img", "system.sig", "system", false, false },
+ { nullptr, "system_other.img", "system.sig", "system", true, true },
+ { "vbmeta", "vbmeta.img", "vbmeta.sig", "vbmeta", true, false },
+ { "vendor", "vendor.img", "vendor.sig", "vendor", true, false },
+ { nullptr, "vendor_other.img", "vendor.sig", "vendor", true, true },
+ // clang-format on
};
-static std::string find_item_given_name(const char* img_name, const char* product) {
- if(product) {
- std::string path = android::base::GetExecutablePath();
- path.erase(path.find_last_of('/'));
- return android::base::StringPrintf("%s/../../../target/product/%s/%s",
- path.c_str(), product, img_name);
- }
-
- char *dir = getenv("ANDROID_PRODUCT_OUT");
+static std::string find_item_given_name(const char* img_name) {
+ char* dir = getenv("ANDROID_PRODUCT_OUT");
if (dir == nullptr || dir[0] == '\0') {
- die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
+ die("ANDROID_PRODUCT_OUT not set");
}
-
return android::base::StringPrintf("%s/%s", dir, img_name);
}
-std::string find_item(const char* item, const char* product) {
- const char *fn;
-
- if (!strcmp(item,"boot")) {
- fn = "boot.img";
- } else if(!strcmp(item,"recovery")) {
- fn = "recovery.img";
- } else if(!strcmp(item,"system")) {
- fn = "system.img";
- } else if(!strcmp(item,"vendor")) {
- fn = "vendor.img";
- } else if(!strcmp(item,"userdata")) {
- fn = "userdata.img";
- } else if(!strcmp(item,"cache")) {
- fn = "cache.img";
- } else if(!strcmp(item,"info")) {
- fn = "android-info.txt";
- } else {
- fprintf(stderr,"unknown partition '%s'\n", item);
- return "";
+static std::string find_item(const std::string& item) {
+ for (size_t i = 0; i < arraysize(images); ++i) {
+ if (images[i].nickname && item == images[i].nickname) {
+ return find_item_given_name(images[i].img_name);
+ }
}
- return find_item_given_name(fn, product);
+ if (item == "userdata") return find_item_given_name("userdata.img");
+ if (item == "cache") return find_item_given_name("cache.img");
+
+ fprintf(stderr, "unknown partition '%s'\n", item.c_str());
+ return "";
}
static int64_t get_file_size(int fd) {
@@ -314,8 +310,21 @@
usb_open(list_devices_callback);
}
-static void usage() {
- fprintf(stderr,
+static void syntax_error(const char* fmt, ...) {
+ fprintf(stderr, "fastboot: usage: ");
+
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+static int show_help() {
+ // clang-format off
+ fprintf(stdout,
/* 1234567890123456789012345678901234567890123456789012345678901234567890123456 */
"usage: fastboot [ <option> ] <command>\n"
"\n"
@@ -353,13 +362,20 @@
" set_active <slot> Sets the active slot. If slots are\n"
" not supported, this does nothing.\n"
" boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
- " flash:raw boot <kernel> [ <ramdisk> [ <second> ] ]\n"
+ " flash:raw <bootable-partition> <kernel> [ <ramdisk> [ <second> ] ]\n"
" Create bootimage and flash it.\n"
" devices [-l] List all connected devices [with\n"
" device paths].\n"
" continue Continue with autoboot.\n"
" reboot [bootloader|emergency] Reboot device [into bootloader or emergency mode].\n"
" reboot-bootloader Reboot device into bootloader.\n"
+ " oem <parameter1> ... <parameterN> Executes oem specific command.\n"
+ " stage <infile> Sends contents of <infile> to stage for\n"
+ " the next command. Supported only on\n"
+ " Android Things devices.\n"
+ " get_staged <outfile> Receives data to <outfile> staged by the\n"
+ " last command. Supported only on Android\n"
+ " Things devices.\n"
" help Show this help message.\n"
"\n"
"options:\n"
@@ -372,7 +388,6 @@
" For ethernet, provide an address in the\n"
" form <protocol>:<hostname>[:port] where\n"
" <protocol> is either tcp or udp.\n"
- " -p <product> Specify product name.\n"
" -c <cmdline> Override kernel commandline.\n"
" -i <vendor id> Specify a custom USB vendor id.\n"
" -b, --base <base_addr> Specify a custom kernel base\n"
@@ -407,6 +422,10 @@
" --skip-reboot Will not reboot the device when\n"
" performing commands that normally\n"
" trigger a reboot.\n"
+ " --disable-verity Set the disable-verity flag in the\n"
+ " the vbmeta image being flashed.\n"
+ " --disable-verification Set the disable-verification flag in"
+ " the vbmeta image being flashed.\n"
#if !defined(_WIN32)
" --wipe-and-use-fbe On devices which support it,\n"
" erase userdata and cache, and\n"
@@ -416,31 +435,22 @@
" --version Display version.\n"
" -h, --help show this message.\n"
);
+ // clang-format off
+ return 0;
}
-static void* load_bootable_image(const char* kernel, const char* ramdisk,
- const char* secondstage, int64_t* sz,
+static void* load_bootable_image(const std::string& kernel, const std::string& ramdisk,
+ const std::string& second_stage, int64_t* sz,
const char* cmdline) {
- if (kernel == nullptr) {
- fprintf(stderr, "no image specified\n");
- return 0;
- }
-
int64_t ksize;
- void* kdata = load_file(kernel, &ksize);
- if (kdata == nullptr) {
- fprintf(stderr, "cannot load '%s': %s\n", kernel, strerror(errno));
- return 0;
- }
+ void* kdata = load_file(kernel.c_str(), &ksize);
+ if (kdata == nullptr) die("cannot load '%s': %s\n", kernel.c_str(), strerror(errno));
// Is this actually a boot image?
- if(!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+ if (!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
if (cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
- if (ramdisk) {
- fprintf(stderr, "cannot boot a boot.img *and* ramdisk\n");
- return 0;
- }
+ if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk\n");
*sz = ksize;
return kdata;
@@ -448,22 +458,16 @@
void* rdata = nullptr;
int64_t rsize = 0;
- if (ramdisk) {
- rdata = load_file(ramdisk, &rsize);
- if (rdata == nullptr) {
- fprintf(stderr,"cannot load '%s': %s\n", ramdisk, strerror(errno));
- return 0;
- }
+ if (!ramdisk.empty()) {
+ rdata = load_file(ramdisk.c_str(), &rsize);
+ if (rdata == nullptr) die("cannot load '%s': %s\n", ramdisk.c_str(), strerror(errno));
}
void* sdata = nullptr;
int64_t ssize = 0;
- if (secondstage) {
- sdata = load_file(secondstage, &ssize);
- if (sdata == nullptr) {
- fprintf(stderr,"cannot load '%s': %s\n", secondstage, strerror(errno));
- return 0;
- }
+ if (!second_stage.empty()) {
+ sdata = load_file(second_stage.c_str(), &ssize);
+ if (sdata == nullptr) die("cannot load '%s': %s\n", second_stage.c_str(), strerror(errno));
}
fprintf(stderr,"creating boot image...\n");
@@ -472,10 +476,8 @@
rdata, rsize, ramdisk_offset,
sdata, ssize, second_offset,
page_size, base_addr, tags_offset, &bsize);
- if (bdata == nullptr) {
- fprintf(stderr,"failed to create boot.img\n");
- return 0;
- }
+ if (bdata == nullptr) die("failed to create boot.img\n");
+
if (cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", bsize);
*sz = bsize;
@@ -483,8 +485,7 @@
return bdata;
}
-static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, int64_t* sz)
-{
+static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
ZipString zip_entry_name(entry_name);
ZipEntry zip_entry;
if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
@@ -494,6 +495,7 @@
*sz = zip_entry.uncompressed_length;
+ fprintf(stderr, "extracting %s (%" PRId64 " MB)...\n", entry_name, *sz / 1024 / 1024);
uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
if (data == nullptr) {
fprintf(stderr, "failed to allocate %" PRId64 " bytes for '%s'\n", *sz, entry_name);
@@ -542,22 +544,39 @@
return "";
}
+static int make_temporary_fd() {
+ // TODO: reimplement to avoid leaking a FILE*.
+ return fileno(tmpfile());
+}
+
#else
+static std::string make_temporary_template() {
+ const char* tmpdir = getenv("TMPDIR");
+ if (tmpdir == nullptr) tmpdir = P_tmpdir;
+ return std::string(tmpdir) + "/fastboot_userdata_XXXXXX";
+}
+
static std::string make_temporary_directory() {
- const char *tmpdir = getenv("TMPDIR");
- if (tmpdir == nullptr) {
- tmpdir = P_tmpdir;
- }
- std::string result = std::string(tmpdir) + "/fastboot_userdata_XXXXXX";
- if (mkdtemp(&result[0]) == NULL) {
- fprintf(stderr, "Unable to create temporary directory: %s\n",
- strerror(errno));
+ std::string result(make_temporary_template());
+ if (mkdtemp(&result[0]) == nullptr) {
+ fprintf(stderr, "Unable to create temporary directory: %s\n", strerror(errno));
return "";
}
return result;
}
+static int make_temporary_fd() {
+ std::string path_template(make_temporary_template());
+ int fd = mkstemp(&path_template[0]);
+ if (fd == -1) {
+ fprintf(stderr, "Unable to create temporary file: %s\n", strerror(errno));
+ return -1;
+ }
+ unlink(path_template.c_str());
+ return fd;
+}
+
#endif
static std::string create_fbemarker_tmpdir() {
@@ -591,9 +610,9 @@
}
}
-static int unzip_to_file(ZipArchiveHandle zip, char* entry_name) {
- FILE* fp = tmpfile();
- if (fp == nullptr) {
+static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
+ unique_fd fd(make_temporary_fd());
+ if (fd == -1) {
fprintf(stderr, "failed to create temporary file for '%s': %s\n",
entry_name, strerror(errno));
return -1;
@@ -603,21 +622,20 @@
ZipEntry zip_entry;
if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name);
- fclose(fp);
return -1;
}
- int fd = fileno(fp);
+ fprintf(stderr, "extracting %s (%" PRIu32 " MB)...\n", entry_name,
+ zip_entry.uncompressed_length / 1024 / 1024);
int error = ExtractEntryToFile(zip, &zip_entry, fd);
if (error != 0) {
fprintf(stderr, "failed to extract '%s': %s\n", entry_name, ErrorCodeString(error));
- fclose(fp);
return -1;
}
lseek(fd, 0, SEEK_SET);
// TODO: We're leaking 'fp' here.
- return fd;
+ return fd.release();
}
static char *strip(char *s)
@@ -786,7 +804,7 @@
}
if (size > limit) {
- return limit;
+ return std::min(limit, RESPARSE_LIMIT);
}
return 0;
@@ -819,10 +837,9 @@
buf->type = FB_BUFFER_SPARSE;
buf->data = s;
} else {
- void* data = load_fd(fd, &sz);
- if (data == nullptr) return -1;
- buf->type = FB_BUFFER;
- buf->data = data;
+ buf->type = FB_BUFFER_FD;
+ buf->data = nullptr;
+ buf->fd = fd;
buf->sz = sz;
}
@@ -830,17 +847,73 @@
}
static bool load_buf(Transport* transport, const char* fname, struct fastboot_buffer* buf) {
- int fd = open(fname, O_RDONLY | O_BINARY);
+ unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
+
if (fd == -1) {
return false;
}
- return load_buf_fd(transport, fd, buf);
+
+ struct stat s;
+ if (fstat(fd, &s)) {
+ return false;
+ }
+ if (!S_ISREG(s.st_mode)) {
+ errno = S_ISDIR(s.st_mode) ? EISDIR : EINVAL;
+ return false;
+ }
+
+ return load_buf_fd(transport, fd.release(), buf);
+}
+
+static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf) {
+ // Buffer needs to be at least the size of the VBMeta struct which
+ // is 256 bytes.
+ if (buf->sz < 256) {
+ return;
+ }
+
+ int fd = make_temporary_fd();
+ if (fd == -1) {
+ die("Failed to create temporary file for vbmeta rewriting");
+ }
+
+ std::string data;
+ if (!android::base::ReadFdToString(buf->fd, &data)) {
+ die("Failed reading from vbmeta");
+ }
+
+ // There's a 32-bit big endian |flags| field at offset 120 where
+ // bit 0 corresponds to disable-verity and bit 1 corresponds to
+ // disable-verification.
+ //
+ // See external/avb/libavb/avb_vbmeta_image.h for the layout of
+ // the VBMeta struct.
+ if (g_disable_verity) {
+ data[123] |= 0x01;
+ }
+ if (g_disable_verification) {
+ data[123] |= 0x02;
+ }
+
+ if (!android::base::WriteStringToFd(data, fd)) {
+ die("Failed writing to modified vbmeta");
+ }
+ close(buf->fd);
+ buf->fd = fd;
+ lseek(fd, 0, SEEK_SET);
}
static void flash_buf(const char *pname, struct fastboot_buffer *buf)
{
sparse_file** s;
+ // Rewrite vbmeta if that's what we're flashing and modification has been requested.
+ if ((g_disable_verity || g_disable_verification) &&
+ (strcmp(pname, "vbmeta") == 0 || strcmp(pname, "vbmeta_a") == 0 ||
+ strcmp(pname, "vbmeta_b") == 0)) {
+ rewrite_vbmeta_buffer(buf);
+ }
+
switch (buf->type) {
case FB_BUFFER_SPARSE: {
std::vector<std::pair<sparse_file*, int64_t>> sparse_files;
@@ -857,9 +930,8 @@
}
break;
}
-
- case FB_BUFFER:
- fb_queue_flash(pname, buf->data, buf->sz);
+ case FB_BUFFER_FD:
+ fb_queue_flash_fd(pname, buf->fd, buf->sz);
break;
default:
die("unknown buffer type: %d", buf->type);
@@ -1037,9 +1109,9 @@
flash_buf(pname, &buf);
}
-static void do_update_signature(ZipArchiveHandle zip, char* fn) {
+static void do_update_signature(ZipArchiveHandle zip, const char* filename) {
int64_t sz;
- void* data = unzip_file(zip, fn, &sz);
+ void* data = unzip_file(zip, filename, &sz);
if (data == nullptr) return;
fb_queue_download("signature", data, sz);
fb_queue_command("signature", "installing signature");
@@ -1131,7 +1203,7 @@
}
flash_buf(partition.c_str(), &buf);
/* not closing the fd here since the sparse code keeps the fd around
- * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
+ * but hasn't mmaped data yet. The temporary file will get cleaned up when the
* program exits.
*/
};
@@ -1166,7 +1238,7 @@
fb_queue_query_save("product", cur_product, sizeof(cur_product));
- fname = find_item("info", product);
+ fname = find_item_given_name("android-info.txt");
if (fname.empty()) die("cannot find android-info.txt");
int64_t sz;
@@ -1198,7 +1270,7 @@
slot = slot_override.c_str();
}
if (!slot) continue;
- fname = find_item_given_name(images[i].img_name, product);
+ fname = find_item_given_name(images[i].img_name);
fastboot_buffer buf;
if (!load_buf(transport, fname.c_str(), &buf)) {
if (images[i].is_optional) continue;
@@ -1222,41 +1294,33 @@
}
}
-#define skip(n) do { argc -= (n); argv += (n); } while (0)
-#define require(n) do { if (argc < (n)) {usage(); exit(1);}} while (0)
-
-static int do_bypass_unlock_command(int argc, char **argv)
-{
- if (argc <= 2) return 0;
- skip(2);
-
- /*
- * Process unlock_bootloader, we have to load the message file
- * and send that to the remote device.
- */
- require(1);
-
- int64_t sz;
- void* data = load_file(*argv, &sz);
- if (data == nullptr) die("could not load '%s': %s", *argv, strerror(errno));
- fb_queue_download("unlock_message", data, sz);
- fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
- skip(1);
- return 0;
+static std::string next_arg(std::vector<std::string>* args) {
+ if (args->empty()) syntax_error("expected argument");
+ std::string result = args->front();
+ args->erase(args->begin());
+ return result;
}
-static int do_oem_command(int argc, char** argv) {
- if (argc <= 1) return 0;
+static void do_bypass_unlock_command(std::vector<std::string>* args) {
+ if (args->empty()) syntax_error("missing unlock_bootloader request");
- std::string command;
- while (argc > 0) {
- command += *argv;
- skip(1);
- if (argc != 0) command += " ";
+ std::string filename = next_arg(args);
+
+ int64_t sz;
+ void* data = load_file(filename.c_str(), &sz);
+ if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
+ fb_queue_download("unlock_message", data, sz);
+ fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
+}
+
+static void do_oem_command(const std::string& cmd, std::vector<std::string>* args) {
+ if (args->empty()) syntax_error("empty oem command");
+
+ std::string command(cmd);
+ while (!args->empty()) {
+ command += " " + next_arg(args);
}
-
fb_queue_command(command.c_str(), "");
- return 0;
}
static int64_t parse_num(const char *arg)
@@ -1332,14 +1396,15 @@
static void fb_perform_format(Transport* transport,
const char* partition, int skip_if_not_supported,
- const char* type_override, const char* size_override,
+ const std::string& type_override, const std::string& size_override,
const std::string& initial_dir) {
std::string partition_type, partition_size;
struct fastboot_buffer buf;
const char* errMsg = nullptr;
const struct fs_generator* gen = nullptr;
- int fd;
+ TemporaryFile output;
+ unique_fd fd;
unsigned int limit = INT_MAX;
if (target_sparse_limit > 0 && target_sparse_limit < limit) {
@@ -1353,10 +1418,10 @@
errMsg = "Can't determine partition type.\n";
goto failed;
}
- if (type_override) {
+ if (!type_override.empty()) {
if (partition_type != type_override) {
fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n",
- partition, partition_type.c_str(), type_override);
+ partition, partition_type.c_str(), type_override.c_str());
}
partition_type = type_override;
}
@@ -1365,10 +1430,10 @@
errMsg = "Unable to get partition size\n";
goto failed;
}
- if (size_override) {
+ if (!size_override.empty()) {
if (partition_size != size_override) {
fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n",
- partition, partition_size.c_str(), size_override);
+ partition, partition_size.c_str(), size_override.c_str());
}
partition_size = size_override;
}
@@ -1392,21 +1457,23 @@
return;
}
- fd = fileno(tmpfile());
-
unsigned eraseBlkSize, logicalBlkSize;
eraseBlkSize = fb_get_flash_block_size(transport, "erase-block-size");
logicalBlkSize = fb_get_flash_block_size(transport, "logical-block-size");
- if (fs_generator_generate(gen, fd, size, initial_dir, eraseBlkSize, logicalBlkSize)) {
- fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
- close(fd);
+ if (fs_generator_generate(gen, output.path, size, initial_dir,
+ eraseBlkSize, logicalBlkSize)) {
+ die("Cannot generate image for %s\n", partition);
return;
}
- if (!load_buf_fd(transport, fd, &buf)) {
+ fd.reset(open(output.path, O_RDONLY));
+ if (fd == -1) {
+ fprintf(stderr, "Cannot open generated image: %s\n", strerror(errno));
+ return;
+ }
+ if (!load_buf_fd(transport, fd.release(), &buf)) {
fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
- close(fd);
return;
}
flash_buf(partition, &buf);
@@ -1455,6 +1522,8 @@
{"set-active", optional_argument, 0, 'a'},
{"skip-secondary", no_argument, 0, 0},
{"skip-reboot", no_argument, 0, 0},
+ {"disable-verity", no_argument, 0, 0},
+ {"disable-verification", no_argument, 0, 0},
#if !defined(_WIN32)
{"wipe-and-use-fbe", no_argument, 0, 0},
#endif
@@ -1464,7 +1533,7 @@
serial = getenv("ANDROID_SERIAL");
while (1) {
- int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:ha::", longopts, &longindex);
+ int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lc:i:m:ha::", longopts, &longindex);
if (c < 0) {
break;
}
@@ -1482,8 +1551,7 @@
cmdline = optarg;
break;
case 'h':
- usage();
- return 1;
+ return show_help();
case 'i': {
char *endptr = nullptr;
unsigned long val;
@@ -1504,9 +1572,6 @@
page_size = (unsigned)strtoul(optarg, nullptr, 0);
if (!page_size) die("invalid page size");
break;
- case 'p':
- product = optarg;
- break;
case 'r':
ramdisk_offset = strtoul(optarg, 0, 16);
break;
@@ -1535,7 +1600,8 @@
setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
} else if (strcmp("version", longopts[longindex].name) == 0) {
- fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
+ fprintf(stdout, "fastboot version %s\n", FASTBOOT_VERSION);
+ fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
return 0;
} else if (strcmp("slot", longopts[longindex].name) == 0) {
slot_override = std::string(optarg);
@@ -1543,6 +1609,10 @@
skip_secondary = true;
} else if (strcmp("skip-reboot", longopts[longindex].name) == 0 ) {
skip_reboot = true;
+ } else if (strcmp("disable-verity", longopts[longindex].name) == 0 ) {
+ g_disable_verity = true;
+ } else if (strcmp("disable-verification", longopts[longindex].name) == 0 ) {
+ g_disable_verification = true;
#if !defined(_WIN32)
} else if (strcmp("wipe-and-use-fbe", longopts[longindex].name) == 0) {
wants_wipe = true;
@@ -1562,20 +1632,15 @@
argc -= optind;
argv += optind;
- if (argc == 0 && !wants_wipe && !wants_set_active) {
- usage();
- return 1;
- }
+ if (argc == 0 && !wants_wipe && !wants_set_active) syntax_error("no command");
if (argc > 0 && !strcmp(*argv, "devices")) {
- skip(1);
list_devices();
return 0;
}
if (argc > 0 && !strcmp(*argv, "help")) {
- usage();
- return 0;
+ return show_help();
}
Transport* transport = open_device();
@@ -1604,17 +1669,19 @@
}
}
- while (argc > 0) {
- if (!strcmp(*argv, "getvar")) {
- require(2);
- fb_queue_display(argv[1], argv[1]);
- skip(2);
- } else if(!strcmp(*argv, "erase")) {
- require(2);
+ std::vector<std::string> args(argv, argv + argc);
+ while (!args.empty()) {
+ std::string command = next_arg(&args);
- auto erase = [&](const std::string &partition) {
+ if (command == "getvar") {
+ std::string variable = next_arg(&args);
+ fb_queue_display(variable.c_str(), variable.c_str());
+ } else if (command == "erase") {
+ std::string partition = next_arg(&args);
+ auto erase = [&](const std::string& partition) {
std::string partition_type;
- if (fb_getvar(transport, std::string("partition-type:") + argv[1], &partition_type) &&
+ if (fb_getvar(transport, std::string("partition-type:") + partition,
+ &partition_type) &&
fs_get_generator(partition_type) != nullptr) {
fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
partition_type.c_str());
@@ -1622,106 +1689,79 @@
fb_queue_erase(partition.c_str());
};
- do_for_partitions(transport, argv[1], slot_override, erase, true);
- skip(2);
- } else if(!strncmp(*argv, "format", strlen("format"))) {
- char *overrides;
- char *type_override = nullptr;
- char *size_override = nullptr;
- require(2);
- /*
- * Parsing for: "format[:[type][:[size]]]"
- * Some valid things:
- * - select ontly the size, and leave default fs type:
- * format::0x4000000 userdata
- * - default fs type and size:
- * format userdata
- * format:: userdata
- */
- overrides = strchr(*argv, ':');
- if (overrides) {
- overrides++;
- size_override = strchr(overrides, ':');
- if (size_override) {
- size_override[0] = '\0';
- size_override++;
- }
- type_override = overrides;
- }
- if (type_override && !type_override[0]) type_override = nullptr;
- if (size_override && !size_override[0]) size_override = nullptr;
+ do_for_partitions(transport, partition, slot_override, erase, true);
+ } else if (android::base::StartsWith(command, "format")) {
+ // Parsing for: "format[:[type][:[size]]]"
+ // Some valid things:
+ // - select only the size, and leave default fs type:
+ // format::0x4000000 userdata
+ // - default fs type and size:
+ // format userdata
+ // format:: userdata
+ std::vector<std::string> pieces = android::base::Split(command, ":");
+ std::string type_override;
+ if (pieces.size() > 1) type_override = pieces[1].c_str();
+ std::string size_override;
+ if (pieces.size() > 2) size_override = pieces[2].c_str();
- auto format = [&](const std::string &partition) {
+ std::string partition = next_arg(&args);
+
+ auto format = [&](const std::string& partition) {
if (erase_first && needs_erase(transport, partition.c_str())) {
fb_queue_erase(partition.c_str());
}
- fb_perform_format(transport, partition.c_str(), 0,
- type_override, size_override, "");
+ fb_perform_format(transport, partition.c_str(), 0, type_override, size_override,
+ "");
};
- do_for_partitions(transport, argv[1], slot_override, format, true);
- skip(2);
- } else if(!strcmp(*argv, "signature")) {
- require(2);
- data = load_file(argv[1], &sz);
- if (data == nullptr) die("could not load '%s': %s", argv[1], strerror(errno));
+ do_for_partitions(transport, partition.c_str(), slot_override, format, true);
+ } else if (command == "signature") {
+ std::string filename = next_arg(&args);
+ data = load_file(filename.c_str(), &sz);
+ if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
if (sz != 256) die("signature must be 256 bytes");
fb_queue_download("signature", data, sz);
fb_queue_command("signature", "installing signature");
- skip(2);
- } else if(!strcmp(*argv, "reboot")) {
+ } else if (command == "reboot") {
wants_reboot = true;
- skip(1);
- if (argc > 0) {
- if (!strcmp(*argv, "bootloader")) {
+
+ if (args.size() == 1) {
+ std::string what = next_arg(&args);
+ if (what == "bootloader") {
wants_reboot = false;
wants_reboot_bootloader = true;
- skip(1);
- } else if (!strcmp(*argv, "emergency")) {
+ } else if (what == "emergency") {
wants_reboot = false;
wants_reboot_emergency = true;
- skip(1);
+ } else {
+ syntax_error("unknown reboot target %s", what.c_str());
}
+
}
- require(0);
- } else if(!strcmp(*argv, "reboot-bootloader")) {
+ if (!args.empty()) syntax_error("junk after reboot command");
+ } else if (command == "reboot-bootloader") {
wants_reboot_bootloader = true;
- skip(1);
- } else if (!strcmp(*argv, "continue")) {
+ } else if (command == "continue") {
fb_queue_command("continue", "resuming boot");
- skip(1);
- } else if(!strcmp(*argv, "boot")) {
- char *kname = 0;
- char *rname = 0;
- char *sname = 0;
- skip(1);
- if (argc > 0) {
- kname = argv[0];
- skip(1);
- }
- if (argc > 0) {
- rname = argv[0];
- skip(1);
- }
- if (argc > 0) {
- sname = argv[0];
- skip(1);
- }
- data = load_bootable_image(kname, rname, sname, &sz, cmdline);
- if (data == 0) return 1;
+ } else if (command == "boot") {
+ std::string kernel = next_arg(&args);
+ std::string ramdisk;
+ if (!args.empty()) ramdisk = next_arg(&args);
+ std::string second_stage;
+ if (!args.empty()) second_stage = next_arg(&args);
+
+ data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline);
fb_queue_download("boot.img", data, sz);
fb_queue_command("boot", "booting");
- } else if(!strcmp(*argv, "flash")) {
- char* pname = argv[1];
+ } else if (command == "flash") {
+ std::string pname = next_arg(&args);
+
std::string fname;
- require(2);
- if (argc > 2) {
- fname = argv[2];
- skip(3);
+ if (!args.empty()) {
+ fname = next_arg(&args);
} else {
- fname = find_item(pname, product);
- skip(2);
+ fname = find_item(pname);
}
- if (fname.empty()) die("cannot determine image filename for '%s'", pname);
+ if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
auto flash = [&](const std::string &partition) {
if (erase_first && needs_erase(transport, partition.c_str())) {
@@ -1729,29 +1769,21 @@
}
do_flash(transport, partition.c_str(), fname.c_str());
};
- do_for_partitions(transport, pname, slot_override, flash, true);
- } else if(!strcmp(*argv, "flash:raw")) {
- char *kname = argv[2];
- char *rname = 0;
- char *sname = 0;
- require(3);
- skip(3);
- if (argc > 0) {
- rname = argv[0];
- skip(1);
- }
- if (argc > 0) {
- sname = argv[0];
- skip(1);
- }
- data = load_bootable_image(kname, rname, sname, &sz, cmdline);
- if (data == 0) die("cannot load bootable image");
- auto flashraw = [&](const std::string &partition) {
+ do_for_partitions(transport, pname.c_str(), slot_override, flash, true);
+ } else if (command == "flash:raw") {
+ std::string partition = next_arg(&args);
+ std::string kernel = next_arg(&args);
+ std::string ramdisk;
+ if (!args.empty()) ramdisk = next_arg(&args);
+ std::string second_stage;
+ if (!args.empty()) second_stage = next_arg(&args);
+
+ data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline);
+ auto flashraw = [&](const std::string& partition) {
fb_queue_flash(partition.c_str(), data, sz);
};
- do_for_partitions(transport, argv[1], slot_override, flashraw, true);
- } else if(!strcmp(*argv, "flashall")) {
- skip(1);
+ do_for_partitions(transport, partition, slot_override, flashraw, true);
+ } else if (command == "flashall") {
if (slot_override == "all") {
fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
do_flashall(transport, slot_override, erase_first, true);
@@ -1759,22 +1791,21 @@
do_flashall(transport, slot_override, erase_first, skip_secondary);
}
wants_reboot = true;
- } else if(!strcmp(*argv, "update")) {
+ } else if (command == "update") {
bool slot_all = (slot_override == "all");
if (slot_all) {
fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
}
- if (argc > 1) {
- do_update(transport, argv[1], slot_override, erase_first, skip_secondary || slot_all);
- skip(2);
- } else {
- do_update(transport, "update.zip", slot_override, erase_first, skip_secondary || slot_all);
- skip(1);
+ std::string filename = "update.zip";
+ if (!args.empty()) {
+ filename = next_arg(&args);
}
+ do_update(transport, filename.c_str(), slot_override, erase_first,
+ skip_secondary || slot_all);
wants_reboot = true;
- } else if(!strcmp(*argv, "set_active")) {
- require(2);
- std::string slot = verify_slot(transport, std::string(argv[1]), false);
+ } else if (command == "set_active") {
+ std::string slot = verify_slot(transport, next_arg(&args), false);
+
// Legacy support: verify_slot() removes leading underscores, we need to put them back
// in for old bootloaders. Legacy bootloaders do not have the slot-count variable but
// do have slot-suffixes.
@@ -1784,28 +1815,36 @@
slot = "_" + slot;
}
fb_set_active(slot.c_str());
- skip(2);
- } else if(!strcmp(*argv, "oem")) {
- argc = do_oem_command(argc, argv);
- } else if(!strcmp(*argv, "flashing")) {
- if (argc == 2 && (!strcmp(*(argv+1), "unlock") ||
- !strcmp(*(argv+1), "lock") ||
- !strcmp(*(argv+1), "unlock_critical") ||
- !strcmp(*(argv+1), "lock_critical") ||
- !strcmp(*(argv+1), "get_unlock_ability") ||
- !strcmp(*(argv+1), "get_unlock_bootloader_nonce") ||
- !strcmp(*(argv+1), "lock_bootloader"))) {
- argc = do_oem_command(argc, argv);
- } else
- if (argc == 3 && !strcmp(*(argv+1), "unlock_bootloader")) {
- argc = do_bypass_unlock_command(argc, argv);
+ } else if (command == "stage") {
+ std::string filename = next_arg(&args);
+
+ struct fastboot_buffer buf;
+ if (!load_buf(transport, filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
+ die("cannot load '%s'", filename.c_str());
+ }
+ fb_queue_download_fd(filename.c_str(), buf.fd, buf.sz);
+ } else if (command == "get_staged") {
+ std::string filename = next_arg(&args);
+ fb_queue_upload(filename.c_str());
+ } else if (command == "oem") {
+ do_oem_command("oem", &args);
+ } else if (command == "flashing") {
+ if (args.empty()) {
+ syntax_error("missing 'flashing' command");
+ } else if (args.size() == 1 && (args[0] == "unlock" || args[0] == "lock" ||
+ args[0] == "unlock_critical" ||
+ args[0] == "lock_critical" ||
+ args[0] == "get_unlock_ability" ||
+ args[0] == "get_unlock_bootloader_nonce" ||
+ args[0] == "lock_bootloader")) {
+ do_oem_command("flashing", &args);
+ } else if (args.size() == 2 && args[0] == "unlock_bootloader") {
+ do_bypass_unlock_command(&args);
} else {
- usage();
- return 1;
+ syntax_error("unknown 'flashing' command %s", args[0].c_str());
}
} else {
- usage();
- return 1;
+ syntax_error("unknown command %s", command.c_str());
}
}
@@ -1818,17 +1857,17 @@
if (initial_userdata_dir.empty()) {
return 1;
}
- fb_perform_format(transport, "userdata", 1, nullptr, nullptr, initial_userdata_dir);
+ fb_perform_format(transport, "userdata", 1, "", "", initial_userdata_dir);
delete_fbemarker_tmpdir(initial_userdata_dir);
} else {
- fb_perform_format(transport, "userdata", 1, nullptr, nullptr, "");
+ fb_perform_format(transport, "userdata", 1, "", "", "");
}
std::string cache_type;
if (fb_getvar(transport, "partition-type:cache", &cache_type) && !cache_type.empty()) {
fprintf(stderr, "wiping cache...\n");
fb_queue_erase("cache");
- fb_perform_format(transport, "cache", 1, nullptr, nullptr, "");
+ fb_perform_format(transport, "cache", 1, "", "", "");
}
}
if (wants_set_active) {
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 6699b6a..e3c60ae 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -41,8 +41,10 @@
/* protocol.c - fastboot protocol */
int fb_command(Transport* transport, const char* cmd);
int fb_command_response(Transport* transport, const char* cmd, char* response);
-int fb_download_data(Transport* transport, const void* data, uint32_t size);
+int64_t fb_download_data(Transport* transport, const void* data, uint32_t size);
+int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size);
int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
+int64_t fb_upload_data(Transport* transport, const char* outfile);
const std::string fb_get_error();
#define FB_COMMAND_SZ 64
@@ -51,6 +53,7 @@
/* engine.c - high level command queue engine */
bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
+void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz);
void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
size_t total);
void fb_queue_erase(const char *ptn);
@@ -62,9 +65,11 @@
void fb_queue_reboot(void);
void fb_queue_command(const char *cmd, const char *msg);
void fb_queue_download(const char *name, void *data, uint32_t size);
+void fb_queue_download_fd(const char *name, int fd, uint32_t sz);
+void fb_queue_upload(const char* outfile);
void fb_queue_notice(const char *notice);
void fb_queue_wait_for_disconnect(void);
-int fb_execute_queue(Transport* transport);
+int64_t fb_execute_queue(Transport* transport);
void fb_set_active(const char *slot);
/* util stuff */
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 5d9ccfe..709f061 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -4,34 +4,168 @@
#include "make_f2fs.h"
#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
+#ifndef WIN32
+#include <sys/wait.h>
+#else
+#include <tchar.h>
+#include <windows.h>
+#endif
#include <unistd.h>
+#include <vector>
+#include <android-base/errors.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
#include <ext4_utils/make_ext4fs.h>
#include <sparse/sparse.h>
-static int generate_ext4_image(int fd, long long partSize, const std::string& initial_dir,
- unsigned eraseBlkSize, unsigned logicalBlkSize)
-{
- if (initial_dir.empty()) {
- make_ext4fs_sparse_fd_align(fd, partSize, NULL, NULL, eraseBlkSize, logicalBlkSize);
- } else {
- make_ext4fs_sparse_fd_directory_align(fd, partSize, NULL, NULL, initial_dir.c_str(),
- eraseBlkSize, logicalBlkSize);
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+#ifdef WIN32
+static int exec_e2fs_cmd(const char* path, char* const argv[]) {
+ std::string cmd;
+ int i = 0;
+ while (argv[i] != nullptr) {
+ cmd += argv[i++];
+ cmd += " ";
}
+ cmd = cmd.substr(0, cmd.size() - 1);
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ DWORD exit_code = 0;
+
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+ ZeroMemory(&pi, sizeof(pi));
+
+ SetEnvironmentVariableA("MKE2FS_CONFIG", "");
+
+ if (!CreateProcessA(nullptr, // No module name (use command line)
+ const_cast<char*>(cmd.c_str()), // Command line
+ nullptr, // Process handle not inheritable
+ nullptr, // Thread handle not inheritable
+ FALSE, // Set handle inheritance to FALSE
+ 0, // No creation flags
+ nullptr, // Use parent's environment block
+ nullptr, // Use parent's starting directory
+ &si, // Pointer to STARTUPINFO structure
+ &pi) // Pointer to PROCESS_INFORMATION structure
+ ) {
+ fprintf(stderr, "CreateProcess failed: %s\n",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ return -1;
+ }
+
+ WaitForSingleObject(pi.hProcess, INFINITE);
+
+ GetExitCodeProcess(pi.hProcess, &exit_code);
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+
+ return exit_code != 0;
+}
+#else
+static int exec_e2fs_cmd(const char* path, char* const argv[]) {
+ int status;
+ pid_t child;
+ if ((child = fork()) == 0) {
+ setenv("MKE2FS_CONFIG", "", 1);
+ execvp(path, argv);
+ _exit(EXIT_FAILURE);
+ }
+ if (child < 0) {
+ fprintf(stderr, "%s failed with fork %s\n", path, strerror(errno));
+ return -1;
+ }
+ if (TEMP_FAILURE_RETRY(waitpid(child, &status, 0)) == -1) {
+ fprintf(stderr, "%s failed with waitpid %s\n", path, strerror(errno));
+ return -1;
+ }
+ int ret = -1;
+ if (WIFEXITED(status)) {
+ ret = WEXITSTATUS(status);
+ if (ret != 0) {
+ fprintf(stderr, "%s failed with status %d\n", path, ret);
+ }
+ }
+ return ret;
+}
+#endif
+
+static int generate_ext4_image(const char* fileName, long long partSize,
+ const std::string& initial_dir, unsigned eraseBlkSize,
+ unsigned logicalBlkSize) {
+ static constexpr int block_size = 4096;
+ const std::string exec_dir = android::base::GetExecutableDirectory();
+
+ const std::string mke2fs_path = exec_dir + "/mke2fs";
+ std::vector<const char*> mke2fs_args = {mke2fs_path.c_str(), "-t", "ext4", "-b"};
+
+ std::string block_size_str = std::to_string(block_size);
+ mke2fs_args.push_back(block_size_str.c_str());
+
+ std::string ext_attr = "android_sparse";
+ if (eraseBlkSize != 0 && logicalBlkSize != 0) {
+ int raid_stride = logicalBlkSize / block_size;
+ int raid_stripe_width = eraseBlkSize / block_size;
+ // stride should be the max of 8kb and logical block size
+ if (logicalBlkSize != 0 && logicalBlkSize < 8192) raid_stride = 8192 / block_size;
+ ext_attr += StringPrintf(",stride=%d,stripe-width=%d", raid_stride, raid_stripe_width);
+ }
+ mke2fs_args.push_back("-E");
+ mke2fs_args.push_back(ext_attr.c_str());
+ mke2fs_args.push_back("-O");
+ mke2fs_args.push_back("uninit_bg");
+ mke2fs_args.push_back(fileName);
+
+ std::string size_str = std::to_string(partSize / block_size);
+ mke2fs_args.push_back(size_str.c_str());
+ mke2fs_args.push_back(nullptr);
+
+ int ret = exec_e2fs_cmd(mke2fs_args[0], const_cast<char**>(mke2fs_args.data()));
+ if (ret != 0) {
+ fprintf(stderr, "mke2fs failed: %d\n", ret);
+ return -1;
+ }
+
+ if (initial_dir.empty()) {
+ return 0;
+ }
+
+ const std::string e2fsdroid_path = exec_dir + "/e2fsdroid";
+ std::vector<const char*> e2fsdroid_args = {e2fsdroid_path.c_str(), "-f", initial_dir.c_str(),
+ fileName, nullptr};
+
+ ret = exec_e2fs_cmd(e2fsdroid_args[0], const_cast<char**>(e2fsdroid_args.data()));
+ if (ret != 0) {
+ fprintf(stderr, "e2fsdroid failed: %d\n", ret);
+ return -1;
+ }
+
return 0;
}
#ifdef USE_F2FS
-static int generate_f2fs_image(int fd, long long partSize, const std::string& initial_dir,
+static int generate_f2fs_image(const char* fileName, long long partSize, const std::string& initial_dir,
unsigned /* unused */, unsigned /* unused */)
{
if (!initial_dir.empty()) {
- fprintf(stderr, "Unable to set initial directory on F2FS filesystem\n");
+ fprintf(stderr, "Unable to set initial directory on F2FS filesystem: %s\n", strerror(errno));
+ return -1;
+ }
+ unique_fd fd(open(fileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
+ if (fd == -1) {
+ fprintf(stderr, "Unable to open output file for F2FS filesystem: %s\n", strerror(errno));
return -1;
}
return make_f2fs_sparse_fd(fd, partSize, NULL, NULL);
@@ -42,7 +176,7 @@
const char* fs_type; //must match what fastboot reports for partition type
//returns 0 or error value
- int (*generate)(int fd, long long partSize, const std::string& initial_dir,
+ int (*generate)(const char* fileName, long long partSize, const std::string& initial_dir,
unsigned eraseBlkSize, unsigned logicalBlkSize);
} generators[] = {
@@ -61,8 +195,8 @@
return nullptr;
}
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
+int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
const std::string& initial_dir, unsigned eraseBlkSize, unsigned logicalBlkSize)
{
- return gen->generate(tmpFileNo, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
+ return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
}
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 0a5f5a4..c6baa7f 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -7,7 +7,7 @@
struct fs_generator;
const struct fs_generator* fs_get_generator(const std::string& fs_type);
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
+int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
const std::string& initial_dir, unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0);
#endif
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
index bfa83b0..dcdf8f0 100644
--- a/fastboot/protocol.cpp
+++ b/fastboot/protocol.cpp
@@ -29,26 +29,34 @@
#define round_down(a, b) \
({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <algorithm>
+#include <vector>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
#include <sparse/sparse.h>
+#include <utils/FileMap.h>
#include "fastboot.h"
#include "transport.h"
static std::string g_error;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
+
const std::string fb_get_error() {
return g_error;
}
-static int check_response(Transport* transport, uint32_t size, char* response) {
+static int64_t check_response(Transport* transport, uint32_t size, char* response) {
char status[65];
while (true) {
@@ -105,7 +113,7 @@
return -1;
}
-static int _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
+static int64_t _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
size_t cmdsize = strlen(cmd);
if (cmdsize > 64) {
g_error = android::base::StringPrintf("command too large (%zu)", cmdsize);
@@ -125,37 +133,51 @@
return check_response(transport, size, response);
}
-static int _command_data(Transport* transport, const void* data, uint32_t size) {
- int r = transport->Write(data, size);
+static int64_t _command_write_data(Transport* transport, const void* data, uint32_t size) {
+ int64_t r = transport->Write(data, size);
if (r < 0) {
- g_error = android::base::StringPrintf("data transfer failure (%s)", strerror(errno));
+ g_error = android::base::StringPrintf("data write failure (%s)", strerror(errno));
transport->Close();
return -1;
}
- if (r != ((int) size)) {
- g_error = "data transfer failure (short transfer)";
+ if (r != static_cast<int64_t>(size)) {
+ g_error = "data write failure (short transfer)";
transport->Close();
return -1;
}
return r;
}
-static int _command_end(Transport* transport) {
+static int64_t _command_read_data(Transport* transport, void* data, uint32_t size) {
+ int64_t r = transport->Read(data, size);
+ if (r < 0) {
+ g_error = android::base::StringPrintf("data read failure (%s)", strerror(errno));
+ transport->Close();
+ return -1;
+ }
+ if (r != (static_cast<int64_t>(size))) {
+ g_error = "data read failure (short transfer)";
+ transport->Close();
+ return -1;
+ }
+ return r;
+}
+
+static int64_t _command_end(Transport* transport) {
return check_response(transport, 0, 0) < 0 ? -1 : 0;
}
-static int _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
+static int64_t _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
char* response) {
if (size == 0) {
return -1;
}
- int r = _command_start(transport, cmd, size, response);
+ int64_t r = _command_start(transport, cmd, size, response);
if (r < 0) {
return -1;
}
-
- r = _command_data(transport, data, size);
+ r = _command_write_data(transport, data, size);
if (r < 0) {
return -1;
}
@@ -168,6 +190,39 @@
return size;
}
+static int64_t _command_send_fd(Transport* transport, const char* cmd, int fd, uint32_t size,
+ char* response) {
+ static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
+ off64_t offset = 0;
+ uint32_t remaining = size;
+
+ if (_command_start(transport, cmd, size, response) < 0) {
+ return -1;
+ }
+
+ while (remaining) {
+ android::FileMap filemap;
+ size_t len = std::min(remaining, MAX_MAP_SIZE);
+
+ if (!filemap.create(NULL, fd, offset, len, true)) {
+ return -1;
+ }
+
+ if (_command_write_data(transport, filemap.getDataPtr(), len) < 0) {
+ return -1;
+ }
+
+ remaining -= len;
+ offset += len;
+ }
+
+ if (_command_end(transport) < 0) {
+ return -1;
+ }
+
+ return size;
+}
+
static int _command_send_no_data(Transport* transport, const char* cmd, char* response) {
return _command_start(transport, cmd, 0, response);
}
@@ -180,10 +235,36 @@
return _command_send_no_data(transport, cmd, response);
}
-int fb_download_data(Transport* transport, const void* data, uint32_t size) {
- char cmd[64];
- snprintf(cmd, sizeof(cmd), "download:%08x", size);
- return _command_send(transport, cmd, data, size, 0) < 0 ? -1 : 0;
+int64_t fb_download_data(Transport* transport, const void* data, uint32_t size) {
+ std::string cmd(android::base::StringPrintf("download:%08x", size));
+ return _command_send(transport, cmd.c_str(), data, size, 0) < 0 ? -1 : 0;
+}
+
+int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size) {
+ std::string cmd(android::base::StringPrintf("download:%08x", size));
+ return _command_send_fd(transport, cmd.c_str(), fd, size, 0) < 0 ? -1 : 0;
+}
+
+int64_t fb_upload_data(Transport* transport, const char* outfile) {
+ // positive return value is the upload size sent by the device
+ int64_t r = _command_start(transport, "upload", std::numeric_limits<int32_t>::max(), nullptr);
+ if (r <= 0) {
+ g_error = android::base::StringPrintf("command start failed (%s)", strerror(errno));
+ return r;
+ }
+
+ std::string data;
+ data.resize(r);
+ if ((r = _command_read_data(transport, &data[0], data.size())) == -1) {
+ return r;
+ }
+
+ if (!WriteStringToFile(data, outfile, true)) {
+ g_error = android::base::StringPrintf("write to '%s' failed", outfile);
+ return -1;
+ }
+
+ return _command_end(transport);
}
#define TRANSPORT_BUF_SIZE 1024
@@ -207,7 +288,7 @@
}
if (transport_buf_len == TRANSPORT_BUF_SIZE) {
- r = _command_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
+ r = _command_write_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
if (r != TRANSPORT_BUF_SIZE) {
return -1;
}
@@ -220,7 +301,7 @@
return -1;
}
to_write = round_down(len, TRANSPORT_BUF_SIZE);
- r = _command_data(transport, ptr, to_write);
+ r = _command_write_data(transport, ptr, to_write);
if (r != to_write) {
return -1;
}
@@ -242,7 +323,8 @@
static int fb_download_data_sparse_flush(Transport* transport) {
if (transport_buf_len > 0) {
- if (_command_data(transport, transport_buf, transport_buf_len) != transport_buf_len) {
+ int64_t r = _command_write_data(transport, transport_buf, transport_buf_len);
+ if (r != static_cast<int64_t>(transport_buf_len)) {
return -1;
}
transport_buf_len = 0;
@@ -256,9 +338,8 @@
return -1;
}
- char cmd[64];
- snprintf(cmd, sizeof(cmd), "download:%08x", size);
- int r = _command_start(transport, cmd, size, 0);
+ std::string cmd(android::base::StringPrintf("download:%08x", size));
+ int r = _command_start(transport, cmd.c_str(), size, 0);
if (r < 0) {
return -1;
}
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 9069baa..e95b049 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -93,12 +93,9 @@
SInt32 score;
UInt8 interfaceNumEndpoints;
- // Placing the constant KIOUSBFindInterfaceDontCare into the following
- // fields of the IOUSBFindInterfaceRequest structure will allow us to
- // find all of the interfaces
- request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
- request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
- request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceClass = 0xff;
+ request.bInterfaceSubClass = 0x42;
+ request.bInterfaceProtocol = 0x03;
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
// Get an iterator for the interfaces on the device
@@ -282,7 +279,6 @@
&plugin, &score);
if ((kr != 0) || (plugin == NULL)) {
- ERR("Unable to create a plug-in (%08x)\n", kr);
goto error;
}
@@ -436,8 +432,7 @@
if (try_device(device, &h) != 0) {
IOObjectRelease(device);
- ret = -1;
- break;
+ continue;
}
if (h.success) {
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
new file mode 100644
index 0000000..7fd67c2
--- /dev/null
+++ b/fs_mgr/Android.bp
@@ -0,0 +1,79 @@
+//
+// 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.
+//
+
+cc_defaults {
+ name: "fs_mgr_defaults",
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+ local_include_dirs: ["include/"],
+ cppflags: ["-Werror"],
+}
+
+cc_library_static {
+ name: "libfs_mgr",
+ defaults: ["fs_mgr_defaults"],
+ export_include_dirs: ["include"],
+ include_dirs: ["system/vold"],
+ srcs: [
+ "fs_mgr.cpp",
+ "fs_mgr_dm_ioctl.cpp",
+ "fs_mgr_format.cpp",
+ "fs_mgr_verity.cpp",
+ "fs_mgr_avb.cpp",
+ "fs_mgr_avb_ops.cpp",
+ ],
+ static_libs: [
+ "libfec",
+ "libfec_rs",
+ "libbase",
+ "libcrypto_utils",
+ "libcrypto",
+ "libext4_utils",
+ "libsquashfs_utils",
+ "libselinux",
+ "libavb",
+ "libfstab",
+ ],
+ export_static_lib_headers: [
+ "libfstab",
+ ],
+ whole_static_libs: [
+ "liblogwrap",
+ "libfstab",
+ ],
+ product_variables: {
+ debuggable: {
+ cppflags: ["-DALLOW_ADBD_DISABLE_VERITY=1"],
+ },
+ eng: {
+ cppflags: ["-DALLOW_SKIP_SECURE_CHECK=1"],
+ },
+ },
+}
+
+cc_library_static {
+ name: "libfstab",
+ vendor_available: true,
+ defaults: ["fs_mgr_defaults"],
+ srcs: [
+ "fs_mgr_fstab.cpp",
+ "fs_mgr_boot_config.cpp",
+ "fs_mgr_slotselect.cpp",
+ ],
+ export_include_dirs: ["include_fstab"],
+ header_libs: ["libbase_headers"],
+}
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 2863a26..007189d 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -15,41 +15,12 @@
libavb
include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-LOCAL_SRC_FILES:= \
- fs_mgr.cpp \
- fs_mgr_dm_ioctl.cpp \
- fs_mgr_format.cpp \
- fs_mgr_fstab.cpp \
- fs_mgr_slotselect.cpp \
- fs_mgr_verity.cpp \
- fs_mgr_avb.cpp \
- fs_mgr_avb_ops.cpp \
- fs_mgr_boot_config.cpp
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/include \
- system/vold \
- system/extras/ext4_utils
-LOCAL_MODULE:= libfs_mgr
-LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT)))
-LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
-endif
-ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
-LOCAL_CFLAGS += -DALLOW_SKIP_SECURE_CHECK=1
-endif
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
LOCAL_SANITIZE := integer
LOCAL_SRC_FILES:= fs_mgr_main.cpp
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_MODULE:= fs_mgr
LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := mke2fs mke2fs.conf e2fsdroid
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 803b894..874189a 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -32,6 +32,7 @@
#include <unistd.h>
#include <memory>
+#include <thread>
#include <android-base/file.h>
#include <android-base/properties.h>
@@ -78,43 +79,33 @@
FS_STAT_E2FSCK_F_ALWAYS = 0x0004,
FS_STAT_UNCLEAN_SHUTDOWN = 0x0008,
FS_STAT_QUOTA_ENABLED = 0x0010,
- FS_STAT_TUNE2FS_FAILED = 0x0020,
FS_STAT_RO_MOUNT_FAILED = 0x0040,
FS_STAT_RO_UNMOUNT_FAILED = 0x0080,
FS_STAT_FULL_MOUNT_FAILED = 0x0100,
FS_STAT_E2FSCK_FAILED = 0x0200,
FS_STAT_E2FSCK_FS_FIXED = 0x0400,
FS_STAT_EXT4_INVALID_MAGIC = 0x0800,
+ FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
+ FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
+ FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,
};
-/*
- * gettime() - returns the time in seconds of the system's monotonic clock or
- * zero on error.
- */
-static time_t gettime(void)
-{
- struct timespec ts;
- int ret;
+// TODO: switch to inotify()
+bool fs_mgr_wait_for_file(const std::string& filename,
+ const std::chrono::milliseconds relative_timeout) {
+ auto start_time = std::chrono::steady_clock::now();
- ret = clock_gettime(CLOCK_MONOTONIC, &ts);
- if (ret < 0) {
- PERROR << "clock_gettime(CLOCK_MONOTONIC) failed";
- return 0;
+ while (true) {
+ if (!access(filename.c_str(), F_OK) || errno != ENOENT) {
+ return true;
+ }
+
+ std::this_thread::sleep_for(50ms);
+
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed > relative_timeout) return false;
}
-
- return ts.tv_sec;
-}
-
-static int wait_for_file(const char *filename, int timeout)
-{
- struct stat info;
- time_t timeout_time = gettime() + timeout;
- int ret = -1;
-
- while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
- usleep(10000);
-
- return ret;
}
static void log_fs_stat(const char* blk_device, int fs_stat)
@@ -128,10 +119,16 @@
}
}
+static bool is_extfs(const std::string& fs_type) {
+ return fs_type == "ext4" || fs_type == "ext3" || fs_type == "ext2";
+}
+
static bool should_force_check(int fs_stat) {
- return fs_stat & (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
- FS_STAT_TUNE2FS_FAILED | FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED |
- FS_STAT_FULL_MOUNT_FAILED | FS_STAT_E2FSCK_FAILED);
+ return fs_stat &
+ (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
+ FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED | FS_STAT_FULL_MOUNT_FAILED |
+ FS_STAT_E2FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |
+ FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED);
}
static void check_fs(const char *blk_device, char *fs_type, char *target, int *fs_stat)
@@ -144,7 +141,7 @@
const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device};
/* Check for the types of filesystems we know how to check */
- if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
+ if (is_extfs(fs_type)) {
if (*fs_stat & FS_STAT_EXT4_INVALID_MAGIC) { // will fail, so do not try
return;
}
@@ -242,186 +239,214 @@
return;
}
-/* Function to read the primary superblock */
-static int read_super_block(int fd, struct ext4_super_block *sb)
-{
- off64_t ret;
-
- ret = lseek64(fd, 1024, SEEK_SET);
- if (ret < 0)
- return ret;
-
- ret = read(fd, sb, sizeof(*sb));
- if (ret < 0)
- return ret;
- if (ret != sizeof(*sb))
- return ret;
-
- return 0;
-}
-
-static ext4_fsblk_t ext4_blocks_count(struct ext4_super_block *es)
-{
+static ext4_fsblk_t ext4_blocks_count(const struct ext4_super_block* es) {
return ((ext4_fsblk_t)le32_to_cpu(es->s_blocks_count_hi) << 32) |
- le32_to_cpu(es->s_blocks_count_lo);
+ le32_to_cpu(es->s_blocks_count_lo);
}
-static ext4_fsblk_t ext4_r_blocks_count(struct ext4_super_block *es)
-{
+static ext4_fsblk_t ext4_r_blocks_count(const struct ext4_super_block* es) {
return ((ext4_fsblk_t)le32_to_cpu(es->s_r_blocks_count_hi) << 32) |
- le32_to_cpu(es->s_r_blocks_count_lo);
+ le32_to_cpu(es->s_r_blocks_count_lo);
}
-static int do_quota_with_shutdown_check(char *blk_device, char *fs_type,
- struct fstab_rec *rec, int *fs_stat)
-{
- int force_check = 0;
- if (!strcmp(fs_type, "ext4")) {
- /*
- * Some system images do not have tune2fs for licensing reasons
- * Detect these and skip reserve blocks.
- */
- if (access(TUNE2FS_BIN, X_OK)) {
- LERROR << "Not running " << TUNE2FS_BIN << " on "
- << blk_device << " (executable not in system image)";
- } else {
- const char* arg1 = nullptr;
- const char* arg2 = nullptr;
- int status = 0;
- int ret = 0;
- android::base::unique_fd fd(
- TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
- if (fd >= 0) {
- struct ext4_super_block sb;
- ret = read_super_block(fd, &sb);
- if (ret < 0) {
- PERROR << "Can't read '" << blk_device << "' super block";
- return force_check;
- }
- if (sb.s_magic != EXT4_SUPER_MAGIC) {
- LINFO << "Invalid ext4 magic:0x" << std::hex << sb.s_magic << "," << blk_device;
- *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
- return 0; // not a valid fs, tune2fs, fsck, and mount will all fail.
- }
- *fs_stat |= FS_STAT_IS_EXT4;
- LINFO << "superblock s_max_mnt_count:" << sb.s_max_mnt_count << "," << blk_device;
- if (sb.s_max_mnt_count == 0xffff) { // -1 (int16) in ext2, but uint16 in ext4
- *fs_stat |= FS_STAT_NEW_IMAGE_VERSION;
- }
- if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
- (sb.s_state & EXT4_VALID_FS) == 0) {
- LINFO << __FUNCTION__ << "(): was not clealy shutdown, state flag:"
- << std::hex << sb.s_state
- << "incompat flag:" << std::hex << sb.s_feature_incompat;
- force_check = 1;
- *fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN;
- }
- int has_quota = (sb.s_feature_ro_compat
- & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
- int want_quota = fs_mgr_is_quota(rec) != 0;
-
- if (has_quota == want_quota) {
- LINFO << "Requested quota status is match on " << blk_device;
- return force_check;
- } else if (want_quota) {
- LINFO << "Enabling quota on " << blk_device;
- arg1 = "-Oquota";
- arg2 = "-Qusrquota,grpquota";
- force_check = 1;
- *fs_stat |= FS_STAT_QUOTA_ENABLED;
- } else {
- LINFO << "Disabling quota on " << blk_device;
- arg1 = "-Q^usrquota,^grpquota";
- arg2 = "-O^quota";
- }
- } else {
- PERROR << "Failed to open '" << blk_device << "'";
- return force_check;
- }
-
- const char *tune2fs_argv[] = {
- TUNE2FS_BIN,
- arg1,
- arg2,
- blk_device,
- };
- ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
- const_cast<char **>(tune2fs_argv),
- &status, true, LOG_KLOG | LOG_FILE,
- true, NULL, NULL, 0);
- if (ret < 0) {
- /* No need to check for error in fork, we can't really handle it now */
- LERROR << "Failed trying to run " << TUNE2FS_BIN;
- *fs_stat |= FS_STAT_TUNE2FS_FAILED;
- }
- }
- }
- return force_check;
+static bool is_ext4_superblock_valid(const struct ext4_super_block* es) {
+ if (es->s_magic != EXT4_SUPER_MAGIC) return false;
+ if (es->s_rev_level != EXT4_DYNAMIC_REV && es->s_rev_level != EXT4_GOOD_OLD_REV) return false;
+ if (EXT4_INODES_PER_GROUP(es) == 0) return false;
+ return true;
}
-static void do_reserved_size(char *blk_device, char *fs_type, struct fstab_rec *rec, int *fs_stat)
-{
- /* Check for the types of filesystems we know how to check */
- if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
- /*
- * Some system images do not have tune2fs for licensing reasons
- * Detect these and skip reserve blocks.
- */
- if (access(TUNE2FS_BIN, X_OK)) {
- LERROR << "Not running " << TUNE2FS_BIN << " on "
- << blk_device << " (executable not in system image)";
+// Read the primary superblock from an ext4 filesystem. On failure return
+// false. If it's not an ext4 filesystem, also set FS_STAT_EXT4_INVALID_MAGIC.
+static bool read_ext4_superblock(const char* blk_device, struct ext4_super_block* sb, int* fs_stat) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
+
+ if (fd < 0) {
+ PERROR << "Failed to open '" << blk_device << "'";
+ return false;
+ }
+
+ if (pread(fd, sb, sizeof(*sb), 1024) != sizeof(*sb)) {
+ PERROR << "Can't read '" << blk_device << "' superblock";
+ return false;
+ }
+
+ if (!is_ext4_superblock_valid(sb)) {
+ LINFO << "Invalid ext4 superblock on '" << blk_device << "'";
+ // not a valid fs, tune2fs, fsck, and mount will all fail.
+ *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
+ return false;
+ }
+ *fs_stat |= FS_STAT_IS_EXT4;
+ LINFO << "superblock s_max_mnt_count:" << sb->s_max_mnt_count << "," << blk_device;
+ if (sb->s_max_mnt_count == 0xffff) { // -1 (int16) in ext2, but uint16 in ext4
+ *fs_stat |= FS_STAT_NEW_IMAGE_VERSION;
+ }
+ return true;
+}
+
+// Some system images do not have tune2fs for licensing reasons.
+// Detect these and skip running it.
+static bool tune2fs_available(void) {
+ return access(TUNE2FS_BIN, X_OK) == 0;
+}
+
+static bool run_tune2fs(const char* argv[], int argc) {
+ int ret;
+
+ ret = android_fork_execvp_ext(argc, const_cast<char**>(argv), nullptr, true,
+ LOG_KLOG | LOG_FILE, true, nullptr, nullptr, 0);
+ return ret == 0;
+}
+
+// Enable/disable quota support on the filesystem if needed.
+static void tune_quota(const char* blk_device, const struct fstab_rec* rec,
+ const struct ext4_super_block* sb, int* fs_stat) {
+ bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
+ bool want_quota = fs_mgr_is_quota(rec) != 0;
+
+ if (has_quota == want_quota) {
+ return;
+ }
+
+ if (!tune2fs_available()) {
+ LERROR << "Unable to " << (want_quota ? "enable" : "disable") << " quotas on " << blk_device
+ << " because " TUNE2FS_BIN " is missing";
+ return;
+ }
+
+ const char* argv[] = {TUNE2FS_BIN, nullptr, nullptr, blk_device};
+
+ if (want_quota) {
+ LINFO << "Enabling quotas on " << blk_device;
+ argv[1] = "-Oquota";
+ argv[2] = "-Qusrquota,grpquota";
+ *fs_stat |= FS_STAT_QUOTA_ENABLED;
+ } else {
+ LINFO << "Disabling quotas on " << blk_device;
+ argv[1] = "-O^quota";
+ argv[2] = "-Q^usrquota,^grpquota";
+ }
+
+ if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+ LERROR << "Failed to run " TUNE2FS_BIN " to " << (want_quota ? "enable" : "disable")
+ << " quotas on " << blk_device;
+ *fs_stat |= FS_STAT_TOGGLE_QUOTAS_FAILED;
+ }
+}
+
+// Set the number of reserved filesystem blocks if needed.
+static void tune_reserved_size(const char* blk_device, const struct fstab_rec* rec,
+ const struct ext4_super_block* sb, int* fs_stat) {
+ if (!(rec->fs_mgr_flags & MF_RESERVEDSIZE)) {
+ return;
+ }
+
+ // The size to reserve is given in the fstab, but we won't reserve more
+ // than 2% of the filesystem.
+ const uint64_t max_reserved_blocks = ext4_blocks_count(sb) * 0.02;
+ uint64_t reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(sb);
+
+ if (reserved_blocks > max_reserved_blocks) {
+ LWARNING << "Reserved blocks " << reserved_blocks << " is too large; "
+ << "capping to " << max_reserved_blocks;
+ reserved_blocks = max_reserved_blocks;
+ }
+
+ if (ext4_r_blocks_count(sb) == reserved_blocks) {
+ return;
+ }
+
+ if (!tune2fs_available()) {
+ LERROR << "Unable to set the number of reserved blocks on " << blk_device
+ << " because " TUNE2FS_BIN " is missing";
+ return;
+ }
+
+ char buf[32];
+ const char* argv[] = {TUNE2FS_BIN, "-r", buf, blk_device};
+
+ snprintf(buf, sizeof(buf), "%" PRIu64, reserved_blocks);
+ LINFO << "Setting reserved block count on " << blk_device << " to " << reserved_blocks;
+ if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+ LERROR << "Failed to run " TUNE2FS_BIN " to set the number of reserved blocks on "
+ << blk_device;
+ *fs_stat |= FS_STAT_SET_RESERVED_BLOCKS_FAILED;
+ }
+}
+
+// Enable file-based encryption if needed.
+static void tune_encrypt(const char* blk_device, const struct fstab_rec* rec,
+ const struct ext4_super_block* sb, int* fs_stat) {
+ bool has_encrypt = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) != 0;
+ bool want_encrypt = fs_mgr_is_file_encrypted(rec) != 0;
+
+ if (has_encrypt || !want_encrypt) {
+ return;
+ }
+
+ if (!tune2fs_available()) {
+ LERROR << "Unable to enable ext4 encryption on " << blk_device
+ << " because " TUNE2FS_BIN " is missing";
+ return;
+ }
+
+ const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device};
+
+ LINFO << "Enabling ext4 encryption on " << blk_device;
+ if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+ LERROR << "Failed to run " TUNE2FS_BIN " to enable "
+ << "ext4 encryption on " << blk_device;
+ *fs_stat |= FS_STAT_ENABLE_ENCRYPTION_FAILED;
+ }
+}
+
+//
+// Prepare the filesystem on the given block device to be mounted.
+//
+// If the "check" option was given in the fstab record, or it seems that the
+// filesystem was uncleanly shut down, we'll run fsck on the filesystem.
+//
+// If needed, we'll also enable (or disable) filesystem features as specified by
+// the fstab record.
+//
+static int prepare_fs_for_mount(const char* blk_device, const struct fstab_rec* rec) {
+ int fs_stat = 0;
+
+ if (is_extfs(rec->fs_type)) {
+ struct ext4_super_block sb;
+
+ if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
+ if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
+ (sb.s_state & EXT4_VALID_FS) == 0) {
+ LINFO << "Filesystem on " << blk_device << " was not cleanly shutdown; "
+ << "state flags: 0x" << std::hex << sb.s_state << ", "
+ << "incompat feature flags: 0x" << std::hex << sb.s_feature_incompat;
+ fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN;
+ }
+
+ // Note: quotas should be enabled before running fsck.
+ tune_quota(blk_device, rec, &sb, &fs_stat);
} else {
- LINFO << "Running " << TUNE2FS_BIN << " on " << blk_device;
-
- int status = 0;
- int ret = 0;
- unsigned long reserved_blocks = 0;
- android::base::unique_fd fd(
- TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
- if (fd >= 0) {
- struct ext4_super_block sb;
- ret = read_super_block(fd, &sb);
- if (ret < 0) {
- PERROR << "Can't read '" << blk_device << "' super block";
- return;
- }
- reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(&sb);
- unsigned long reserved_threshold = ext4_blocks_count(&sb) * 0.02;
- if (reserved_threshold < reserved_blocks) {
- LWARNING << "Reserved blocks " << reserved_blocks
- << " is too large";
- reserved_blocks = reserved_threshold;
- }
-
- if (ext4_r_blocks_count(&sb) == reserved_blocks) {
- LINFO << "Have reserved same blocks";
- return;
- }
- } else {
- PERROR << "Failed to open '" << blk_device << "'";
- return;
- }
-
- char buf[16] = {0};
- snprintf(buf, sizeof (buf), "-r %lu", reserved_blocks);
- const char *tune2fs_argv[] = {
- TUNE2FS_BIN,
- buf,
- blk_device,
- };
-
- ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
- const_cast<char **>(tune2fs_argv),
- &status, true, LOG_KLOG | LOG_FILE,
- true, NULL, NULL, 0);
-
- if (ret < 0) {
- /* No need to check for error in fork, we can't really handle it now */
- LERROR << "Failed trying to run " << TUNE2FS_BIN;
- *fs_stat |= FS_STAT_TUNE2FS_FAILED;
- }
+ return fs_stat;
}
}
+
+ if ((rec->fs_mgr_flags & MF_CHECK) ||
+ (fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
+ check_fs(blk_device, rec->fs_type, rec->mount_point, &fs_stat);
+ }
+
+ if (is_extfs(rec->fs_type) && (rec->fs_mgr_flags & (MF_RESERVEDSIZE | MF_FILEENCRYPTION))) {
+ struct ext4_super_block sb;
+
+ if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
+ tune_reserved_size(blk_device, rec, &sb, &fs_stat);
+ tune_encrypt(blk_device, rec, &sb, &fs_stat);
+ }
+ }
+
+ return fs_stat;
}
static void remove_trailing_slashes(char *n)
@@ -457,6 +482,16 @@
return rc;
}
+// Orange state means the device is unlocked, see the following link for details.
+// https://source.android.com/security/verifiedboot/verified-boot#device_state
+bool fs_mgr_is_device_unlocked() {
+ std::string verified_boot_state;
+ if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) {
+ return verified_boot_state == "orange";
+ }
+ return false;
+}
+
/*
* __mount(): wrapper around the mount() system call which also
* sets the underlying block device to read-only if the mount is read-only.
@@ -476,10 +511,11 @@
if ((info.st_mode & S_IFMT) == S_IFLNK)
unlink(target);
mkdir(target, 0755);
+ errno = 0;
ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
save_errno = errno;
- LINFO << __FUNCTION__ << "(source=" << source << ",target="
- << target << ",type=" << rec->fs_type << ")=" << ret;
+ PINFO << __FUNCTION__ << "(source=" << source << ",target=" << target
+ << ",type=" << rec->fs_type << ")=" << ret;
if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
fs_mgr_set_blk_ro(source);
}
@@ -559,10 +595,7 @@
continue;
}
- int fs_stat = 0;
- int force_check = do_quota_with_shutdown_check(fstab->recs[i].blk_device,
- fstab->recs[i].fs_type,
- &fstab->recs[i], &fs_stat);
+ int fs_stat = prepare_fs_for_mount(fstab->recs[i].blk_device, &fstab->recs[i]);
if (fs_stat & FS_STAT_EXT4_INVALID_MAGIC) {
LERROR << __FUNCTION__ << "(): skipping mount, invalid ext4, mountpoint="
<< fstab->recs[i].mount_point << " rec[" << i
@@ -570,15 +603,6 @@
mount_errno = EINVAL; // continue bootup for FDE
continue;
}
- if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
- check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
- fstab->recs[i].mount_point, &fs_stat);
- }
-
- if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
- do_reserved_size(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
- &fstab->recs[i], &fs_stat);
- }
int retry_count = 2;
while (retry_count-- > 0) {
@@ -744,19 +768,6 @@
}
}
-// TODO: add ueventd notifiers if they don't exist.
-// This is just doing a wait_for_device for maximum of 1s
-int fs_mgr_test_access(const char *device) {
- int tries = 25;
- while (tries--) {
- if (!access(device, F_OK) || errno != ENOENT) {
- return 0;
- }
- usleep(40 * 1000);
- }
- return -1;
-}
-
bool is_device_secure() {
int ret = -1;
char value[PROP_VALUE_MAX];
@@ -790,7 +801,7 @@
FsManagerAvbUniquePtr avb_handle(nullptr);
if (!fstab) {
- return -1;
+ return FS_MGR_MNTALL_FAIL;
}
for (i = 0; i < fstab->num_entries; i++) {
@@ -817,9 +828,7 @@
}
/* Translate LABEL= file system labels into block devices */
- if (!strcmp(fstab->recs[i].fs_type, "ext2") ||
- !strcmp(fstab->recs[i].fs_type, "ext3") ||
- !strcmp(fstab->recs[i].fs_type, "ext4")) {
+ if (is_extfs(fstab->recs[i].fs_type)) {
int tret = translate_ext_labels(&fstab->recs[i]);
if (tret < 0) {
LERROR << "Could not translate label to block device";
@@ -827,8 +836,10 @@
}
}
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
- wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
+ !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
+ LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all";
+ continue;
}
if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
@@ -836,10 +847,11 @@
avb_handle = FsManagerAvbHandle::Open(*fstab);
if (!avb_handle) {
LERROR << "Failed to open FsManagerAvbHandle";
- return -1;
+ return FS_MGR_MNTALL_FAIL;
}
}
- if (!avb_handle->SetUpAvb(&fstab->recs[i], true /* wait_for_verity_dev */)) {
+ if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
+ SetUpAvbHashtreeResult::kFail) {
LERROR << "Failed to set up AVB on partition: "
<< fstab->recs[i].mount_point << ", skipping!";
/* Skips mounting the device. */
@@ -847,7 +859,9 @@
}
} else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
- if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
+ if (__android_log_is_debuggable() &&
+ (rc == FS_MGR_SETUP_VERITY_DISABLED ||
+ rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
LINFO << "Verity disabled";
} else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
LERROR << "Could not set up verified partition, skipping!";
@@ -970,7 +984,7 @@
}
if (error_count) {
- return -1;
+ return FS_MGR_MNTALL_FAIL;
} else {
return encryptable;
}
@@ -1003,14 +1017,13 @@
char *tmp_mount_point)
{
int i = 0;
- int ret = FS_MGR_DOMNT_FAILED;
int mount_errors = 0;
int first_mount_errno = 0;
- char *m;
+ char* mount_point;
FsManagerAvbUniquePtr avb_handle(nullptr);
if (!fstab) {
- return ret;
+ return FS_MGR_DOMNT_FAILED;
}
for (i = 0; i < fstab->num_entries; i++) {
@@ -1025,36 +1038,27 @@
!strcmp(fstab->recs[i].fs_type, "mtd")) {
LERROR << "Cannot mount filesystem of type "
<< fstab->recs[i].fs_type << " on " << n_blk_device;
- goto out;
+ return FS_MGR_DOMNT_FAILED;
}
/* First check the filesystem if requested */
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
- wait_for_file(n_blk_device, WAIT_TIMEOUT);
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
+ LERROR << "Skipping mounting '" << n_blk_device << "'";
+ continue;
}
- int fs_stat = 0;
- int force_check = do_quota_with_shutdown_check(n_blk_device, fstab->recs[i].fs_type,
- &fstab->recs[i], &fs_stat);
-
- if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
- check_fs(n_blk_device, fstab->recs[i].fs_type,
- fstab->recs[i].mount_point, &fs_stat);
- }
-
- if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
- do_reserved_size(n_blk_device, fstab->recs[i].fs_type, &fstab->recs[i], &fs_stat);
- }
+ int fs_stat = prepare_fs_for_mount(n_blk_device, &fstab->recs[i]);
if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
if (!avb_handle) {
avb_handle = FsManagerAvbHandle::Open(*fstab);
if (!avb_handle) {
LERROR << "Failed to open FsManagerAvbHandle";
- return -1;
+ return FS_MGR_DOMNT_FAILED;
}
}
- if (!avb_handle->SetUpAvb(&fstab->recs[i], true /* wait_for_verity_dev */)) {
+ if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
+ SetUpAvbHashtreeResult::kFail) {
LERROR << "Failed to set up AVB on partition: "
<< fstab->recs[i].mount_point << ", skipping!";
/* Skips mounting the device. */
@@ -1062,7 +1066,9 @@
}
} else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
- if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
+ if (__android_log_is_debuggable() &&
+ (rc == FS_MGR_SETUP_VERITY_DISABLED ||
+ rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
LINFO << "Verity disabled";
} else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
LERROR << "Could not set up verified partition, skipping!";
@@ -1072,16 +1078,15 @@
/* Now mount it where requested */
if (tmp_mount_point) {
- m = tmp_mount_point;
+ mount_point = tmp_mount_point;
} else {
- m = fstab->recs[i].mount_point;
+ mount_point = fstab->recs[i].mount_point;
}
int retry_count = 2;
while (retry_count-- > 0) {
- if (!__mount(n_blk_device, m, &fstab->recs[i])) {
- ret = 0;
+ if (!__mount(n_blk_device, mount_point, &fstab->recs[i])) {
fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
- goto out;
+ return FS_MGR_DOMNT_SUCCESS;
} else {
if (retry_count <= 0) break; // run check_fs only once
if (!first_mount_errno) first_mount_errno = errno;
@@ -1093,22 +1098,16 @@
}
log_fs_stat(fstab->recs[i].blk_device, fs_stat);
}
+
+ // Reach here means the mount attempt fails.
if (mount_errors) {
- PERROR << "Cannot mount filesystem on " << n_blk_device
- << " at " << m;
- if (first_mount_errno == EBUSY) {
- ret = FS_MGR_DOMNT_BUSY;
- } else {
- ret = FS_MGR_DOMNT_FAILED;
- }
+ PERROR << "Cannot mount filesystem on " << n_blk_device << " at " << mount_point;
+ if (first_mount_errno == EBUSY) return FS_MGR_DOMNT_BUSY;
} else {
/* We didn't find a match, say so and return an error */
- LERROR << "Cannot find mount point " << fstab->recs[i].mount_point
- << " in fstab";
+ LERROR << "Cannot find mount point " << n_name << " in fstab";
}
-
-out:
- return ret;
+ return FS_MGR_DOMNT_FAILED;
}
/*
@@ -1203,12 +1202,15 @@
ret = -1;
continue;
}
- fprintf(zram_fp, "%d\n", fstab->recs[i].zram_size);
+ fprintf(zram_fp, "%u\n", fstab->recs[i].zram_size);
fclose(zram_fp);
}
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
- wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
+ !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
+ LERROR << "Skipping mkswap for '" << fstab->recs[i].blk_device << "'";
+ ret = -1;
+ continue;
}
/* Initialize the swap area */
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 36883e6..7824cfa 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -16,15 +16,14 @@
#include "fs_mgr_avb.h"
-#include <errno.h>
#include <fcntl.h>
-#include <inttypes.h>
#include <libgen.h>
-#include <stdio.h>
#include <string.h>
-#include <sys/stat.h>
+#include <sys/ioctl.h>
#include <sys/types.h>
-#include <unistd.h>
+
+#include <sstream>
+#include <string>
#include <vector>
#include <android-base/file.h>
@@ -33,11 +32,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <cutils/properties.h>
#include <libavb/libavb.h>
-#include <openssl/sha.h>
-#include <sys/ioctl.h>
-#include <utils/Compat.h>
#include "fs_mgr.h"
#include "fs_mgr_priv.h"
@@ -45,48 +40,6 @@
#include "fs_mgr_priv_dm_ioctl.h"
#include "fs_mgr_priv_sha.h"
-/* The format of dm-verity construction parameters:
- * <version> <dev> <hash_dev> <data_block_size> <hash_block_size>
- * <num_data_blocks> <hash_start_block> <algorithm> <digest> <salt>
- */
-#define VERITY_TABLE_FORMAT \
- "%u %s %s %u %u " \
- "%" PRIu64 " %" PRIu64 " %s %s %s "
-
-#define VERITY_TABLE_PARAMS(hashtree_desc, blk_device, digest, salt) \
- hashtree_desc.dm_verity_version, blk_device, blk_device, hashtree_desc.data_block_size, \
- hashtree_desc.hash_block_size, \
- hashtree_desc.image_size / hashtree_desc.data_block_size, /* num_data_blocks. */ \
- hashtree_desc.tree_offset / hashtree_desc.hash_block_size, /* hash_start_block. */ \
- (char*)hashtree_desc.hash_algorithm, digest, salt
-
-#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
-#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
-
-/* The default format of dm-verity optional parameters:
- * <#opt_params> ignore_zero_blocks restart_on_corruption
- */
-#define VERITY_TABLE_OPT_DEFAULT_FORMAT "2 %s %s"
-#define VERITY_TABLE_OPT_DEFAULT_PARAMS VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
-
-/* The FEC (forward error correction) format of dm-verity optional parameters:
- * <#opt_params> use_fec_from_device <fec_dev>
- * fec_roots <num> fec_blocks <num> fec_start <offset>
- * ignore_zero_blocks restart_on_corruption
- */
-#define VERITY_TABLE_OPT_FEC_FORMAT \
- "10 use_fec_from_device %s fec_roots %u fec_blocks %" PRIu64 " fec_start %" PRIu64 " %s %s"
-
-/* Note that fec_blocks is the size that FEC covers, *not* the
- * size of the FEC data. Since we use FEC for everything up until
- * the FEC data, it's the same as the offset (fec_start).
- */
-#define VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device) \
- blk_device, hashtree_desc.fec_num_roots, \
- hashtree_desc.fec_offset / hashtree_desc.data_block_size, /* fec_blocks */ \
- hashtree_desc.fec_offset / hashtree_desc.data_block_size, /* fec_start */ \
- VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
-
static inline bool nibble_value(const char& c, uint8_t* value) {
FS_MGR_CHECK(value != nullptr);
@@ -161,7 +114,6 @@
// Reads the following values from kernel cmdline and provides the
// VerifyVbmetaImages() to verify AvbSlotVerifyData.
-// - androidboot.vbmeta.device_state
// - androidboot.vbmeta.hash_alg
// - androidboot.vbmeta.size
// - androidboot.vbmeta.digest
@@ -170,7 +122,6 @@
// The factory method to return a unique_ptr<FsManagerAvbVerifier>
static std::unique_ptr<FsManagerAvbVerifier> Create();
bool VerifyVbmetaImages(const AvbSlotVerifyData& verify_data);
- bool IsDeviceUnlocked() { return is_device_unlocked_; }
protected:
FsManagerAvbVerifier() = default;
@@ -185,13 +136,12 @@
HashAlgorithm hash_alg_;
uint8_t digest_[SHA512_DIGEST_LENGTH];
size_t vbmeta_size_;
- bool is_device_unlocked_;
};
std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
std::string cmdline;
if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
- LERROR << "Failed to read /proc/cmdline";
+ PERROR << "Failed to read /proc/cmdline";
return nullptr;
}
@@ -208,9 +158,7 @@
const std::string& key = pieces[0];
const std::string& value = pieces[1];
- if (key == "androidboot.vbmeta.device_state") {
- avb_verifier->is_device_unlocked_ = (value == "unlocked");
- } else if (key == "androidboot.vbmeta.hash_alg") {
+ if (key == "androidboot.vbmeta.hash_alg") {
hash_alg = value;
} else if (key == "androidboot.vbmeta.size") {
if (!android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
@@ -280,10 +228,81 @@
return true;
}
-static bool hashtree_load_verity_table(struct dm_ioctl* io, const std::string& dm_device_name,
- int fd, const std::string& blk_device,
- const AvbHashtreeDescriptor& hashtree_desc,
- const std::string& salt, const std::string& root_digest) {
+// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
+// See the following link for more details:
+// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
+static std::string construct_verity_table(const AvbHashtreeDescriptor& hashtree_desc,
+ const std::string& salt, const std::string& root_digest,
+ const std::string& blk_device) {
+ // Loads androidboot.veritymode from kernel cmdline.
+ std::string verity_mode;
+ if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+ verity_mode = "enforcing"; // Defaults to enforcing when it's absent.
+ }
+
+ // Converts veritymode to the format used in kernel.
+ std::string dm_verity_mode;
+ if (verity_mode == "enforcing") {
+ dm_verity_mode = "restart_on_corruption";
+ } else if (verity_mode == "logging") {
+ dm_verity_mode = "ignore_corruption";
+ } else if (verity_mode != "eio") { // Default dm_verity_mode is eio.
+ LERROR << "Unknown androidboot.veritymode: " << verity_mode;
+ return "";
+ }
+
+ // dm-verity construction parameters:
+ // <version> <dev> <hash_dev>
+ // <data_block_size> <hash_block_size>
+ // <num_data_blocks> <hash_start_block>
+ // <algorithm> <digest> <salt>
+ // [<#opt_params> <opt_params>]
+ std::ostringstream verity_table;
+ verity_table << hashtree_desc.dm_verity_version << " " << blk_device << " " << blk_device << " "
+ << hashtree_desc.data_block_size << " " << hashtree_desc.hash_block_size << " "
+ << hashtree_desc.image_size / hashtree_desc.data_block_size << " "
+ << hashtree_desc.tree_offset / hashtree_desc.hash_block_size << " "
+ << hashtree_desc.hash_algorithm << " " << root_digest << " " << salt;
+
+ // Continued from the above optional parameters:
+ // [<#opt_params> <opt_params>]
+ int optional_argc = 0;
+ std::ostringstream optional_args;
+
+ // dm-verity optional parameters for FEC (forward error correction):
+ // use_fec_from_device <fec_dev>
+ // fec_roots <num>
+ // fec_blocks <num>
+ // fec_start <offset>
+ if (hashtree_desc.fec_size > 0) {
+ // Note that fec_blocks is the size that FEC covers, *NOT* the
+ // size of the FEC data. Since we use FEC for everything up until
+ // the FEC data, it's the same as the offset (fec_start).
+ optional_argc += 8;
+ // clang-format off
+ optional_args << "use_fec_from_device " << blk_device
+ << " fec_roots " << hashtree_desc.fec_num_roots
+ << " fec_blocks " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
+ << " fec_start " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
+ << " ";
+ // clang-format on
+ }
+
+ if (!dm_verity_mode.empty()) {
+ optional_argc += 1;
+ optional_args << dm_verity_mode << " ";
+ }
+
+ // Always use ignore_zero_blocks.
+ optional_argc += 1;
+ optional_args << "ignore_zero_blocks";
+
+ verity_table << " " << optional_argc << " " << optional_args.str();
+ return verity_table.str();
+}
+
+static bool load_verity_table(struct dm_ioctl* io, const std::string& dm_device_name, int fd,
+ uint64_t image_size, const std::string& verity_table) {
fs_mgr_verity_ioctl_init(io, dm_device_name, DM_STATUS_TABLE_FLAG);
// The buffer consists of [dm_ioctl][dm_target_spec][verity_params].
@@ -294,35 +313,25 @@
io->target_count = 1;
dm_target->status = 0;
dm_target->sector_start = 0;
- dm_target->length = hashtree_desc.image_size / 512;
+ dm_target->length = image_size / 512;
strcpy(dm_target->target_type, "verity");
// Builds the verity params.
char* verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
size_t bufsize = DM_BUF_SIZE - (verity_params - buffer);
- int res = 0;
- if (hashtree_desc.fec_size > 0) {
- res = snprintf(verity_params, bufsize, VERITY_TABLE_FORMAT VERITY_TABLE_OPT_FEC_FORMAT,
- VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(), root_digest.c_str(),
- salt.c_str()),
- VERITY_TABLE_OPT_FEC_PARAMS(hashtree_desc, blk_device.c_str()));
- } else {
- res = snprintf(verity_params, bufsize, VERITY_TABLE_FORMAT VERITY_TABLE_OPT_DEFAULT_FORMAT,
- VERITY_TABLE_PARAMS(hashtree_desc, blk_device.c_str(), root_digest.c_str(),
- salt.c_str()),
- VERITY_TABLE_OPT_DEFAULT_PARAMS);
- }
+ LINFO << "Loading verity table: '" << verity_table << "'";
- if (res < 0 || (size_t)res >= bufsize) {
- LERROR << "Error building verity table; insufficient buffer size?";
+ // Copies verity_table to verity_params (including the terminating null byte).
+ if (verity_table.size() > bufsize - 1) {
+ LERROR << "Verity table size too large: " << verity_table.size()
+ << " (max allowable size: " << bufsize - 1 << ")";
return false;
}
-
- LINFO << "Loading verity table: '" << verity_params << "'";
+ memcpy(verity_params, verity_table.c_str(), verity_table.size() + 1);
// Sets ext target boundary.
- verity_params += strlen(verity_params) + 1;
+ verity_params += verity_table.size() + 1;
verity_params = (char*)(((unsigned long)verity_params + 7) & ~7);
dm_target->next = verity_params - buffer;
@@ -362,9 +371,15 @@
return false;
}
+ std::string verity_table =
+ construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device);
+ if (verity_table.empty()) {
+ LERROR << "Failed to construct verity table.";
+ return false;
+ }
+
// Loads the verity mapping table.
- if (!hashtree_load_verity_table(io, mount_point, fd, std::string(fstab_entry->blk_device),
- hashtree_desc, salt, root_digest)) {
+ if (!load_verity_table(io, mount_point, fd, hashtree_desc.image_size, verity_table)) {
LERROR << "Couldn't load verity table!";
return false;
}
@@ -382,7 +397,7 @@
fstab_entry->blk_device = strdup(verity_blk_name.c_str());
// Makes sure we've set everything up properly.
- if (wait_for_verity_dev && fs_mgr_test_access(verity_blk_name.c_str()) < 0) {
+ if (wait_for_verity_dev && !fs_mgr_wait_for_file(verity_blk_name, 1s)) {
return false;
}
@@ -473,12 +488,7 @@
}
FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
- // Gets the expected hash value of vbmeta images from kernel cmdline.
- std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
- if (!avb_verifier) {
- LERROR << "Failed to create FsManagerAvbVerifier";
- return nullptr;
- }
+ bool is_device_unlocked = fs_mgr_is_device_unlocked();
FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle());
if (!avb_handle) {
@@ -486,8 +496,10 @@
return nullptr;
}
- AvbSlotVerifyResult verify_result = avb_ops->AvbSlotVerify(
- fs_mgr_get_slot_suffix(), avb_verifier->IsDeviceUnlocked(), &avb_handle->avb_slot_data_);
+ AvbSlotVerifyFlags flags = is_device_unlocked ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
+ : AVB_SLOT_VERIFY_FLAGS_NONE;
+ AvbSlotVerifyResult verify_result =
+ avb_ops->AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->avb_slot_data_);
// Only allow two verify results:
// - AVB_SLOT_VERIFY_RESULT_OK.
@@ -503,62 +515,81 @@
// for more details.
switch (verify_result) {
case AVB_SLOT_VERIFY_RESULT_OK:
- avb_handle->status_ = kFsManagerAvbHandleSuccess;
+ avb_handle->status_ = kAvbHandleSuccess;
break;
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
- if (!avb_verifier->IsDeviceUnlocked()) {
+ if (!is_device_unlocked) {
LERROR << "ERROR_VERIFICATION isn't allowed when the device is LOCKED";
return nullptr;
}
- avb_handle->status_ = kFsManagerAvbHandleErrorVerification;
+ avb_handle->status_ = kAvbHandleVerificationError;
break;
default:
LERROR << "avb_slot_verify failed, result: " << verify_result;
return nullptr;
}
- // Verifies vbmeta images against the digest passed from bootloader.
- if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
- LERROR << "VerifyVbmetaImages failed";
- return nullptr;
- }
-
// Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
avb_handle->avb_version_ =
android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
- // Checks whether FLAGS_HASHTREE_DISABLED is set.
+ // Checks whether FLAGS_VERIFICATION_DISABLED is set:
+ // - Only the top-level vbmeta struct is read.
+ // - vbmeta struct in other partitions are NOT processed, including AVB HASH descriptor(s)
+ // and AVB HASHTREE descriptor(s).
AvbVBMetaImageHeader vbmeta_header;
avb_vbmeta_image_header_to_host_byte_order(
(AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
&vbmeta_header);
+ bool verification_disabled =
+ ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
- bool hashtree_disabled =
- ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
- if (hashtree_disabled) {
- avb_handle->status_ = kFsManagerAvbHandleHashtreeDisabled;
+ if (verification_disabled) {
+ avb_handle->status_ = kAvbHandleVerificationDisabled;
+ } else {
+ // Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
+ std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
+ if (!avb_verifier) {
+ LERROR << "Failed to create FsManagerAvbVerifier";
+ return nullptr;
+ }
+ if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
+ LERROR << "VerifyVbmetaImages failed";
+ return nullptr;
+ }
+
+ // Checks whether FLAGS_HASHTREE_DISABLED is set.
+ bool hashtree_disabled =
+ ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+ if (hashtree_disabled) {
+ avb_handle->status_ = kAvbHandleHashtreeDisabled;
+ }
}
LINFO << "Returning avb_handle with status: " << avb_handle->status_;
return avb_handle;
}
-bool FsManagerAvbHandle::SetUpAvb(struct fstab_rec* fstab_entry, bool wait_for_verity_dev) {
- if (!fstab_entry) return false;
- if (!avb_slot_data_ || avb_slot_data_->num_vbmeta_images < 1) {
- return false;
+SetUpAvbHashtreeResult FsManagerAvbHandle::SetUpAvbHashtree(struct fstab_rec* fstab_entry,
+ bool wait_for_verity_dev) {
+ if (!fstab_entry || status_ == kAvbHandleUninitialized || !avb_slot_data_ ||
+ avb_slot_data_->num_vbmeta_images < 1) {
+ return SetUpAvbHashtreeResult::kFail;
}
- if (status_ == kFsManagerAvbHandleUninitialized) return false;
- if (status_ == kFsManagerAvbHandleHashtreeDisabled) {
- LINFO << "AVB HASHTREE disabled on:" << fstab_entry->mount_point;
- return true;
+ if (status_ == kAvbHandleHashtreeDisabled || status_ == kAvbHandleVerificationDisabled) {
+ LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point;
+ return SetUpAvbHashtreeResult::kDisabled;
}
- std::string partition_name(basename(fstab_entry->mount_point));
- if (!avb_validate_utf8((const uint8_t*)partition_name.c_str(), partition_name.length())) {
- LERROR << "Partition name: " << partition_name.c_str() << " is not valid UTF-8.";
- return false;
+ // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
+ // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
+ std::string partition_name(basename(fstab_entry->blk_device));
+ if (fstab_entry->fs_mgr_flags & MF_SLOTSELECT) {
+ auto ab_suffix = partition_name.rfind(fs_mgr_get_slot_suffix());
+ if (ab_suffix != std::string::npos) {
+ partition_name.erase(ab_suffix);
+ }
}
AvbHashtreeDescriptor hashtree_descriptor;
@@ -566,13 +597,14 @@
std::string root_digest;
if (!get_hashtree_descriptor(partition_name, *avb_slot_data_, &hashtree_descriptor, &salt,
&root_digest)) {
- return false;
+ return SetUpAvbHashtreeResult::kFail;
}
// Converts HASHTREE descriptor to verity_table_params.
if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest,
wait_for_verity_dev)) {
- return false;
+ return SetUpAvbHashtreeResult::kFail;
}
- return true;
+
+ return SetUpAvbHashtreeResult::kSuccess;
}
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index 47df861..43879fe 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -89,16 +89,28 @@
return AVB_IO_RESULT_OK;
}
+static AvbIOResult dummy_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+ const char* partition ATTRIBUTE_UNUSED,
+ uint64_t* out_size_num_byte) {
+ // The function is for bootloader to load entire content of AVB HASH partitions.
+ // In user-space, returns 0 as we only need to set up AVB HASHTHREE partitions.
+ *out_size_num_byte = 0;
+ return AVB_IO_RESULT_OK;
+}
+
void FsManagerAvbOps::InitializeAvbOps() {
// We only need to provide the implementation of read_from_partition()
// operation since that's all what is being used by the avb_slot_verify().
// Other I/O operations are only required in bootloader but not in
- // user-space so we set them as dummy operations.
+ // user-space so we set them as dummy operations. Also zero the entire
+ // struct so operations added in the future will be set to NULL.
+ memset(&avb_ops_, 0, sizeof(AvbOps));
avb_ops_.read_from_partition = read_from_partition;
avb_ops_.read_rollback_index = dummy_read_rollback_index;
avb_ops_.validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
avb_ops_.read_is_device_unlocked = dummy_read_is_device_unlocked;
avb_ops_.get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
+ avb_ops_.get_size_of_partition = dummy_get_size_of_partition;
// Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
avb_ops_.user_data = this;
@@ -130,10 +142,8 @@
}
std::string path = iter->second;
- // Ensures the device path (a symlink created by init) is ready to
- // access. fs_mgr_test_access() will test a few iterations if the
- // path doesn't exist yet.
- if (fs_mgr_test_access(path.c_str()) < 0) {
+ // Ensures the device path (a symlink created by init) is ready to access.
+ if (!fs_mgr_wait_for_file(path, 1s)) {
return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
}
@@ -148,13 +158,13 @@
if (offset < 0) {
off64_t total_size = lseek64(fd, 0, SEEK_END);
if (total_size == -1) {
- LERROR << "Failed to lseek64 to end of the partition";
+ PERROR << "Failed to lseek64 to end of the partition";
return AVB_IO_RESULT_ERROR_IO;
}
offset = total_size + offset;
// Repositions the offset to the beginning.
if (lseek64(fd, 0, SEEK_SET) == -1) {
- LERROR << "Failed to lseek64 to the beginning of the partition";
+ PERROR << "Failed to lseek64 to the beginning of the partition";
return AVB_IO_RESULT_ERROR_IO;
}
}
@@ -175,13 +185,15 @@
}
AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix,
- bool allow_verification_error,
+ AvbSlotVerifyFlags flags,
AvbSlotVerifyData** out_data) {
// Invokes avb_slot_verify() to load and verify all vbmeta images.
// Sets requested_partitions to nullptr as it's to copy the contents
// of HASH partitions into handle>avb_slot_data_, which is not required as
// fs_mgr only deals with HASHTREE partitions.
const char* requested_partitions[] = {nullptr};
- return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(),
- allow_verification_error, out_data);
+ // The |hashtree_error_mode| field doesn't matter as it only
+ // influences the generated kernel cmdline parameters.
+ return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,
+ AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, out_data);
}
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index cffa6ce..9c5d3f3 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <string>
+
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -21,19 +23,11 @@
#include "fs_mgr_priv.h"
-// Tries to get the boot config value in properties, kernel cmdline and
-// device tree (in that order). returns 'true' if successfully found, 'false'
-// otherwise
-bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
+// Tries to get the given boot config value from kernel cmdline.
+// Returns true if successfully found, false otherwise.
+bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
FS_MGR_CHECK(out_val != nullptr);
- // first check if we have "ro.boot" property already
- *out_val = android::base::GetProperty("ro.boot." + key, "");
- if (!out_val->empty()) {
- return true;
- }
-
- // fallback to kernel cmdline, properties may not be ready yet
std::string cmdline;
std::string cmdline_key("androidboot." + key);
if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
@@ -48,15 +42,35 @@
}
}
+ return false;
+}
+
+// Tries to get the boot config value in properties, kernel cmdline and
+// device tree (in that order). returns 'true' if successfully found, 'false'
+// otherwise
+bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
+ FS_MGR_CHECK(out_val != nullptr);
+
+ // first check if we have "ro.boot" property already
+ *out_val = android::base::GetProperty("ro.boot." + key, "");
+ if (!out_val->empty()) {
+ return true;
+ }
+
+ // fallback to kernel cmdline, properties may not be ready yet
+ if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
+ return true;
+ }
+
// lastly, check the device tree
if (is_dt_compatible()) {
- std::string file_name = kAndroidDtDir + "/" + key;
- // DT entries terminate with '\0' but so do the properties
+ std::string file_name = get_android_dt_dir() + "/" + key;
if (android::base::ReadFileToString(file_name, out_val)) {
- return true;
+ if (!out_val->empty()) {
+ out_val->pop_back(); // Trims the trailing '\0' out.
+ return true;
+ }
}
-
- LINFO << "Error finding '" << key << "' in device tree";
}
return false;
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 5705f93..75feee7 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -24,25 +24,21 @@
#include <cutils/partition_utils.h>
#include <sys/mount.h>
-#include <ext4_utils/ext4_utils.h>
#include <ext4_utils/ext4.h>
-#include <ext4_utils/make_ext4fs.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
+#include <ext4_utils/ext4_utils.h>
+#include <logwrap/logwrap.h>
#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
#include "fs_mgr_priv.h"
#include "cryptfs.h"
-extern "C" {
-extern struct fs_info info; /* magic global from ext4_utils */
-extern void reset_ext4fs_info();
-}
-
static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
{
uint64_t dev_sz;
int fd, rc = 0;
+ int status;
if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
PERROR << "Cannot open block device";
@@ -55,30 +51,36 @@
return -1;
}
- struct selabel_handle *sehandle = selinux_android_file_context_handle();
- if (!sehandle) {
- /* libselinux logs specific error */
- LERROR << "Cannot initialize android file_contexts";
- close(fd);
- return -1;
- }
-
- /* Format the partition using the calculated length */
- reset_ext4fs_info();
- info.len = (off64_t)dev_sz;
- if (crypt_footer) {
- info.len -= CRYPT_FOOTER_OFFSET;
- }
-
- /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
- rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, sehandle, 0, 0, NULL, NULL, NULL);
- if (rc) {
- LERROR << "make_ext4fs returned " << rc;
- }
close(fd);
- if (sehandle) {
- selabel_close(sehandle);
+ /* Format the partition using the calculated length */
+ if (crypt_footer) {
+ dev_sz -= CRYPT_FOOTER_OFFSET;
+ }
+
+ std::string size_str = std::to_string(dev_sz / 4096);
+ const char* const mke2fs_args[] = {
+ "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", fs_blkdev, size_str.c_str(), nullptr};
+
+ rc = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), &status,
+ true, LOG_KLOG, true, nullptr, nullptr, 0);
+ if (rc) {
+ LERROR << "mke2fs returned " << rc;
+ return rc;
+ }
+
+ const char* const e2fsdroid_args[] = {
+ "/system/bin/e2fsdroid",
+ "-e",
+ "-a",
+ fs_mnt_point,
+ fs_blkdev,
+ nullptr};
+
+ rc = android_fork_execvp_ext(arraysize(e2fsdroid_args), const_cast<char**>(e2fsdroid_args),
+ &status, true, LOG_KLOG, true, nullptr, nullptr, 0);
+ if (rc) {
+ LERROR << "e2fsdroid returned " << rc;
}
return rc;
@@ -86,42 +88,11 @@
static int format_f2fs(char *fs_blkdev)
{
- char * args[3];
- int pid;
- int rc = 0;
+ int status;
+ const char* const args[] = {"/system/bin/make_f2fs", "-f", "-O encrypt", fs_blkdev, nullptr};
- args[0] = (char *)"/sbin/mkfs.f2fs";
- args[1] = fs_blkdev;
- args[2] = (char *)0;
-
- pid = fork();
- if (pid < 0) {
- return pid;
- }
- if (!pid) {
- /* This doesn't return */
- execv("/sbin/mkfs.f2fs", args);
- exit(1);
- }
- for(;;) {
- pid_t p = waitpid(pid, &rc, 0);
- if (p != pid) {
- LERROR << "Error waiting for child process - " << p;
- rc = -1;
- break;
- }
- if (WIFEXITED(rc)) {
- rc = WEXITSTATUS(rc);
- LINFO << args[0] << " done, status " << rc;
- if (rc) {
- rc = -1;
- }
- break;
- }
- LERROR << "Still waiting for " << args[0] << "...";
- }
-
- return rc;
+ return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), &status, true,
+ LOG_KLOG, true, nullptr, nullptr, 0);
}
int fs_mgr_do_format(struct fstab_rec *fstab, bool crypt_footer)
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 7d09d01..41a5868 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -29,6 +29,8 @@
#include "fs_mgr_priv.h"
+const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android");
+
struct fs_mgr_flag_values {
char *key_loc;
char *verity_loc;
@@ -358,9 +360,26 @@
return f;
}
+static std::string init_android_dt_dir() {
+ std::string android_dt_dir;
+ // The platform may specify a custom Android DT path in kernel cmdline
+ if (!fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
+ // Fall back to the standard procfs-based path
+ android_dt_dir = kDefaultAndroidDtDir;
+ }
+ return android_dt_dir;
+}
+
+// FIXME: The same logic is duplicated in system/core/init/
+const std::string& get_android_dt_dir() {
+ // Set once and saves time for subsequent calls to this function
+ static const std::string kAndroidDtDir = init_android_dt_dir();
+ return kAndroidDtDir;
+}
+
static bool is_dt_fstab_compatible() {
std::string dt_value;
- std::string file_name = kAndroidDtDir + "/fstab/compatible";
+ std::string file_name = get_android_dt_dir() + "/fstab/compatible";
if (read_dt_file(file_name, &dt_value)) {
if (dt_value == "android,fstab") {
return true;
@@ -376,22 +395,14 @@
return fstab;
}
- std::string fstabdir_name = kAndroidDtDir + "/fstab";
+ 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;
dirent* dp;
while ((dp = readdir(fstabdir.get())) != NULL) {
- // skip over name and compatible
- if (dp->d_type != DT_DIR) {
- continue;
- }
-
- // skip if its not 'vendor', 'odm' or 'system'
- if (strcmp(dp->d_name, "odm") && strcmp(dp->d_name, "system") &&
- strcmp(dp->d_name, "vendor")) {
- continue;
- }
+ // skip over name, compatible and .
+ if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
// create <dev> <mnt_point> <type> <mnt_flags> <fsmgr_flags>\n
std::vector<std::string> fstab_entry;
@@ -447,7 +458,7 @@
}
bool is_dt_compatible() {
- std::string file_name = kAndroidDtDir + "/compatible";
+ std::string file_name = get_android_dt_dir() + "/compatible";
std::string dt_value;
if (read_dt_file(file_name, &dt_value)) {
if (dt_value == "android,firmware") {
@@ -863,32 +874,26 @@
return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
}
-int fs_mgr_is_notrim(struct fstab_rec *fstab)
-{
+int fs_mgr_is_notrim(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & MF_NOTRIM;
}
-int fs_mgr_is_formattable(struct fstab_rec *fstab)
-{
+int fs_mgr_is_formattable(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & (MF_FORMATTABLE);
}
-int fs_mgr_is_slotselect(struct fstab_rec *fstab)
-{
+int fs_mgr_is_slotselect(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & MF_SLOTSELECT;
}
-int fs_mgr_is_nofail(struct fstab_rec *fstab)
-{
+int fs_mgr_is_nofail(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & MF_NOFAIL;
}
-int fs_mgr_is_latemount(struct fstab_rec *fstab)
-{
+int fs_mgr_is_latemount(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & MF_LATEMOUNT;
}
-int fs_mgr_is_quota(struct fstab_rec *fstab)
-{
+int fs_mgr_is_quota(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & MF_QUOTA;
}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index c985462..7423c1f 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -17,8 +17,12 @@
#ifndef __CORE_FS_MGR_PRIV_H
#define __CORE_FS_MGR_PRIV_H
+#include <chrono>
+#include <string>
+
#include <android-base/logging.h>
-#include <fs_mgr.h>
+
+#include "fs_mgr.h"
#include "fs_mgr_priv_boot_config.h"
/* The CHECK() in logging.h will use program invocation name as the tag.
@@ -43,8 +47,6 @@
#define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000"
-#define WAIT_TIMEOUT 20
-
/* fstab has the following format:
*
* Any line starting with a # is a comment and ignored
@@ -110,9 +112,14 @@
#define DM_BUF_SIZE 4096
+using namespace std::chrono_literals;
+
int fs_mgr_set_blk_ro(const char *blockdev);
-int fs_mgr_test_access(const char *device);
+bool fs_mgr_wait_for_file(const std::string& filename,
+ const std::chrono::milliseconds relative_timeout);
bool fs_mgr_update_for_slotselect(struct fstab *fstab);
+bool fs_mgr_is_device_unlocked();
+const std::string& get_android_dt_dir();
bool is_dt_compatible();
bool is_device_secure();
int load_verity_state(struct fstab_rec* fstab, int* mode);
diff --git a/fs_mgr/fs_mgr_priv_avb_ops.h b/fs_mgr/fs_mgr_priv_avb_ops.h
index a6b52e4..d1ef2e9 100644
--- a/fs_mgr/fs_mgr_priv_avb_ops.h
+++ b/fs_mgr/fs_mgr_priv_avb_ops.h
@@ -56,7 +56,7 @@
AvbIOResult ReadFromPartition(const char* partition, int64_t offset, size_t num_bytes,
void* buffer, size_t* out_num_read);
- AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, bool allow_verification_error,
+ AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, AvbSlotVerifyFlags flags,
AvbSlotVerifyData** out_data);
private:
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
index 8773d33..d98dc02 100644
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -20,8 +20,7 @@
#include <sys/cdefs.h>
#include <string>
-const std::string kAndroidDtDir("/proc/device-tree/firmware/android");
-
+bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
#endif /* __CORE_FS_MGR_PRIV_BOOTCONFIG_H */
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5fa10bc..7f8e1e2 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -348,10 +348,13 @@
static int was_verity_restart()
{
- static const char *files[] = {
+ static const char* files[] = {
+ // clang-format off
+ "/sys/fs/pstore/console-ramoops-0",
"/sys/fs/pstore/console-ramoops",
"/proc/last_kmsg",
NULL
+ // clang-format on
};
int i;
@@ -689,27 +692,55 @@
return read_verity_state(fstab->verity_loc, offset, mode);
}
-static void update_verity_table_blk_device(char *blk_device, char **table)
-{
- std::string result, word;
+// Update the verity table using the actual block device path.
+// Two cases:
+// Case-1: verity table is shared for devices with different by-name prefix.
+// Example:
+// verity table token: /dev/block/bootdevice/by-name/vendor
+// blk_device-1 (non-A/B): /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+// blk_device-2 (A/B): /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor_a
+//
+// Case-2: append A/B suffix in the verity table.
+// Example:
+// verity table token: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+// blk_device: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor_a
+static void update_verity_table_blk_device(const std::string& blk_device, char** table,
+ bool slot_select) {
+ bool updated = false;
+ std::string result, ab_suffix;
auto tokens = android::base::Split(*table, " ");
+ // If slot_select is set, it means blk_device is already updated with ab_suffix.
+ if (slot_select) ab_suffix = fs_mgr_get_slot_suffix();
+
for (const auto& token : tokens) {
- if (android::base::StartsWith(token, "/dev/block/") &&
- android::base::StartsWith(blk_device, token.c_str())) {
- word = blk_device;
+ std::string new_token;
+ if (android::base::StartsWith(token, "/dev/block/")) {
+ if (token == blk_device) return; // no need to update if they're already the same.
+ std::size_t found1 = blk_device.find("by-name");
+ std::size_t found2 = token.find("by-name");
+ if (found1 != std::string::npos && found2 != std::string::npos &&
+ blk_device.substr(found1) == token.substr(found2) + ab_suffix) {
+ new_token = blk_device;
+ }
+ }
+
+ if (!new_token.empty()) {
+ updated = true;
+ LINFO << "Verity table: updated block device from '" << token << "' to '" << new_token
+ << "'";
} else {
- word = token;
+ new_token = token;
}
if (result.empty()) {
- result = word;
+ result = new_token;
} else {
- result += " " + word;
+ result += " " + new_token;
}
}
- if (result.empty()) {
+ if (!updated) {
return;
}
@@ -738,7 +769,7 @@
// setup is needed at all.
if (!is_device_secure()) {
LINFO << "Verity setup skipped for " << mount_point;
- return FS_MGR_SETUP_VERITY_SUCCESS;
+ return FS_MGR_SETUP_VERITY_SKIPPED;
}
if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
@@ -751,8 +782,8 @@
if (fec_verity_get_metadata(f, &verity) < 0) {
PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
// Allow verity disabled when the device is unlocked without metadata
- if ("0" == android::base::GetProperty("ro.boot.flash.locked", "")) {
- retval = FS_MGR_SETUP_VERITY_DISABLED;
+ if (fs_mgr_is_device_unlocked()) {
+ retval = FS_MGR_SETUP_VERITY_SKIPPED;
LWARNING << "Allow invalid metadata when the device is unlocked";
}
goto out;
@@ -810,9 +841,15 @@
// verify the signature on the table
if (verify_verity_signature(verity) < 0) {
+ // Allow signature verification error when the device is unlocked
+ if (fs_mgr_is_device_unlocked()) {
+ retval = FS_MGR_SETUP_VERITY_SKIPPED;
+ LWARNING << "Allow signature verification error when the device is unlocked";
+ goto out;
+ }
if (params.mode == VERITY_MODE_LOGGING) {
// the user has been warned, allow mounting without dm-verity
- retval = FS_MGR_SETUP_VERITY_SUCCESS;
+ retval = FS_MGR_SETUP_VERITY_SKIPPED;
goto out;
}
@@ -825,10 +862,9 @@
LINFO << "Enabling dm-verity for " << mount_point.c_str()
<< " (mode " << params.mode << ")";
- if (fstab->fs_mgr_flags & MF_SLOTSELECT) {
- // Update the verity params using the actual block device path
- update_verity_table_blk_device(fstab->blk_device, ¶ms.table);
- }
+ // Update the verity params using the actual block device path
+ update_verity_table_blk_device(fstab->blk_device, ¶ms.table,
+ fstab->fs_mgr_flags & MF_SLOTSELECT);
// load the verity mapping table
if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
@@ -899,7 +935,7 @@
}
// make sure we've set everything up properly
- if (wait_for_verity_dev && fs_mgr_test_access(fstab->blk_device) < 0) {
+ if (wait_for_verity_dev && !fs_mgr_wait_for_file(fstab->blk_device, 1s)) {
goto out;
}
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index fd63dfd..3d3faf3 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -22,11 +22,7 @@
#include <stdbool.h>
#include <linux/dm-ioctl.h>
-// C++ only headers
-// TODO: move this into separate header files under include/fs_mgr/*.h
-#ifdef __cplusplus
-#include <string>
-#endif
+#include <fstab/fstab.h>
// Magic number at start of verity metadata
#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001
@@ -53,48 +49,10 @@
MOUNT_MODE_LATE = 2
};
-/*
- * The entries must be kept in the same order as they were seen in the fstab.
- * Unless explicitly requested, a lookup on mount point should always
- * return the 1st one.
- */
-struct fstab {
- int num_entries;
- struct fstab_rec *recs;
- char *fstab_filename;
-};
-
-struct fstab_rec {
- char *blk_device;
- char *mount_point;
- char *fs_type;
- unsigned long flags;
- char *fs_options;
- int fs_mgr_flags;
- char *key_loc;
- char *verity_loc;
- long long length;
- char *label;
- int partnum;
- int swap_prio;
- int max_comp_streams;
- unsigned int zram_size;
- uint64_t reserved_size;
- unsigned int file_contents_mode;
- unsigned int file_names_mode;
- unsigned int erase_blk_size;
- unsigned int logical_blk_size;
-};
-
// Callback function for verity status
typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab,
const char *mount_point, int mode, int status);
-struct fstab *fs_mgr_read_fstab_default();
-struct fstab *fs_mgr_read_fstab_dt();
-struct fstab *fs_mgr_read_fstab(const char *fstab_path);
-void fs_mgr_free_fstab(struct fstab *fstab);
-
#define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4
#define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 3
@@ -106,6 +64,7 @@
#define FS_MGR_DOMNT_FAILED (-1)
#define FS_MGR_DOMNT_BUSY (-2)
+#define FS_MGR_DOMNT_SUCCESS 0
int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
char *tmp_mount_point);
@@ -116,32 +75,11 @@
char *real_blk_device, int size);
bool fs_mgr_load_verity_state(int* mode);
bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
-int fs_mgr_add_entry(struct fstab *fstab,
- const char *mount_point, const char *fs_type,
- const char *blk_device);
-struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path);
-int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab);
-int fs_mgr_is_nonremovable(const struct fstab_rec *fstab);
-int fs_mgr_is_verified(const struct fstab_rec *fstab);
-int fs_mgr_is_verifyatboot(const struct fstab_rec *fstab);
-int fs_mgr_is_avb(const struct fstab_rec *fstab);
-int fs_mgr_is_encryptable(const struct fstab_rec *fstab);
-int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab);
-void fs_mgr_get_file_encryption_modes(const struct fstab_rec *fstab,
- const char **contents_mode_ret,
- const char **filenames_mode_ret);
-int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab);
-int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab);
-int fs_mgr_is_notrim(struct fstab_rec *fstab);
-int fs_mgr_is_formattable(struct fstab_rec *fstab);
-int fs_mgr_is_slotselect(struct fstab_rec *fstab);
-int fs_mgr_is_nofail(struct fstab_rec *fstab);
-int fs_mgr_is_latemount(struct fstab_rec *fstab);
-int fs_mgr_is_quota(struct fstab_rec *fstab);
int fs_mgr_swapon_all(struct fstab *fstab);
int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
+#define FS_MGR_SETUP_VERITY_SKIPPED (-3)
#define FS_MGR_SETUP_VERITY_DISABLED (-2)
#define FS_MGR_SETUP_VERITY_FAIL (-1)
#define FS_MGR_SETUP_VERITY_SUCCESS 0
@@ -149,10 +87,4 @@
__END_DECLS
-// C++ only functions
-// TODO: move this into separate header files under include/fs_mgr/*.h
-#ifdef __cplusplus
-std::string fs_mgr_get_slot_suffix();
-#endif
-
#endif /* __CORE_FS_MGR_H */
diff --git a/fs_mgr/include/fs_mgr_avb.h b/fs_mgr/include/fs_mgr_avb.h
index bbafe1a..73a22c8 100644
--- a/fs_mgr/include/fs_mgr_avb.h
+++ b/fs_mgr/include/fs_mgr_avb.h
@@ -25,11 +25,10 @@
#include "fs_mgr.h"
-enum FsManagerAvbHandleStatus {
- kFsManagerAvbHandleUninitialized = -1,
- kFsManagerAvbHandleSuccess = 0,
- kFsManagerAvbHandleHashtreeDisabled = 1,
- kFsManagerAvbHandleErrorVerification = 2,
+enum class SetUpAvbHashtreeResult {
+ kSuccess = 0,
+ kFail,
+ kDisabled,
};
class FsManagerAvbOps;
@@ -40,8 +39,8 @@
using ByNameSymlinkMap = std::map<std::string, std::string>;
// Provides a factory method to return a unique_ptr pointing to itself and the
-// SetUpAvb() function to extract dm-verity parameters from AVB metadata to
-// load verity table into kernel through ioctl.
+// SetUpAvbHashtree() function to extract dm-verity parameters from AVB HASHTREE
+// descriptors to load verity table into kernel through ioctl.
class FsManagerAvbHandle {
public:
// The factory method to return a FsManagerAvbUniquePtr that holds
@@ -65,12 +64,22 @@
// - nullptr: any error when reading and verifying the metadata,
// e.g., I/O error, digest value mismatch, size mismatch, etc.
//
- // - a valid unique_ptr with status kFsMgrAvbHandleHashtreeDisabled:
+ // - a valid unique_ptr with status kAvbHandleHashtreeDisabled:
// to support the existing 'adb disable-verity' feature in Android.
// It's very helpful for developers to make the filesystem writable to
// allow replacing binaries on the device.
//
- // - a valid unique_ptr with status kFsMgrAvbHandleSuccess: the metadata
+ // - a valid unique_ptr with status kAvbHandleVerificationDisabled:
+ // to support 'avbctl disable-verification': only the top-level
+ // vbmeta is read, vbmeta structs in other partitions are not processed.
+ // It's needed to bypass AVB when using the generic system.img to run
+ // VTS for project Treble.
+ //
+ // - a valid unique_ptr with status kAvbHandleVerificationError:
+ // there is verification error when libavb loads vbmeta from each
+ // partition. This is only allowed when the device is unlocked.
+ //
+ // - a valid unique_ptr with status kAvbHandleSuccess: the metadata
// is verified and can be trusted.
//
static FsManagerAvbUniquePtr Open(const fstab& fstab);
@@ -79,14 +88,15 @@
// Sets up dm-verity on the given fstab entry.
// The 'wait_for_verity_dev' parameter makes this function wait for the
// verity device to get created before return.
- // Returns true if the mount point is eligible to mount, it includes:
- // - status_ is kFsMgrAvbHandleHashtreeDisabled or
- // - status_ is kFsMgrAvbHandleSuccess and sending ioctl DM_TABLE_LOAD
- // to load verity table is success.
- // Otherwise, returns false.
- bool SetUpAvb(fstab_rec* fstab_entry, bool wait_for_verity_dev);
+ //
+ // Return value:
+ // - kSuccess: successfully loads dm-verity table into kernel.
+ // - kFailed: failed to setup dm-verity, e.g., vbmeta verification error,
+ // failed to get the HASHTREE descriptor, runtime error when set up
+ // device-mapper, etc.
+ // - kDisabled: hashtree is disabled.
+ SetUpAvbHashtreeResult SetUpAvbHashtree(fstab_rec* fstab_entry, bool wait_for_verity_dev);
- bool hashtree_disabled() const { return status_ == kFsManagerAvbHandleHashtreeDisabled; }
const std::string& avb_version() const { return avb_version_; }
FsManagerAvbHandle(const FsManagerAvbHandle&) = delete; // no copy
@@ -102,11 +112,19 @@
};
private:
- FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kFsManagerAvbHandleUninitialized) {}
+ enum AvbHandleStatus {
+ kAvbHandleSuccess = 0,
+ kAvbHandleUninitialized,
+ kAvbHandleHashtreeDisabled,
+ kAvbHandleVerificationDisabled,
+ kAvbHandleVerificationError,
+ };
+
+ FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kAvbHandleUninitialized) {}
static FsManagerAvbUniquePtr DoOpen(FsManagerAvbOps* avb_ops);
AvbSlotVerifyData* avb_slot_data_;
- FsManagerAvbHandleStatus status_;
+ AvbHandleStatus status_;
std::string avb_version_;
};
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
new file mode 100644
index 0000000..8a18ec0
--- /dev/null
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ * 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 __CORE_FS_TAB_H
+#define __CORE_FS_TAB_H
+
+#include <linux/dm-ioctl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+// C++ only headers
+// TODO: move this into separate header files under include/fs_mgr/*.h
+#ifdef __cplusplus
+#include <string>
+#endif
+
+__BEGIN_DECLS
+
+/*
+ * The entries must be kept in the same order as they were seen in the fstab.
+ * Unless explicitly requested, a lookup on mount point should always
+ * return the 1st one.
+ */
+struct fstab {
+ int num_entries;
+ struct fstab_rec* recs;
+ char* fstab_filename;
+};
+
+struct fstab_rec {
+ char* blk_device;
+ char* mount_point;
+ char* fs_type;
+ unsigned long flags;
+ char* fs_options;
+ int fs_mgr_flags;
+ char* key_loc;
+ char* verity_loc;
+ long long length;
+ char* label;
+ int partnum;
+ int swap_prio;
+ int max_comp_streams;
+ unsigned int zram_size;
+ uint64_t reserved_size;
+ unsigned int file_contents_mode;
+ unsigned int file_names_mode;
+ unsigned int erase_blk_size;
+ unsigned int logical_blk_size;
+};
+
+struct fstab* fs_mgr_read_fstab_default();
+struct fstab* fs_mgr_read_fstab_dt();
+struct fstab* fs_mgr_read_fstab(const char* fstab_path);
+void fs_mgr_free_fstab(struct fstab* fstab);
+
+int fs_mgr_add_entry(struct fstab* fstab, const char* mount_point, const char* fs_type,
+ const char* blk_device);
+struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const char* path);
+int fs_mgr_is_voldmanaged(const struct fstab_rec* fstab);
+int fs_mgr_is_nonremovable(const struct fstab_rec* fstab);
+int fs_mgr_is_verified(const struct fstab_rec* fstab);
+int fs_mgr_is_verifyatboot(const struct fstab_rec* fstab);
+int fs_mgr_is_avb(const struct fstab_rec* fstab);
+int fs_mgr_is_encryptable(const struct fstab_rec* fstab);
+int fs_mgr_is_file_encrypted(const struct fstab_rec* fstab);
+void fs_mgr_get_file_encryption_modes(const struct fstab_rec* fstab, const char** contents_mode_ret,
+ const char** filenames_mode_ret);
+int fs_mgr_is_convertible_to_fbe(const struct fstab_rec* fstab);
+int fs_mgr_is_noemulatedsd(const struct fstab_rec* fstab);
+int fs_mgr_is_notrim(const struct fstab_rec* fstab);
+int fs_mgr_is_formattable(const struct fstab_rec* fstab);
+int fs_mgr_is_slotselect(const struct fstab_rec* fstab);
+int fs_mgr_is_nofail(const struct fstab_rec* fstab);
+int fs_mgr_is_latemount(const struct fstab_rec* fstab);
+int fs_mgr_is_quota(const struct fstab_rec* fstab);
+
+__END_DECLS
+
+// C++ only functions
+// TODO: move this into separate header files under include/fs_mgr/*.h
+#ifdef __cplusplus
+std::string fs_mgr_get_slot_suffix();
+#endif
+
+#endif /* __CORE_FS_TAB_H */
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
index cb02a6f..2f4f4d7 100644
--- a/gatekeeperd/SoftGateKeeper.h
+++ b/gatekeeperd/SoftGateKeeper.h
@@ -26,11 +26,11 @@
}
#include <android-base/memory.h>
-#include <UniquePtr.h>
#include <gatekeeper/gatekeeper.h>
#include <iostream>
#include <unordered_map>
+#include <memory>
namespace gatekeeper {
@@ -173,7 +173,7 @@
typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap;
typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap;
- UniquePtr<uint8_t[]> key_;
+ std::unique_ptr<uint8_t[]> key_;
FailureRecordMap failure_map_;
FastHashMap fast_hash_map_;
};
diff --git a/gatekeeperd/SoftGateKeeperDevice.h b/gatekeeperd/SoftGateKeeperDevice.h
index 3463c29..e3dc068 100644
--- a/gatekeeperd/SoftGateKeeperDevice.h
+++ b/gatekeeperd/SoftGateKeeperDevice.h
@@ -19,7 +19,7 @@
#include "SoftGateKeeper.h"
-#include <UniquePtr.h>
+#include <memory>
using namespace gatekeeper;
@@ -68,7 +68,7 @@
const uint8_t *provided_password, uint32_t provided_password_length,
uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
private:
- UniquePtr<SoftGateKeeper> impl_;
+ std::unique_ptr<SoftGateKeeper> impl_;
};
} // namespace gatekeeper
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index e6eb3bc..73dab9b 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -23,6 +23,7 @@
#include <inttypes.h>
#include <stdint.h>
#include <unistd.h>
+#include <memory>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -375,7 +376,7 @@
private:
sp<IGatekeeper> hw_device;
- UniquePtr<SoftGateKeeperDevice> soft_device;
+ std::unique_ptr<SoftGateKeeperDevice> soft_device;
};
}// namespace android
diff --git a/gatekeeperd/tests/Android.mk b/gatekeeperd/tests/Android.mk
index a62b1d4..c38c64b 100644
--- a/gatekeeperd/tests/Android.mk
+++ b/gatekeeperd/tests/Android.mk
@@ -19,7 +19,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE := gatekeeperd-unit-tests
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers
LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto libbase
LOCAL_STATIC_LIBRARIES := libscrypt_static
LOCAL_C_INCLUDES := external/scrypt/lib/crypto
diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp
index 47a8bfa..100375f 100644
--- a/gatekeeperd/tests/gatekeeper_test.cpp
+++ b/gatekeeperd/tests/gatekeeper_test.cpp
@@ -19,7 +19,6 @@
#include <gtest/gtest.h>
#include <hardware/hw_auth_token.h>
-#include <UniquePtr.h>
#include "../SoftGateKeeper.h"
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index d26530b..676ee41 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -116,6 +116,10 @@
{ "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE },
{ "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE },
{ "Cold", BATTERY_HEALTH_COLD },
+ // battery health values from JEITA spec
+ { "Warm", BATTERY_HEALTH_GOOD },
+ { "Cool", BATTERY_HEALTH_GOOD },
+ { "Hot", BATTERY_HEALTH_OVERHEAT },
{ NULL, 0 },
};
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 49a534c..c76762d 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -33,6 +33,7 @@
#include <functional>
#include <android-base/file.h>
+#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <sys/socket.h>
@@ -76,8 +77,6 @@
#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
-#define LAST_KMSG_PATH "/proc/last_kmsg"
-#define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops"
#define LAST_KMSG_MAX_SZ (32 * 1024)
#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
@@ -217,14 +216,21 @@
LOGW("\n");
LOGW("*************** LAST KMSG ***************\n");
LOGW("\n");
- buf = (char *)load_file(LAST_KMSG_PSTORE_PATH, &sz);
+ const char* kmsg[] = {
+ // clang-format off
+ "/sys/fs/pstore/console-ramoops-0",
+ "/sys/fs/pstore/console-ramoops",
+ "/proc/last_kmsg",
+ // clang-format on
+ };
+ for (size_t i = 0; i < arraysize(kmsg); ++i) {
+ buf = (char*)load_file(kmsg[i], &sz);
+ if (buf && sz) break;
+ }
if (!buf || !sz) {
- buf = (char *)load_file(LAST_KMSG_PATH, &sz);
- if (!buf || !sz) {
- LOGW("last_kmsg not found. Cold reset?\n");
- goto out;
- }
+ LOGW("last_kmsg not found. Cold reset?\n");
+ goto out;
}
len = min(sz, LAST_KMSG_MAX_SZ);
diff --git a/include/private/fs_config.h b/include/private/fs_config.h
new file mode 100644
index 0000000..e9868a4
--- /dev/null
+++ b/include/private/fs_config.h
@@ -0,0 +1,4 @@
+// TODO(b/63135587) remove this file after the transitive dependency
+// from private/android_filesystem_config.h is resolved. All files that use
+// libcutils/include/private/fs_config.h should include the file directly, not
+// indirectly via private/android_filesystem_config.h.
diff --git a/include/system b/include/system
index b443c36..91d45be 120000
--- a/include/system
+++ b/include/system
@@ -1 +1 @@
-../libsystem/include/system
\ No newline at end of file
+../libsystem/include/system/
\ No newline at end of file
diff --git a/init/Android.bp b/init/Android.bp
new file mode 100644
index 0000000..672942e
--- /dev/null
+++ b/init/Android.bp
@@ -0,0 +1,185 @@
+//
+// 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.
+//
+
+cc_defaults {
+ name: "init_defaults",
+ cpp_std: "experimental",
+ sanitize: {
+ misc_undefined: ["signed-integer-overflow"],
+ },
+ cppflags: [
+ "-DLOG_UEVENTS=0",
+ "-Wall",
+ "-Wextra",
+ "-Wno-unused-parameter",
+ "-Werror",
+ "-DALLOW_LOCAL_PROP_OVERRIDE=0",
+ "-DALLOW_PERMISSIVE_SELINUX=0",
+ "-DREBOOT_BOOTLOADER_ON_PANIC=0",
+ "-DWORLD_WRITABLE_KMSG=0",
+ "-DDUMP_ON_UMOUNT_FAILURE=0",
+ "-DSHUTDOWN_ZERO_TIMEOUT=0",
+ ],
+ product_variables: {
+ debuggable: {
+ cppflags: [
+ "-UALLOW_LOCAL_PROP_OVERRIDE",
+ "-DALLOW_LOCAL_PROP_OVERRIDE=1",
+ "-UALLOW_PERMISSIVE_SELINUX",
+ "-DALLOW_PERMISSIVE_SELINUX=1",
+ "-UREBOOT_BOOTLOADER_ON_PANIC",
+ "-DREBOOT_BOOTLOADER_ON_PANIC=1",
+ "-UWORLD_WRITABLE_KMSG",
+ "-DWORLD_WRITABLE_KMSG=1",
+ "-UDUMP_ON_UMOUNT_FAILURE",
+ "-DDUMP_ON_UMOUNT_FAILURE=1",
+ ],
+ },
+ eng: {
+ cppflags: [
+ "-USHUTDOWN_ZERO_TIMEOUT",
+ "-DSHUTDOWN_ZERO_TIMEOUT=1",
+ ],
+ },
+ uml: {
+ cppflags: ["-DUSER_MODE_LINUX"],
+ }
+ },
+}
+
+cc_library_static {
+ name: "libinit",
+ defaults: ["init_defaults"],
+ srcs: [
+ "action.cpp",
+ "capabilities.cpp",
+ "descriptors.cpp",
+ "devices.cpp",
+ "firmware_handler.cpp",
+ "import_parser.cpp",
+ "log.cpp",
+ "parser.cpp",
+ "persistent_properties.cpp",
+ "property_service.cpp",
+ "security.cpp",
+ "selinux.cpp",
+ "service.cpp",
+ "rlimit_parser.cpp",
+ "tokenizer.cpp",
+ "uevent_listener.cpp",
+ "ueventd_parser.cpp",
+ "util.cpp",
+ ],
+ whole_static_libs: ["libcap"],
+ static_libs: [
+ "libbase",
+ "libselinux",
+ "liblog",
+ "libprocessgroup",
+ "libfs_mgr",
+ ],
+ include_dirs: [
+ "system/core/mkbootimg",
+ ],
+
+}
+
+/*
+This is not yet ready, see the below TODOs for what is missing
+
+cc_binary {
+ // TODO: Missing,
+ //LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+ //LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
+
+ name: "init",
+ defaults: ["init_defaults"],
+ required: [
+ "e2fsdroid",
+ "mke2fs",
+ ],
+ static_executable: true,
+ srcs: [
+ "bootchart.cpp",
+ "builtins.cpp",
+ "init.cpp",
+ "init_first_stage.cpp",
+ "keychords.cpp",
+ "reboot.cpp",
+ "sigchld_handler.cpp",
+ "ueventd.cpp",
+ "watchdogd.cpp",
+ ],
+ static_libs: [
+ "libinit",
+ "libbootloader_message",
+ "libfs_mgr",
+ "libfec",
+ "libfec_rs",
+ "libsquashfs_utils",
+ "liblogwrap",
+ "libext4_utils",
+ "libcutils",
+ "libbase",
+ "libc",
+ "libselinux",
+ "liblog",
+ "libcrypto_utils",
+ "libcrypto",
+ "libc++_static",
+ "libdl",
+ "libsparse",
+ "libz",
+ "libprocessgroup",
+ "libavb",
+ "libkeyutils",
+ ],
+ symlinks: [
+ "sbin/ueventd",
+ "sbin/watchdogd",
+ ],
+}
+*/
+
+// Tests
+// ------------------------------------------------------------------------------
+
+cc_test {
+ name: "init_tests",
+ defaults: ["init_defaults"],
+ srcs: [
+ "devices_test.cpp",
+ "init_test.cpp",
+ "persistent_properties_test.cpp",
+ "property_service_test.cpp",
+ "result_test.cpp",
+ "rlimit_parser_test.cpp",
+ "service_test.cpp",
+ "ueventd_test.cpp",
+ "util_test.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ ],
+ static_libs: [
+ "libinit",
+ "libselinux",
+ "libcrypto",
+ ],
+}
+
+subdirs = ["*"]
diff --git a/init/Android.mk b/init/Android.mk
index 42e42ec..6c28517 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -9,12 +9,14 @@
-DALLOW_LOCAL_PROP_OVERRIDE=1 \
-DALLOW_PERMISSIVE_SELINUX=1 \
-DREBOOT_BOOTLOADER_ON_PANIC=1 \
+ -DWORLD_WRITABLE_KMSG=1 \
-DDUMP_ON_UMOUNT_FAILURE=1
else
init_options += \
-DALLOW_LOCAL_PROP_OVERRIDE=0 \
-DALLOW_PERMISSIVE_SELINUX=0 \
-DREBOOT_BOOTLOADER_ON_PANIC=0 \
+ -DWORLD_WRITABLE_KMSG=0 \
-DDUMP_ON_UMOUNT_FAILURE=0
endif
@@ -37,66 +39,20 @@
# --
-# If building on Linux, then build unit test for the host.
-ifeq ($(HOST_OS),linux)
-include $(CLEAR_VARS)
-LOCAL_CPPFLAGS := $(init_cflags)
-LOCAL_SRC_FILES:= \
- parser/tokenizer.cpp \
-
-LOCAL_MODULE := libinit_parser
-LOCAL_CLANG := true
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := init_parser_tests
-LOCAL_SRC_FILES := \
- parser/tokenizer_test.cpp \
-
-LOCAL_STATIC_LIBRARIES := libinit_parser
-LOCAL_CLANG := true
-include $(BUILD_HOST_NATIVE_TEST)
-endif
-
-include $(CLEAR_VARS)
-LOCAL_CPPFLAGS := $(init_cflags)
-LOCAL_SRC_FILES:= \
- action.cpp \
- capabilities.cpp \
- descriptors.cpp \
- import_parser.cpp \
- init_parser.cpp \
- log.cpp \
- parser.cpp \
- service.cpp \
- util.cpp \
-
-LOCAL_STATIC_LIBRARIES := libbase libselinux liblog libprocessgroup
-LOCAL_WHOLE_STATIC_LIBRARIES := libcap
-LOCAL_MODULE := libinit
-LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
-include $(BUILD_STATIC_LIBRARY)
-
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES:= \
bootchart.cpp \
builtins.cpp \
- devices.cpp \
init.cpp \
init_first_stage.cpp \
keychords.cpp \
- property_service.cpp \
reboot.cpp \
- signal_handler.cpp \
+ sigchld_handler.cpp \
ueventd.cpp \
- ueventd_parser.cpp \
watchdogd.cpp \
LOCAL_MODULE:= init
-LOCAL_C_INCLUDES += \
- system/core/mkbootimg
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
@@ -114,6 +70,7 @@
libcutils \
libbase \
libc \
+ libseccomp_policy \
libselinux \
liblog \
libcrypto_utils \
@@ -123,39 +80,17 @@
libsparse \
libz \
libprocessgroup \
- libavb
+ libavb \
+ libkeyutils \
+
+LOCAL_REQUIRED_MODULES := \
+ e2fsdroid \
+ mke2fs \
# Create symlinks.
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
-LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
+LOCAL_SANITIZE := signed-integer-overflow
include $(BUILD_EXECUTABLE)
-
-
-# Unit tests.
-# =========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := init_tests
-LOCAL_SRC_FILES := \
- init_parser_test.cpp \
- property_service_test.cpp \
- service_test.cpp \
- util_test.cpp \
-
-LOCAL_SHARED_LIBRARIES += \
- libcutils \
- libbase \
-
-LOCAL_STATIC_LIBRARIES := libinit
-LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
-LOCAL_CPPFLAGS := -Wall -Wextra -Werror
-include $(BUILD_NATIVE_TEST)
-
-
-# Include targets in subdirs.
-# =========================================================
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/init/README.md b/init/README.md
index 8cb1e52..7df37ff 100644
--- a/init/README.md
+++ b/init/README.md
@@ -16,9 +16,7 @@
or options belong to the section most recently declared. Commands
or options before the first section are ignored.
-Actions and Services have unique names. If a second Action is defined
-with the same name as an existing one, its commands are appended to
-the commands of the existing action. If a second Service is defined
+Services have unique names. If a second Service is defined
with the same name as an existing one, it is ignored and an error
message is logged.
@@ -31,13 +29,21 @@
/init.rc is the primary .rc file and is loaded by the init executable
at the beginning of its execution. It is responsible for the initial
-set up of the system. It imports /init.${ro.hardware}.rc which is the
-primary vendor supplied .rc file.
+set up of the system.
-During the mount\_all command, the init executable loads all of the
-files contained within the /{system,vendor,odm}/etc/init/ directories.
-These directories are intended for all Actions and Services used after
-file system mounting.
+Devices that mount /system, /vendor through the first stage mount mechanism
+load all of the files contained within the
+/{system,vendor,odm}/etc/init/ directories immediately after loading
+the primary /init.rc. This is explained in more details in the
+Imports section of this file.
+
+Legacy devices without the first stage mount mechanism do the following:
+1. /init.rc imports /init.${ro.hardware}.rc which is the primary
+ vendor supplied .rc file.
+2. During the mount\_all command, the init executable loads all of the
+ files contained within the /{system,vendor,odm}/etc/init/ directories.
+ These directories are intended for all Actions and Services used after
+ file system mounting.
One may specify paths in the mount\_all command line to have it import
.rc files at the specified paths instead of the default ones listed above.
@@ -89,7 +95,7 @@
Actions
-------
Actions are named sequences of commands. Actions have a trigger which
-is used to determine when the action should occur. When an event
+is used to determine when the action is executed. When an event
occurs which matches an action's trigger, that action is added to
the tail of a to-be-executed queue (unless it is already on the
queue).
@@ -106,6 +112,34 @@
<command>
<command>
+Actions are added to the queue and executed based on the order that
+the file that contains them was parsed (see the Imports section), then
+sequentially within an individual file.
+
+For example if a file contains:
+
+ on boot
+ setprop a 1
+ setprop b 2
+
+ on boot && property:true=true
+ setprop c 1
+ setprop d 2
+
+ on boot
+ setprop e 1
+ setprop f 2
+
+Then when the `boot` trigger occurs and assuming the property `true`
+equals `true`, then the order of the commands executed will be:
+
+ setprop a 1
+ setprop b 2
+ setprop c 1
+ setprop d 2
+ setprop e 1
+ setprop f 2
+
Services
--------
@@ -182,6 +216,12 @@
http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
capabilities.
+`setrlimit <resource> <cur> <max>`
+> This applies the given rlimit to the service. rlimits are inherited by child
+ processes, so this effectively applies the given rlimit to the process tree
+ started by this service.
+ It is parsed similarly to the setrlimit command specified below.
+
`seclabel <seclabel>`
> Change to 'seclabel' before exec'ing this service.
Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
@@ -226,6 +266,26 @@
> Sets the child's /proc/self/oom\_score\_adj to the specified value,
which must range from -1000 to 1000.
+`memcg.swappiness <value>`
+> Sets the child's memory.swappiness to the specified value (only if memcg is mounted),
+ which must be equal or greater than 0.
+
+`memcg.soft_limit_in_bytes <value>`
+> Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted),
+ which must be equal or greater than 0.
+
+`memcg.limit_in_bytes <value>`
+> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
+ which must be equal or greater than 0.
+
+`shutdown <shutdown_behavior>`
+> Set shutdown behavior of the service process. When this is not specified,
+ the service is killed during shutdown process by using SIGTERM and SIGKILL.
+ The service with shutdown_behavior of "critical" is not killed during shutdown
+ until shutdown times out. When shutdown times out, even services tagged with
+ "shutdown critical" will be killed. When the service tagged with "shutdown critical"
+ is not running when shut down starts, it will be started.
+
Triggers
--------
@@ -276,7 +336,8 @@
`class_start <serviceclass>`
> Start all services of the specified class if they are
- not already running.
+ not already running. See the start entry for more information on
+ starting services.
`class_stop <serviceclass>`
> Stop and disable all services of the specified class if they are
@@ -321,9 +382,9 @@
Init halts executing commands until the forked process exits.
`exec_start <service>`
-> Start service a given service and halt processing of additional init commands
- until it returns. It functions similarly to the `exec` command, but uses an
- existing service definition instead of providing them as arguments.
+> Start a given service and halt the processing of additional init commands
+ until it returns. The command functions similarly to the `exec` command,
+ but uses an existing service definition in place of the exec argument vector.
`export <name> <value>`
> Set the environment variable _name_ equal to _value_ in the
@@ -392,15 +453,33 @@
`rmdir <path>`
> Calls rmdir(2) on the given path.
+`readahead <file|dir> [--fully]`
+> Calls readahead(2) on the file or files within given directory.
+ Use option --fully to read the full file content.
+
`setprop <name> <value>`
> Set system property _name_ to _value_. Properties are expanded
within _value_.
`setrlimit <resource> <cur> <max>`
-> Set the rlimit for a resource.
+> Set the rlimit for a resource. This applies to all processes launched after
+ the limit is set. It is intended to be set early in init and applied globally.
+ _resource_ is best specified using its text representation ('cpu', 'rtio', etc
+ or 'RLIM_CPU', 'RLIM_RTIO', etc). It also may be specified as the int value
+ that the resource enum corresponds to.
`start <service>`
> Start a service running if it is not already running.
+ Note that this is _not_ synchronous, and even if it were, there is
+ no guarantee that the operating system's scheduler will execute the
+ service sufficiently to guarantee anything about the service's status.
+
+> This creates an important consequence that if the service offers
+ functionality to other services, such as providing a
+ communication channel, simply starting this service before those
+ services is _not_ sufficient to guarantee that the channel has
+ been set up before those services ask for it. There must be a
+ separate mechanism to make any such guarantees.
`stop <service>`
> Stop a service from running if it is currently running.
@@ -447,21 +526,54 @@
Imports
-------
-The import keyword is not a command, but rather its own section and is
-handled immediately after the .rc file that contains it has finished
-being parsed. It takes the below form:
-
`import <path>`
> Parse an init config file, extending the current configuration.
If _path_ is a directory, each file in the directory is parsed as
a config file. It is not recursive, nested directories will
not be parsed.
-There are only two times where the init executable imports .rc files:
+The import keyword is not a command, but rather its own section,
+meaning that it does not happen as part of an Action, but rather,
+imports are handled as a file is being parsed and follow the below logic.
- 1. When it imports /init.rc during initial boot
- 2. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
- paths during mount_all
+There are only three times where the init executable imports .rc files:
+
+ 1. When it imports /init.rc or the script indicated by the property
+ `ro.boot.init_rc` during initial boot.
+ 2. When it imports /{system,vendor,odm}/etc/init/ for first stage mount
+ devices immediately after importing /init.rc.
+ 3. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
+ paths during mount_all.
+
+The order that files are imported is a bit complex for legacy reasons
+and to keep backwards compatibility. It is not strictly guaranteed.
+
+The only correct way to guarantee that a command has been run before a
+different command is to either 1) place it in an Action with an
+earlier executed trigger, or 2) place it in an Action with the same
+trigger within the same file at an earlier line.
+
+Nonetheless, the defacto order for first stage mount devices is:
+1. /init.rc is parsed then recursively each of its imports are
+ parsed.
+2. The contents of /system/etc/init/ are alphabetized and parsed
+ sequentially, with imports happening recursively after each file is
+ parsed.
+3. Step 2 is repeated for /vendor/etc/init then /odm/etc/init
+
+The below pseudocode may explain this more clearly:
+
+ fn Import(file)
+ Parse(file)
+ for (import : file.imports)
+ Import(import)
+
+ Import(/init.rc)
+ Directories = [/system/etc/init, /vendor/etc/init, /odm/etc/init]
+ for (directory : Directories)
+ files = <Alphabetical order of directory's contents>
+ for (file : files)
+ Import(file)
Properties
diff --git a/init/action.cpp b/init/action.cpp
index 2ccf0bc..60204a8 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -16,34 +16,28 @@
#include "action.h"
-#include <errno.h>
-
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include "builtins.h"
-#include "error.h"
-#include "init_parser.h"
-#include "log.h"
#include "util.h"
using android::base::Join;
-using android::base::StringPrintf;
-Command::Command(BuiltinFunction f, const std::vector<std::string>& args,
- const std::string& filename, int line)
- : func_(f), args_(args), filename_(filename), line_(line) {
-}
+namespace android {
+namespace init {
-int Command::InvokeFunc() const {
+Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
+ : func_(f), args_(args), line_(line) {}
+
+Result<Success> Command::InvokeFunc() const {
std::vector<std::string> expanded_args;
expanded_args.resize(args_.size());
expanded_args[0] = args_[0];
for (std::size_t i = 1; i < args_.size(); ++i) {
if (!expand_props(args_[i], &expanded_args[i])) {
- LOG(ERROR) << args_[0] << ": cannot expand '" << args_[i] << "'";
- return -EINVAL;
+ return Error() << "cannot expand '" << args_[i] << "'";
}
}
@@ -54,50 +48,25 @@
return Join(args_, ' ');
}
-std::string Command::BuildSourceString() const {
- if (!filename_.empty()) {
- return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
- } else {
- return std::string();
- }
-}
-
-Action::Action(bool oneshot) : oneshot_(oneshot) {
-}
+Action::Action(bool oneshot, const std::string& filename, int line)
+ : oneshot_(oneshot), filename_(filename), line_(line) {}
const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
-bool Action::AddCommand(const std::vector<std::string>& args,
- const std::string& filename, int line, std::string* err) {
+Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
if (!function_map_) {
- *err = "no function map available";
- return false;
+ return Error() << "no function map available";
}
- if (args.empty()) {
- *err = "command needed, but not provided";
- return false;
- }
+ auto function = function_map_->FindFunction(args);
+ if (!function) return Error() << function.error();
- auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
- if (!function) {
- return false;
- }
-
- AddCommand(function, args, filename, line);
- return true;
+ AddCommand(*function, args, line);
+ return Success();
}
-void Action::AddCommand(BuiltinFunction f,
- const std::vector<std::string>& args,
- const std::string& filename, int line) {
- commands_.emplace_back(f, args, filename, line);
-}
-
-void Action::CombineAction(const Action& action) {
- for (const auto& c : action.commands_) {
- commands_.emplace_back(c);
- }
+void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
+ commands_.emplace_back(f, args, line);
}
std::size_t Action::NumCommands() const {
@@ -118,83 +87,86 @@
}
void Action::ExecuteCommand(const Command& command) const {
- Timer t;
- int result = command.InvokeFunc();
+ android::base::Timer t;
+ auto result = command.InvokeFunc();
+ auto duration = t.duration();
- double duration_ms = t.duration_s() * 1000;
+ // There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
+ // device, such as '/sys/class/leds/jogball-backlight/brightness'. As of this writing, there
+ // are 198 such failures on bullhead. Instead of spamming the log reporting them, we do not
+ // report such failures unless we're running at the DEBUG log level.
+ bool report_failure = !result.has_value();
+ if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
+ result.error_errno() == ENOENT) {
+ report_failure = false;
+ }
+
// Any action longer than 50ms will be warned to user as slow operation
- if (duration_ms > 50.0 ||
+ if (report_failure || duration > 50ms ||
android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
- std::string source = command.BuildSourceString();
- LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
- << " returned " << result << " took " << duration_ms << "ms.";
+ LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
+ << ":" << command.line() << ") took " << duration.count() << "ms and "
+ << (result ? "succeeded" : "failed: " + result.error_string());
}
}
-bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
+Result<Success> Action::ParsePropertyTrigger(const std::string& trigger) {
const static std::string prop_str("property:");
std::string prop_name(trigger.substr(prop_str.length()));
size_t equal_pos = prop_name.find('=');
if (equal_pos == std::string::npos) {
- *err = "property trigger found without matching '='";
- return false;
+ return Error() << "property trigger found without matching '='";
}
std::string prop_value(prop_name.substr(equal_pos + 1));
prop_name.erase(equal_pos);
if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
- *err = "multiple property triggers found for same property";
- return false;
+ return Error() << "multiple property triggers found for same property";
}
- return true;
+ return Success();
}
-bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Action::InitTriggers(const std::vector<std::string>& args) {
const static std::string prop_str("property:");
for (std::size_t i = 0; i < args.size(); ++i) {
if (args[i].empty()) {
- *err = "empty trigger is not valid";
- return false;
+ return Error() << "empty trigger is not valid";
}
if (i % 2) {
if (args[i] != "&&") {
- *err = "&& is the only symbol allowed to concatenate actions";
- return false;
+ return Error() << "&& is the only symbol allowed to concatenate actions";
} else {
continue;
}
}
if (!args[i].compare(0, prop_str.length(), prop_str)) {
- if (!ParsePropertyTrigger(args[i], err)) {
- return false;
+ if (auto result = ParsePropertyTrigger(args[i]); !result) {
+ return result;
}
} else {
if (!event_trigger_.empty()) {
- *err = "multiple event triggers are not allowed";
- return false;
+ return Error() << "multiple event triggers are not allowed";
}
event_trigger_ = args[i];
}
}
- return true;
+ return Success();
}
-bool Action::InitSingleTrigger(const std::string& trigger) {
+Result<Success> Action::InitSingleTrigger(const std::string& trigger) {
std::vector<std::string> name_vector{trigger};
- std::string err;
- bool ret = InitTriggers(name_vector, &err);
- if (!ret) {
- LOG(ERROR) << "InitSingleTrigger failed due to: " << err;
+ if (auto result = InitTriggers(name_vector); !result) {
+ return Error() << "InitTriggers() failed: " << result.error();
}
- return ret;
+ return Success();
}
// This function checks that all property triggers are satisfied, that is
@@ -228,20 +200,17 @@
return found;
}
-bool Action::CheckEventTrigger(const std::string& trigger) const {
- return !event_trigger_.empty() &&
- trigger == event_trigger_ &&
- CheckPropertyTriggers();
+bool Action::CheckEvent(const EventTrigger& event_trigger) const {
+ return event_trigger == event_trigger_ && CheckPropertyTriggers();
}
-bool Action::CheckPropertyTrigger(const std::string& name,
- const std::string& value) const {
+bool Action::CheckEvent(const PropertyChange& property_change) const {
+ const auto& [name, value] = property_change;
return event_trigger_.empty() && CheckPropertyTriggers(name, value);
}
-bool Action::TriggersEqual(const Action& other) const {
- return property_triggers_ == other.property_triggers_ &&
- event_trigger_ == other.event_trigger_;
+bool Action::CheckEvent(const BuiltinAction& builtin_action) const {
+ return this == builtin_action;
}
std::string Action::BuildTriggersString() const {
@@ -267,41 +236,6 @@
}
}
-class EventTrigger : public Trigger {
-public:
- explicit EventTrigger(const std::string& trigger) : trigger_(trigger) {
- }
- bool CheckTriggers(const Action& action) const override {
- return action.CheckEventTrigger(trigger_);
- }
-private:
- const std::string trigger_;
-};
-
-class PropertyTrigger : public Trigger {
-public:
- PropertyTrigger(const std::string& name, const std::string& value)
- : name_(name), value_(value) {
- }
- bool CheckTriggers(const Action& action) const override {
- return action.CheckPropertyTrigger(name_, value_);
- }
-private:
- const std::string name_;
- const std::string value_;
-};
-
-class BuiltinTrigger : public Trigger {
-public:
- explicit BuiltinTrigger(Action* action) : action_(action) {
- }
- bool CheckTriggers(const Action& action) const override {
- return action_ == &action;
- }
-private:
- const Action* action_;
-};
-
ActionManager::ActionManager() : current_command_(0) {
}
@@ -311,56 +245,46 @@
}
void ActionManager::AddAction(std::unique_ptr<Action> action) {
- auto old_action_it =
- std::find_if(actions_.begin(), actions_.end(),
- [&action] (std::unique_ptr<Action>& a) {
- return action->TriggersEqual(*a);
- });
-
- if (old_action_it != actions_.end()) {
- (*old_action_it)->CombineAction(*action);
- } else {
- actions_.emplace_back(std::move(action));
- }
+ actions_.emplace_back(std::move(action));
}
void ActionManager::QueueEventTrigger(const std::string& trigger) {
- trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
+ event_queue_.emplace(trigger);
}
-void ActionManager::QueuePropertyTrigger(const std::string& name,
- const std::string& value) {
- trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
+void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
+ event_queue_.emplace(std::make_pair(name, value));
}
-void ActionManager::QueueAllPropertyTriggers() {
- QueuePropertyTrigger("", "");
+void ActionManager::QueueAllPropertyActions() {
+ QueuePropertyChange("", "");
}
-void ActionManager::QueueBuiltinAction(BuiltinFunction func,
- const std::string& name) {
- auto action = std::make_unique<Action>(true);
+void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
+ auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
std::vector<std::string> name_vector{name};
- if (!action->InitSingleTrigger(name)) {
+ if (auto result = action->InitSingleTrigger(name); !result) {
+ LOG(ERROR) << "Cannot queue BuiltinAction for " << name << ": " << result.error();
return;
}
- action->AddCommand(func, name_vector);
+ action->AddCommand(func, name_vector, 0);
- trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
+ event_queue_.emplace(action.get());
actions_.emplace_back(std::move(action));
}
void ActionManager::ExecuteOneCommand() {
- // Loop through the trigger queue until we have an action to execute
- while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
+ // Loop through the event queue until we have an action to execute
+ while (current_executing_actions_.empty() && !event_queue_.empty()) {
for (const auto& action : actions_) {
- if (trigger_queue_.front()->CheckTriggers(*action)) {
+ if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
+ event_queue_.front())) {
current_executing_actions_.emplace(action.get());
}
}
- trigger_queue_.pop();
+ event_queue_.pop();
}
if (current_executing_actions_.empty()) {
@@ -371,7 +295,8 @@
if (current_command_ == 0) {
std::string trigger_name = action->BuildTriggersString();
- LOG(INFO) << "processing action (" << trigger_name << ")";
+ LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
+ << ":" << action->line() << ")";
}
action->ExecuteOneCommand(current_command_);
@@ -393,7 +318,7 @@
}
bool ActionManager::HasMoreCommands() const {
- return !current_executing_actions_.empty() || !trigger_queue_.empty();
+ return !current_executing_actions_.empty() || !event_queue_.empty();
}
void ActionManager::DumpState() const {
@@ -402,31 +327,39 @@
}
}
-bool ActionParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+void ActionManager::ClearQueue() {
+ // We are shutting down so don't claim the oneshot builtin actions back
+ current_executing_actions_ = {};
+ event_queue_ = {};
+ current_command_ = 0;
+}
+
+Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
if (triggers.size() < 1) {
- *err = "actions must have a trigger";
- return false;
+ return Error() << "Actions must have a trigger";
}
- auto action = std::make_unique<Action>(false);
- if (!action->InitTriggers(triggers, err)) {
- return false;
+ auto action = std::make_unique<Action>(false, filename, line);
+
+ if (auto result = action->InitTriggers(triggers); !result) {
+ return Error() << "InitTriggers() failed: " << result.error();
}
action_ = std::move(action);
- return true;
+ return Success();
}
-bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const {
- return action_ ? action_->AddCommand(args, filename, line, err) : false;
+Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ return action_ ? action_->AddCommand(std::move(args), line) : Success();
}
void ActionParser::EndSection() {
if (action_ && action_->NumCommands() > 0) {
- ActionManager::GetInstance().AddAction(std::move(action_));
+ action_manager_->AddAction(std::move(action_));
}
}
+
+} // namespace init
+} // namespace android
diff --git a/init/action.h b/init/action.h
index 0bae9f0..d977f82 100644
--- a/init/action.h
+++ b/init/action.h
@@ -20,51 +20,56 @@
#include <map>
#include <queue>
#include <string>
+#include <variant>
#include <vector>
#include "builtins.h"
-#include "init_parser.h"
#include "keyword_map.h"
+#include "parser.h"
+#include "result.h"
+
+namespace android {
+namespace init {
class Command {
-public:
- Command(BuiltinFunction f, const std::vector<std::string>& args,
- const std::string& filename, int line);
+ public:
+ Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
- int InvokeFunc() const;
+ Result<Success> InvokeFunc() const;
std::string BuildCommandString() const;
- std::string BuildSourceString() const;
-private:
+ int line() const { return line_; }
+
+ private:
BuiltinFunction func_;
std::vector<std::string> args_;
- std::string filename_;
int line_;
};
-class Action {
-public:
- explicit Action(bool oneshot = false);
+using EventTrigger = std::string;
+using PropertyChange = std::pair<std::string, std::string>;
+using BuiltinAction = class Action*;
- bool AddCommand(const std::vector<std::string>& args,
- const std::string& filename, int line, std::string* err);
- void AddCommand(BuiltinFunction f,
- const std::vector<std::string>& args,
- const std::string& filename = "", int line = 0);
- void CombineAction(const Action& action);
- bool InitTriggers(const std::vector<std::string>& args, std::string* err);
- bool InitSingleTrigger(const std::string& trigger);
+class Action {
+ public:
+ explicit Action(bool oneshot, const std::string& filename, int line);
+
+ Result<Success> AddCommand(const std::vector<std::string>& args, int line);
+ void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
+ Result<Success> InitTriggers(const std::vector<std::string>& args);
+ Result<Success> InitSingleTrigger(const std::string& trigger);
std::size_t NumCommands() const;
void ExecuteOneCommand(std::size_t command) const;
void ExecuteAllCommands() const;
- bool CheckEventTrigger(const std::string& trigger) const;
- bool CheckPropertyTrigger(const std::string& name,
- const std::string& value) const;
- bool TriggersEqual(const Action& other) const;
+ bool CheckEvent(const EventTrigger& event_trigger) const;
+ bool CheckEvent(const PropertyChange& property_change) const;
+ bool CheckEvent(const BuiltinAction& builtin_action) const;
std::string BuildTriggersString() const;
void DumpState() const;
bool oneshot() const { return oneshot_; }
+ const std::string& filename() const { return filename_; }
+ int line() const { return line_; }
static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
function_map_ = function_map;
}
@@ -74,60 +79,59 @@
void ExecuteCommand(const Command& command) const;
bool CheckPropertyTriggers(const std::string& name = "",
const std::string& value = "") const;
- bool ParsePropertyTrigger(const std::string& trigger, std::string* err);
+ Result<Success> ParsePropertyTrigger(const std::string& trigger);
std::map<std::string, std::string> property_triggers_;
std::string event_trigger_;
std::vector<Command> commands_;
bool oneshot_;
+ std::string filename_;
+ int line_;
static const KeywordMap<BuiltinFunction>* function_map_;
};
-class Trigger {
-public:
- virtual ~Trigger() { }
- virtual bool CheckTriggers(const Action& action) const = 0;
-};
-
class ActionManager {
-public:
+ public:
static ActionManager& GetInstance();
+ // Exposed for testing
+ ActionManager();
+
void AddAction(std::unique_ptr<Action> action);
void QueueEventTrigger(const std::string& trigger);
- void QueuePropertyTrigger(const std::string& name, const std::string& value);
- void QueueAllPropertyTriggers();
+ void QueuePropertyChange(const std::string& name, const std::string& value);
+ void QueueAllPropertyActions();
void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
void ExecuteOneCommand();
bool HasMoreCommands() const;
void DumpState() const;
+ void ClearQueue();
-private:
- ActionManager();
-
+ private:
ActionManager(ActionManager const&) = delete;
void operator=(ActionManager const&) = delete;
std::vector<std::unique_ptr<Action>> actions_;
- std::queue<std::unique_ptr<Trigger>> trigger_queue_;
+ std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
std::queue<const Action*> current_executing_actions_;
std::size_t current_command_;
};
class ActionParser : public SectionParser {
-public:
- ActionParser() : action_(nullptr) {
- }
- bool ParseSection(const std::vector<std::string>& args,
- std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override;
+ public:
+ ActionParser(ActionManager* action_manager)
+ : action_manager_(action_manager), action_(nullptr) {}
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
void EndSection() override;
- void EndFile(const std::string&) override {
- }
-private:
+
+ private:
+ ActionManager* action_manager_;
std::unique_ptr<Action> action_;
};
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index beabea1..ec84317 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -17,7 +17,6 @@
#include "bootchart.h"
#include <dirent.h>
-#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
@@ -31,9 +30,7 @@
#include <condition_variable>
#include <memory>
#include <mutex>
-#include <string>
#include <thread>
-#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -43,6 +40,9 @@
using android::base::StringPrintf;
using namespace std::chrono_literals;
+namespace android {
+namespace init {
+
static std::thread* g_bootcharting_thread;
static std::mutex g_bootcharting_finished_mutex;
@@ -163,35 +163,38 @@
LOG(INFO) << "Bootcharting finished";
}
-static int do_bootchart_start() {
- // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
- std::string start;
- if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
- LOG(VERBOSE) << "Not bootcharting";
- return 0;
- }
+static Result<Success> do_bootchart_start() {
+ // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
+ std::string start;
+ if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
+ LOG(VERBOSE) << "Not bootcharting";
+ return Success();
+ }
- g_bootcharting_thread = new std::thread(bootchart_thread_main);
- return 0;
+ g_bootcharting_thread = new std::thread(bootchart_thread_main);
+ return Success();
}
-static int do_bootchart_stop() {
- if (!g_bootcharting_thread) return 0;
+static Result<Success> do_bootchart_stop() {
+ if (!g_bootcharting_thread) return Success();
- // Tell the worker thread it's time to quit.
- {
- std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
- g_bootcharting_finished = true;
- g_bootcharting_finished_cv.notify_one();
- }
+ // Tell the worker thread it's time to quit.
+ {
+ std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
+ g_bootcharting_finished = true;
+ g_bootcharting_finished_cv.notify_one();
+ }
- g_bootcharting_thread->join();
- delete g_bootcharting_thread;
- g_bootcharting_thread = nullptr;
- return 0;
+ g_bootcharting_thread->join();
+ delete g_bootcharting_thread;
+ g_bootcharting_thread = nullptr;
+ return Success();
}
-int do_bootchart(const std::vector<std::string>& args) {
- if (args[1] == "start") return do_bootchart_start();
- return do_bootchart_stop();
+Result<Success> do_bootchart(const std::vector<std::string>& args) {
+ if (args[1] == "start") return do_bootchart_start();
+ return do_bootchart_stop();
}
+
+} // namespace init
+} // namespace android
diff --git a/init/bootchart.h b/init/bootchart.h
index 0e3593d..f614f71 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,6 +20,14 @@
#include <string>
#include <vector>
-int do_bootchart(const std::vector<std::string>& args);
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<Success> do_bootchart(const std::vector<std::string>& args);
+
+} // namespace init
+} // namespace android
#endif /* _BOOTCHART_H */
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 7ee02d0..886c572 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -19,174 +19,184 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <fts.h>
+#include <linux/loop.h>
+#include <linux/module.h>
#include <mntent.h>
#include <net/if.h>
-#include <signal.h>
#include <sched.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/syscall.h>
+#include <sys/system_properties.h>
#include <sys/time.h>
#include <sys/types.h>
-#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
-#include <linux/loop.h>
-#include <linux/module.h>
-#include <string>
-#include <thread>
-
-#include <selinux/android.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
#include <ext4_utils/ext4_crypt.h>
#include <ext4_utils/ext4_crypt_init_extensions.h>
#include <fs_mgr.h>
-#include <logwrap/logwrap.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+#include <system/thread_defs.h>
#include "action.h"
#include "bootchart.h"
-#include "devices.h"
#include "init.h"
-#include "init_parser.h"
-#include "log.h"
+#include "parser.h"
#include "property_service.h"
#include "reboot.h"
+#include "rlimit_parser.h"
#include "service.h"
-#include "signal_handler.h"
#include "util.h"
using namespace std::literals::string_literals;
+using android::base::unique_fd;
+
#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
+namespace android {
+namespace init {
+
static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
-static int insmod(const char *filename, const char *options, int flags) {
- int fd = open(filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
- if (fd == -1) {
- PLOG(ERROR) << "insmod: open(\"" << filename << "\") failed";
- return -1;
- }
- int rc = syscall(__NR_finit_module, fd, options, flags);
- if (rc == -1) {
- PLOG(ERROR) << "finit_module for \"" << filename << "\" failed";
- }
- close(fd);
- return rc;
-}
-
-static int __ifupdown(const char *interface, int up) {
- struct ifreq ifr;
- int s, ret;
-
- strlcpy(ifr.ifr_name, interface, IFNAMSIZ);
-
- s = socket(AF_INET, SOCK_DGRAM, 0);
- if (s < 0)
- return -1;
-
- ret = ioctl(s, SIOCGIFFLAGS, &ifr);
- if (ret < 0) {
- goto done;
- }
-
- if (up)
- ifr.ifr_flags |= IFF_UP;
- else
- ifr.ifr_flags &= ~IFF_UP;
-
- ret = ioctl(s, SIOCSIFFLAGS, &ifr);
-
-done:
- close(s);
- return ret;
-}
-
-static int reboot_into_recovery(const std::vector<std::string>& options) {
+static Result<Success> reboot_into_recovery(const std::vector<std::string>& options) {
std::string err;
if (!write_bootloader_message(options, &err)) {
- LOG(ERROR) << "failed to set bootloader message: " << err;
- return -1;
+ return Error() << "Failed to set bootloader message: " << err;
}
- DoReboot(ANDROID_RB_RESTART2, "reboot", "recovery", false);
- return 0;
+ property_set("sys.powerctl", "reboot,recovery");
+ return Success();
}
-static int do_class_start(const std::vector<std::string>& args) {
- /* Starting a class does not start services
- * which are explicitly disabled. They must
- * be started individually.
- */
- ServiceManager::GetInstance().
- ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
- return 0;
-}
-
-static int do_class_stop(const std::vector<std::string>& args) {
- ServiceManager::GetInstance().
- ForEachServiceInClass(args[1], [] (Service* s) { s->Stop(); });
- return 0;
-}
-
-static int do_class_reset(const std::vector<std::string>& args) {
- ServiceManager::GetInstance().
- ForEachServiceInClass(args[1], [] (Service* s) { s->Reset(); });
- return 0;
-}
-
-static int do_class_restart(const std::vector<std::string>& args) {
- ServiceManager::GetInstance().
- ForEachServiceInClass(args[1], [] (Service* s) { s->Restart(); });
- return 0;
-}
-
-static int do_domainname(const std::vector<std::string>& args) {
- return write_file("/proc/sys/kernel/domainname", args[1]) ? 0 : 1;
-}
-
-static int do_enable(const std::vector<std::string>& args) {
- Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
- if (!svc) {
- return -1;
+template <typename F>
+static void ForEachServiceInClass(const std::string& classname, F function) {
+ for (const auto& service : ServiceList::GetInstance()) {
+ if (service->classnames().count(classname)) std::invoke(function, service);
}
- return svc->Enable();
}
-static int do_exec(const std::vector<std::string>& args) {
- return ServiceManager::GetInstance().Exec(args) ? 0 : -1;
+static Result<Success> do_class_start(const std::vector<std::string>& args) {
+ // Starting a class does not start services which are explicitly disabled.
+ // They must be started individually.
+ ForEachServiceInClass(args[1], &Service::StartIfNotDisabled);
+ return Success();
}
-static int do_exec_start(const std::vector<std::string>& args) {
- return ServiceManager::GetInstance().ExecStart(args[1]) ? 0 : -1;
+static Result<Success> do_class_stop(const std::vector<std::string>& args) {
+ ForEachServiceInClass(args[1], &Service::Stop);
+ return Success();
}
-static int do_export(const std::vector<std::string>& args) {
- return add_environment(args[1].c_str(), args[2].c_str());
+static Result<Success> do_class_reset(const std::vector<std::string>& args) {
+ ForEachServiceInClass(args[1], &Service::Reset);
+ return Success();
}
-static int do_hostname(const std::vector<std::string>& args) {
- return write_file("/proc/sys/kernel/hostname", args[1]) ? 0 : 1;
+static Result<Success> do_class_restart(const std::vector<std::string>& args) {
+ ForEachServiceInClass(args[1], &Service::Restart);
+ return Success();
}
-static int do_ifup(const std::vector<std::string>& args) {
- return __ifupdown(args[1].c_str(), 1);
+static Result<Success> do_domainname(const std::vector<std::string>& args) {
+ if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
+ return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
+ }
+ return Success();
}
-static int do_insmod(const std::vector<std::string>& args) {
+static Result<Success> do_enable(const std::vector<std::string>& args) {
+ Service* svc = ServiceList::GetInstance().FindService(args[1]);
+ if (!svc) return Error() << "Could not find service";
+
+ if (auto result = svc->Enable(); !result) {
+ return Error() << "Could not enable service: " << result.error();
+ }
+
+ return Success();
+}
+
+static Result<Success> do_exec(const std::vector<std::string>& args) {
+ auto service = Service::MakeTemporaryOneshotService(args);
+ if (!service) {
+ return Error() << "Could not create exec service";
+ }
+ if (auto result = service->ExecStart(); !result) {
+ return Error() << "Could not start exec service: " << result.error();
+ }
+
+ ServiceList::GetInstance().AddService(std::move(service));
+ return Success();
+}
+
+static Result<Success> do_exec_start(const std::vector<std::string>& args) {
+ Service* service = ServiceList::GetInstance().FindService(args[1]);
+ if (!service) {
+ return Error() << "Service not found";
+ }
+
+ if (auto result = service->ExecStart(); !result) {
+ return Error() << "Could not start exec service: " << result.error();
+ }
+
+ return Success();
+}
+
+static Result<Success> do_export(const std::vector<std::string>& args) {
+ if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {
+ return ErrnoError() << "setenv() failed";
+ }
+ return Success();
+}
+
+static Result<Success> do_hostname(const std::vector<std::string>& args) {
+ if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
+ return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
+ }
+ return Success();
+}
+
+static Result<Success> do_ifup(const std::vector<std::string>& args) {
+ struct ifreq ifr;
+
+ strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
+
+ unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM, 0)));
+ if (s < 0) return ErrnoError() << "opening socket failed";
+
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
+ return ErrnoError() << "ioctl(..., SIOCGIFFLAGS, ...) failed";
+ }
+
+ ifr.ifr_flags |= IFF_UP;
+
+ if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
+ return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed";
+ }
+
+ return Success();
+}
+
+static Result<Success> do_insmod(const std::vector<std::string>& args) {
int flags = 0;
auto it = args.begin() + 1;
@@ -197,45 +207,56 @@
std::string filename = *it++;
std::string options = android::base::Join(std::vector<std::string>(it, args.end()), ' ');
- return insmod(filename.c_str(), options.c_str(), flags);
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+ if (fd == -1) return ErrnoError() << "open(\"" << filename << "\") failed";
+
+ int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags);
+ if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed";
+
+ return Success();
}
-static int do_mkdir(const std::vector<std::string>& args) {
+// mkdir <path> [mode] [owner] [group]
+static Result<Success> do_mkdir(const std::vector<std::string>& args) {
mode_t mode = 0755;
- int ret;
-
- /* mkdir <path> [mode] [owner] [group] */
-
if (args.size() >= 3) {
mode = std::strtoul(args[2].c_str(), 0, 8);
}
- ret = make_dir(args[1].c_str(), mode);
- /* chmod in case the directory already exists */
- if (ret == -1 && errno == EEXIST) {
- ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
- }
- if (ret == -1) {
- return -errno;
+ if (!make_dir(args[1], mode)) {
+ /* chmod in case the directory already exists */
+ if (errno == EEXIST) {
+ if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+ return ErrnoError() << "fchmodat() failed";
+ }
+ } else {
+ return ErrnoError() << "mkdir() failed";
+ }
}
if (args.size() >= 4) {
- uid_t uid = decode_uid(args[3].c_str());
- gid_t gid = -1;
+ auto uid = DecodeUid(args[3]);
+ if (!uid) {
+ return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
+ }
+ Result<gid_t> gid = -1;
if (args.size() == 5) {
- gid = decode_uid(args[4].c_str());
+ gid = DecodeUid(args[4]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
+ }
}
- if (lchown(args[1].c_str(), uid, gid) == -1) {
- return -errno;
+ if (lchown(args[1].c_str(), *uid, *gid) == -1) {
+ return ErrnoError() << "lchown failed";
}
/* chown may have cleared S_ISUID and S_ISGID, chmod again */
if (mode & (S_ISUID | S_ISGID)) {
- ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
- if (ret == -1) {
- return -errno;
+ if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+ return ErrnoError() << "fchmodat failed";
}
}
}
@@ -246,15 +267,18 @@
"--prompt_and_wipe_data",
"--reason=set_policy_failed:"s + args[1]};
reboot_into_recovery(options);
- return -1;
+ return Error() << "reboot into recovery failed";
}
}
- return 0;
+ return Success();
}
/* umount <path> */
-static int do_umount(const std::vector<std::string>& args) {
- return umount(args[1].c_str());
+static Result<Success> do_umount(const std::vector<std::string>& args) {
+ if (umount(args[1].c_str()) < 0) {
+ return ErrnoError() << "umount() failed";
+ }
+ return Success();
}
static struct {
@@ -282,16 +306,13 @@
#define DATA_MNT_POINT "/data"
/* mount <type> <device> <path> <flags ...> <options> */
-static int do_mount(const std::vector<std::string>& args) {
- char tmp[64];
- const char *source, *target, *system;
- const char *options = NULL;
+static Result<Success> do_mount(const std::vector<std::string>& args) {
+ const char* options = nullptr;
unsigned flags = 0;
- std::size_t na = 0;
- int n, i;
- int wait = 0;
+ bool wait = false;
- for (na = 4; na < args.size(); na++) {
+ for (size_t na = 4; na < args.size(); na++) {
+ size_t i;
for (i = 0; mount_flags[i].name; i++) {
if (!args[na].compare(mount_flags[i].name)) {
flags |= mount_flags[i].flag;
@@ -300,71 +321,54 @@
}
if (!mount_flags[i].name) {
- if (!args[na].compare("wait"))
- wait = 1;
- /* if our last argument isn't a flag, wolf it up as an option string */
- else if (na + 1 == args.size())
+ if (!args[na].compare("wait")) {
+ wait = true;
+ // If our last argument isn't a flag, wolf it up as an option string.
+ } else if (na + 1 == args.size()) {
options = args[na].c_str();
+ }
}
}
- system = args[1].c_str();
- source = args[2].c_str();
- target = args[3].c_str();
+ const char* system = args[1].c_str();
+ const char* source = args[2].c_str();
+ const char* target = args[3].c_str();
- if (!strncmp(source, "loop@", 5)) {
- int mode, loop, fd;
- struct loop_info info;
+ if (android::base::StartsWith(source, "loop@")) {
+ int mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
+ unique_fd fd(TEMP_FAILURE_RETRY(open(source + 5, mode | O_CLOEXEC)));
+ if (fd < 0) return ErrnoError() << "open(" << source + 5 << ", " << mode << ") failed";
- mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
- fd = open(source + 5, mode | O_CLOEXEC);
- if (fd < 0) {
- return -1;
- }
+ for (size_t n = 0;; n++) {
+ std::string tmp = android::base::StringPrintf("/dev/block/loop%zu", n);
+ unique_fd loop(TEMP_FAILURE_RETRY(open(tmp.c_str(), mode | O_CLOEXEC)));
+ if (loop < 0) return ErrnoError() << "open(" << tmp << ", " << mode << ") failed";
- for (n = 0; ; n++) {
- snprintf(tmp, sizeof(tmp), "/dev/block/loop%d", n);
- loop = open(tmp, mode | O_CLOEXEC);
- if (loop < 0) {
- close(fd);
- return -1;
- }
-
+ loop_info info;
/* if it is a blank loop device */
if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
/* if it becomes our loop device */
- if (ioctl(loop, LOOP_SET_FD, fd) >= 0) {
- close(fd);
-
- if (mount(tmp, target, system, flags, options) < 0) {
+ if (ioctl(loop, LOOP_SET_FD, fd.get()) >= 0) {
+ if (mount(tmp.c_str(), target, system, flags, options) < 0) {
ioctl(loop, LOOP_CLR_FD, 0);
- close(loop);
- return -1;
+ return ErrnoError() << "mount() failed";
}
-
- close(loop);
- goto exit_success;
+ return Success();
}
}
-
- close(loop);
}
- close(fd);
- LOG(ERROR) << "out of loopback devices";
- return -1;
+ return Error() << "out of loopback devices";
} else {
if (wait)
wait_for_file(source, kCommandRetryTimeout);
if (mount(source, target, system, flags, options) < 0) {
- return -1;
+ return ErrnoError() << "mount() failed";
}
}
-exit_success:
- return 0;
-
+ return Success();
}
/* Imports .rc files from the specified paths. Default ones are applied if none is given.
@@ -372,21 +376,15 @@
* start_index: index of the first path in the args list
*/
static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
- Parser& parser = Parser::GetInstance();
+ auto& action_manager = ActionManager::GetInstance();
+ auto& service_list = ServiceList::GetInstance();
+ Parser parser = CreateParser(action_manager, service_list);
if (end_index <= start_index) {
// Fallbacks for partitions on which early mount isn't enabled.
- if (!parser.is_system_etc_init_loaded()) {
- parser.ParseConfig("/system/etc/init");
- parser.set_is_system_etc_init_loaded(true);
+ for (const auto& path : late_import_paths) {
+ parser.ParseConfig(path);
}
- if (!parser.is_vendor_etc_init_loaded()) {
- parser.ParseConfig("/vendor/etc/init");
- parser.set_is_vendor_etc_init_loaded(true);
- }
- if (!parser.is_odm_etc_init_loaded()) {
- parser.ParseConfig("/odm/etc/init");
- parser.set_is_odm_etc_init_loaded(true);
- }
+ late_import_paths.clear();
} else {
for (size_t i = start_index; i < end_index; ++i) {
parser.ParseConfig(args[i]);
@@ -395,16 +393,14 @@
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
- if (false) parser.DumpState();
+ if (false) DumpState();
}
/* mount_fstab
*
* Call fs_mgr_mount_all() to mount the given fstab
*/
-static int mount_fstab(const char* fstabfile, int mount_mode) {
- int ret = -1;
-
+static Result<int> mount_fstab(const char* fstabfile, int mount_mode) {
/*
* Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and
* do the call in the child to provide protection to the main init
@@ -422,9 +418,9 @@
}
if (WIFEXITED(status)) {
- ret = WEXITSTATUS(status);
+ return WEXITSTATUS(status);
} else {
- ret = -1;
+ return Error() << "child aborted";
}
} else if (pid == 0) {
/* child, call fs_mgr_mount_all() */
@@ -441,10 +437,8 @@
}
_exit(child_ret);
} else {
- /* fork failed, return an error */
- return -1;
+ return Error() << "fork() failed";
}
- return ret;
}
/* Queue event based on fs_mgr return code.
@@ -456,29 +450,33 @@
*
* return code is processed based on input code
*/
-static int queue_fs_event(int code) {
- int ret = code;
+static Result<Success> queue_fs_event(int code) {
if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
ActionManager::GetInstance().QueueEventTrigger("encrypt");
+ return Success();
} else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "block");
ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
+ return Success();
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
property_set("ro.crypto.state", "unencrypted");
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+ return Success();
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
property_set("ro.crypto.state", "unsupported");
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+ return Success();
} else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
/* Setup a wipe via recovery, and reboot into recovery */
PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
- ret = reboot_into_recovery(options);
+ reboot_into_recovery(options);
+ return Error() << "reboot_into_recovery() failed";
/* If reboot worked, there is no return. */
} else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
if (e4crypt_install_keyring()) {
- return -1;
+ return Error() << "e4crypt_install_keyring() failed";
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
@@ -486,12 +484,13 @@
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+ return Success();
} else if (code > 0) {
- PLOG(ERROR) << "fs_mgr_mount_all returned unexpected error " << code;
+ Error() << "fs_mgr_mount_all() returned unexpected error " << code;
}
/* else ... < 0: error */
- return ret;
+ return Error() << "Invalid code: " << code;
}
/* mount_all <fstab> [ <path> ]* [--<options>]*
@@ -499,7 +498,7 @@
* This function might request a reboot, in which case it will
* not return.
*/
-static int do_mount_all(const std::vector<std::string>& args) {
+static Result<Success> do_mount_all(const std::vector<std::string>& args) {
std::size_t na = 0;
bool import_rc = true;
bool queue_event = true;
@@ -522,11 +521,13 @@
}
}
- std::string prop_name = android::base::StringPrintf("ro.boottime.init.mount_all.%s",
- prop_post_fix);
- Timer t;
- int ret = mount_fstab(fstabfile, mount_mode);
- property_set(prop_name.c_str(), std::to_string(t.duration_ms()).c_str());
+ std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
+ android::base::Timer t;
+ auto mount_fstab_return_code = mount_fstab(fstabfile, mount_mode);
+ if (!mount_fstab_return_code) {
+ return Error() << "mount_fstab() failed " << mount_fstab_return_code.error();
+ }
+ property_set(prop_name, std::to_string(t.duration().count()));
if (import_rc) {
/* Paths of .rc files are specified at the 2nd argument and beyond */
@@ -536,13 +537,16 @@
if (queue_event) {
/* queue_fs_event will queue event based on mount_fstab return code
* and return processed return code*/
- ret = queue_fs_event(ret);
+ auto queue_fs_result = queue_fs_event(*mount_fstab_return_code);
+ if (!queue_fs_result) {
+ return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
+ }
}
- return ret;
+ return Success();
}
-static int do_swapon_all(const std::vector<std::string>& args) {
+static Result<Success> do_swapon_all(const std::vector<std::string>& args) {
struct fstab *fstab;
int ret;
@@ -550,128 +554,238 @@
ret = fs_mgr_swapon_all(fstab);
fs_mgr_free_fstab(fstab);
- return ret;
+ if (ret != 0) return Error() << "fs_mgr_swapon_all() failed";
+ return Success();
}
-static int do_setprop(const std::vector<std::string>& args) {
- const char* name = args[1].c_str();
- const char* value = args[2].c_str();
- property_set(name, value);
- return 0;
+static Result<Success> do_setprop(const std::vector<std::string>& args) {
+ property_set(args[1], args[2]);
+ return Success();
}
-static int do_setrlimit(const std::vector<std::string>& args) {
- struct rlimit limit;
- int resource;
- if (android::base::ParseInt(args[1], &resource) &&
- android::base::ParseUint(args[2], &limit.rlim_cur) &&
- android::base::ParseUint(args[3], &limit.rlim_max)) {
- return setrlimit(resource, &limit);
+static Result<Success> do_setrlimit(const std::vector<std::string>& args) {
+ auto rlimit = ParseRlimit(args);
+ if (!rlimit) return rlimit.error();
+
+ if (setrlimit(rlimit->first, &rlimit->second) == -1) {
+ return ErrnoError() << "setrlimit failed";
}
- LOG(WARNING) << "ignoring setrlimit " << args[1] << " " << args[2] << " " << args[3];
- return -1;
+ return Success();
}
-static int do_start(const std::vector<std::string>& args) {
- Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
- if (!svc) {
- LOG(ERROR) << "do_start: Service " << args[1] << " not found";
- return -1;
+static Result<Success> do_start(const std::vector<std::string>& args) {
+ Service* svc = ServiceList::GetInstance().FindService(args[1]);
+ if (!svc) return Error() << "service " << args[1] << " not found";
+ if (auto result = svc->Start(); !result) {
+ return Error() << "Could not start service: " << result.error();
}
- if (!svc->Start())
- return -1;
- return 0;
+ return Success();
}
-static int do_stop(const std::vector<std::string>& args) {
- Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
- if (!svc) {
- LOG(ERROR) << "do_stop: Service " << args[1] << " not found";
- return -1;
- }
+static Result<Success> do_stop(const std::vector<std::string>& args) {
+ Service* svc = ServiceList::GetInstance().FindService(args[1]);
+ if (!svc) return Error() << "service " << args[1] << " not found";
svc->Stop();
- return 0;
+ return Success();
}
-static int do_restart(const std::vector<std::string>& args) {
- Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
- if (!svc) {
- LOG(ERROR) << "do_restart: Service " << args[1] << " not found";
- return -1;
- }
+static Result<Success> do_restart(const std::vector<std::string>& args) {
+ Service* svc = ServiceList::GetInstance().FindService(args[1]);
+ if (!svc) return Error() << "service " << args[1] << " not found";
svc->Restart();
- return 0;
+ return Success();
}
-static int do_trigger(const std::vector<std::string>& args) {
+static Result<Success> do_trigger(const std::vector<std::string>& args) {
ActionManager::GetInstance().QueueEventTrigger(args[1]);
- return 0;
+ return Success();
}
-static int do_symlink(const std::vector<std::string>& args) {
- return symlink(args[1].c_str(), args[2].c_str());
-}
-
-static int do_rm(const std::vector<std::string>& args) {
- return unlink(args[1].c_str());
-}
-
-static int do_rmdir(const std::vector<std::string>& args) {
- return rmdir(args[1].c_str());
-}
-
-static int do_sysclktz(const std::vector<std::string>& args) {
- struct timezone tz = {};
- if (android::base::ParseInt(args[1], &tz.tz_minuteswest) && settimeofday(NULL, &tz) != -1) {
- return 0;
+static Result<Success> do_symlink(const std::vector<std::string>& args) {
+ if (symlink(args[1].c_str(), args[2].c_str()) < 0) {
+ // The symlink builtin is often used to create symlinks for older devices to be backwards
+ // compatible with new paths, therefore we skip reporting this error.
+ if (errno == EEXIST && android::base::GetMinimumLogSeverity() > android::base::DEBUG) {
+ return Success();
+ }
+ return ErrnoError() << "symlink() failed";
}
- return -1;
+ return Success();
}
-static int do_verity_load_state(const std::vector<std::string>& args) {
+static Result<Success> do_rm(const std::vector<std::string>& args) {
+ if (unlink(args[1].c_str()) < 0) {
+ return ErrnoError() << "unlink() failed";
+ }
+ return Success();
+}
+
+static Result<Success> do_rmdir(const std::vector<std::string>& args) {
+ if (rmdir(args[1].c_str()) < 0) {
+ return ErrnoError() << "rmdir() failed";
+ }
+ return Success();
+}
+
+static Result<Success> do_sysclktz(const std::vector<std::string>& args) {
+ struct timezone tz = {};
+ if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
+ return Error() << "Unable to parse mins_west_of_gmt";
+ }
+
+ if (settimeofday(nullptr, &tz) == -1) {
+ return ErrnoError() << "settimeofday() failed";
+ }
+ return Success();
+}
+
+static Result<Success> do_verity_load_state(const std::vector<std::string>& args) {
int mode = -1;
bool loaded = fs_mgr_load_verity_state(&mode);
if (loaded && mode != VERITY_MODE_DEFAULT) {
ActionManager::GetInstance().QueueEventTrigger("verity-logging");
}
- return loaded ? 0 : 1;
+ if (!loaded) return Error() << "Could not load verity state";
+
+ return Success();
}
static void verity_update_property(fstab_rec *fstab, const char *mount_point,
int mode, int status) {
- property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(),
- android::base::StringPrintf("%d", mode).c_str());
+ property_set("partition."s + mount_point + ".verified", std::to_string(mode));
}
-static int do_verity_update_state(const std::vector<std::string>& args) {
- return fs_mgr_update_verity_state(verity_update_property) ? 0 : 1;
-}
-
-static int do_write(const std::vector<std::string>& args) {
- return write_file(args[1], args[2]) ? 0 : 1;
-}
-
-static int do_copy(const std::vector<std::string>& args) {
- std::string data;
- if (read_file(args[1], &data)) {
- return write_file(args[2], data) ? 0 : 1;
+static Result<Success> do_verity_update_state(const std::vector<std::string>& args) {
+ if (!fs_mgr_update_verity_state(verity_update_property)) {
+ return Error() << "fs_mgr_update_verity_state() failed";
}
- return 1;
+ return Success();
}
-static int do_chown(const std::vector<std::string>& args) {
- /* GID is optional. */
- if (args.size() == 3) {
- if (lchown(args[2].c_str(), decode_uid(args[1].c_str()), -1) == -1)
- return -errno;
- } else if (args.size() == 4) {
- if (lchown(args[3].c_str(), decode_uid(args[1].c_str()),
- decode_uid(args[2].c_str())) == -1)
- return -errno;
- } else {
- return -1;
+static Result<Success> do_write(const std::vector<std::string>& args) {
+ if (auto result = WriteFile(args[1], args[2]); !result) {
+ return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
}
- return 0;
+
+ return Success();
+}
+
+static Result<Success> readahead_file(const std::string& filename, bool fully) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY)));
+ if (fd == -1) {
+ return ErrnoError() << "Error opening file";
+ }
+ if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED)) {
+ return ErrnoError() << "Error posix_fadvise file";
+ }
+ if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+ return ErrnoError() << "Error readahead file";
+ }
+ if (fully) {
+ char buf[BUFSIZ];
+ ssize_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
+ }
+ if (n != 0) {
+ return ErrnoError() << "Error reading file";
+ }
+ }
+ return Success();
+}
+
+static Result<Success> do_readahead(const std::vector<std::string>& args) {
+ struct stat sb;
+
+ if (stat(args[1].c_str(), &sb)) {
+ return ErrnoError() << "Error opening " << args[1];
+ }
+
+ bool readfully = false;
+ if (args.size() == 3 && args[2] == "--fully") {
+ readfully = true;
+ }
+ // We will do readahead in a forked process in order not to block init
+ // since it may block while it reads the
+ // filesystem metadata needed to locate the requested blocks. This
+ // occurs frequently with ext[234] on large files using indirect blocks
+ // instead of extents, giving the appearance that the call blocks until
+ // the requested data has been read.
+ pid_t pid = fork();
+ if (pid == 0) {
+ if (setpriority(PRIO_PROCESS, 0, static_cast<int>(ANDROID_PRIORITY_LOWEST)) != 0) {
+ PLOG(WARNING) << "setpriority failed";
+ }
+ if (android_set_ioprio(0, IoSchedClass_IDLE, 7)) {
+ PLOG(WARNING) << "ioprio_get failed";
+ }
+ android::base::Timer t;
+ if (S_ISREG(sb.st_mode)) {
+ if (auto result = readahead_file(args[1], readfully); !result) {
+ LOG(WARNING) << "Unable to readahead '" << args[1] << "': " << result.error();
+ _exit(EXIT_FAILURE);
+ }
+ } else if (S_ISDIR(sb.st_mode)) {
+ char* paths[] = {const_cast<char*>(args[1].data()), nullptr};
+ std::unique_ptr<FTS, decltype(&fts_close)> fts(
+ fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr), fts_close);
+ if (!fts) {
+ PLOG(ERROR) << "Error opening directory: " << args[1];
+ _exit(EXIT_FAILURE);
+ }
+ // Traverse the entire hierarchy and do readahead
+ for (FTSENT* ftsent = fts_read(fts.get()); ftsent != nullptr;
+ ftsent = fts_read(fts.get())) {
+ if (ftsent->fts_info & FTS_F) {
+ const std::string filename = ftsent->fts_accpath;
+ if (auto result = readahead_file(filename, readfully); !result) {
+ LOG(WARNING)
+ << "Unable to readahead '" << filename << "': " << result.error();
+ }
+ }
+ }
+ }
+ LOG(INFO) << "Readahead " << args[1] << " took " << t << " asynchronously";
+ _exit(0);
+ } else if (pid < 0) {
+ return ErrnoError() << "Fork failed";
+ }
+ return Success();
+}
+
+static Result<Success> do_copy(const std::vector<std::string>& args) {
+ auto file_contents = ReadFile(args[1]);
+ if (!file_contents) {
+ return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
+ }
+ if (auto result = WriteFile(args[2], *file_contents); !result) {
+ return Error() << "Could not write to output file '" << args[2] << "': " << result.error();
+ }
+
+ return Success();
+}
+
+static Result<Success> do_chown(const std::vector<std::string>& args) {
+ auto uid = DecodeUid(args[1]);
+ if (!uid) {
+ return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
+ }
+
+ // GID is optional and pushes the index of path out by one if specified.
+ const std::string& path = (args.size() == 4) ? args[3] : args[2];
+ Result<gid_t> gid = -1;
+
+ if (args.size() == 4) {
+ gid = DecodeUid(args[2]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[2] << "': " << gid.error();
+ }
+ }
+
+ if (lchown(path.c_str(), *uid, *gid) == -1) {
+ return ErrnoError() << "lchown() failed";
+ }
+
+ return Success();
}
static mode_t get_mode(const char *s) {
@@ -687,15 +801,15 @@
return mode;
}
-static int do_chmod(const std::vector<std::string>& args) {
+static Result<Success> do_chmod(const std::vector<std::string>& args) {
mode_t mode = get_mode(args[1].c_str());
if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
- return -errno;
+ return ErrnoError() << "fchmodat() failed";
}
- return 0;
+ return Success();
}
-static int do_restorecon(const std::vector<std::string>& args) {
+static Result<Success> do_restorecon(const std::vector<std::string>& args) {
int ret = 0;
struct flag_type {const char* name; int value;};
@@ -712,8 +826,7 @@
for (size_t i = 1; i < args.size(); ++i) {
if (android::base::StartsWith(args[i], "--")) {
if (!in_flags) {
- LOG(ERROR) << "restorecon - flags must precede paths";
- return -1;
+ return Error() << "flags must precede paths";
}
bool found = false;
for (size_t j = 0; flags[j].name; ++j) {
@@ -724,26 +837,27 @@
}
}
if (!found) {
- LOG(ERROR) << "restorecon - bad flag " << args[i];
- return -1;
+ return Error() << "bad flag " << args[i];
}
} else {
in_flags = false;
- if (restorecon(args[i].c_str(), flag) < 0) {
- ret = -errno;
+ if (selinux_android_restorecon(args[i].c_str(), flag) < 0) {
+ ret = errno;
}
}
}
- return ret;
+
+ if (ret) return ErrnoError() << "selinux_android_restorecon() failed";
+ return Success();
}
-static int do_restorecon_recursive(const std::vector<std::string>& args) {
+static Result<Success> do_restorecon_recursive(const std::vector<std::string>& args) {
std::vector<std::string> non_const_args(args);
non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
return do_restorecon(non_const_args);
}
-static int do_loglevel(const std::vector<std::string>& args) {
+static Result<Success> do_loglevel(const std::vector<std::string>& args) {
// TODO: support names instead/as well?
int log_level = -1;
android::base::ParseInt(args[1], &log_level);
@@ -758,92 +872,79 @@
case 1:
case 0: severity = android::base::FATAL; break;
default:
- LOG(ERROR) << "loglevel: invalid log level " << log_level;
- return -EINVAL;
+ return Error() << "invalid log level " << log_level;
}
android::base::SetMinimumLogSeverity(severity);
- return 0;
+ return Success();
}
-static int do_load_persist_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_persist_props(const std::vector<std::string>& args) {
load_persist_props();
- return 0;
+ return Success();
}
-static int do_load_system_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_system_props(const std::vector<std::string>& args) {
load_system_props();
- return 0;
+ return Success();
}
-static int do_wait(const std::vector<std::string>& args) {
- if (args.size() == 2) {
- return wait_for_file(args[1].c_str(), kCommandRetryTimeout);
- } else if (args.size() == 3) {
- int timeout;
- if (android::base::ParseInt(args[2], &timeout)) {
- return wait_for_file(args[1].c_str(), std::chrono::seconds(timeout));
+static Result<Success> do_wait(const std::vector<std::string>& args) {
+ auto timeout = kCommandRetryTimeout;
+ if (args.size() == 3) {
+ int timeout_int;
+ if (!android::base::ParseInt(args[2], &timeout_int)) {
+ return Error() << "failed to parse timeout";
}
+ timeout = std::chrono::seconds(timeout_int);
}
- return -1;
+
+ if (wait_for_file(args[1].c_str(), timeout) != 0) {
+ return Error() << "wait_for_file() failed";
+ }
+
+ return Success();
}
-static int do_wait_for_prop(const std::vector<std::string>& args) {
+static Result<Success> do_wait_for_prop(const std::vector<std::string>& args) {
const char* name = args[1].c_str();
const char* value = args[2].c_str();
size_t value_len = strlen(value);
if (!is_legal_property_name(name)) {
- LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
- << "\") failed: bad name";
- return -1;
+ return Error() << "is_legal_property_name(" << name << ") failed";
}
if (value_len >= PROP_VALUE_MAX) {
- LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
- << "\") failed: value too long";
- return -1;
+ return Error() << "value too long";
}
if (!start_waiting_for_property(name, value)) {
- LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
- << "\") failed: init already in waiting";
- return -1;
+ return Error() << "already waiting for a property";
}
- return 0;
-}
-
-/*
- * Callback to make a directory from the ext4 code
- */
-static int do_installkeys_ensure_dir_exists(const char* dir) {
- if (make_dir(dir, 0700) && errno != EEXIST) {
- return -1;
- }
-
- return 0;
+ return Success();
}
static bool is_file_crypto() {
return android::base::GetProperty("ro.crypto.type", "") == "file";
}
-static int do_installkey(const std::vector<std::string>& args) {
- if (!is_file_crypto()) {
- return 0;
- }
+static Result<Success> do_installkey(const std::vector<std::string>& args) {
+ if (!is_file_crypto()) return Success();
+
auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
- if (do_installkeys_ensure_dir_exists(unencrypted_dir.c_str())) {
- PLOG(ERROR) << "Failed to create " << unencrypted_dir;
- return -1;
+ if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
+ return ErrnoError() << "Failed to create " << unencrypted_dir;
}
std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
"enablefilecrypto"};
return do_exec(exec_args);
}
-static int do_init_user0(const std::vector<std::string>& args) {
- return e4crypt_do_init_user0();
+static Result<Success> do_init_user0(const std::vector<std::string>& args) {
+ std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
+ "init_user0"};
+ return do_exec(exec_args);
}
-BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map builtin_functions = {
@@ -872,6 +973,7 @@
{"mount_all", {1, kMax, do_mount_all}},
{"mount", {3, kMax, do_mount}},
{"umount", {1, 1, do_umount}},
+ {"readahead", {1, 2, do_readahead}},
{"restart", {1, 1, do_restart}},
{"restorecon", {1, kMax, do_restorecon}},
{"restorecon_recursive", {1, kMax, do_restorecon_recursive}},
@@ -894,3 +996,6 @@
// clang-format on
return builtin_functions;
}
+
+} // namespace init
+} // namespace android
diff --git a/init/builtins.h b/init/builtins.h
index 53f4a71..f66ae19 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -17,19 +17,27 @@
#ifndef _INIT_BUILTINS_H
#define _INIT_BUILTINS_H
+#include <functional>
#include <map>
#include <string>
#include <vector>
#include "keyword_map.h"
+#include "result.h"
-using BuiltinFunction = int (*) (const std::vector<std::string>& args);
+namespace android {
+namespace init {
+
+using BuiltinFunction = std::function<Result<Success>(const std::vector<std::string>&)>;
class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
-public:
- BuiltinFunctionMap() {
- }
-private:
- Map& map() const override;
+ public:
+ BuiltinFunctionMap() {}
+
+ private:
+ const Map& map() const override;
};
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index b8a9ec0..642a364 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -25,6 +25,9 @@
#define CAP_MAP_ENTRY(cap) { #cap, CAP_##cap }
+namespace android {
+namespace init {
+
static const std::map<std::string, int> cap_map = {
CAP_MAP_ENTRY(CHOWN),
CAP_MAP_ENTRY(DAC_OVERRIDE),
@@ -104,17 +107,15 @@
}
static bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) {
- cap_t caps = cap_init();
- auto deleter = [](cap_t* p) { cap_free(*p); };
- std::unique_ptr<cap_t, decltype(deleter)> ptr_caps(&caps, deleter);
+ ScopedCaps caps(cap_init());
- cap_clear(caps);
+ cap_clear(caps.get());
cap_value_t value[1];
for (size_t cap = 0; cap < to_keep.size(); ++cap) {
if (to_keep.test(cap)) {
value[0] = cap;
- if (cap_set_flag(caps, CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 ||
- cap_set_flag(caps, CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0) {
+ if (cap_set_flag(caps.get(), CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 ||
+ cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0) {
PLOG(ERROR) << "cap_set_flag(INHERITABLE|PERMITTED, " << cap << ") failed";
return false;
}
@@ -123,14 +124,14 @@
if (add_setpcap) {
value[0] = CAP_SETPCAP;
- if (cap_set_flag(caps, CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0 ||
- cap_set_flag(caps, CAP_EFFECTIVE, arraysize(value), value, CAP_SET) != 0) {
+ if (cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0 ||
+ cap_set_flag(caps.get(), CAP_EFFECTIVE, arraysize(value), value, CAP_SET) != 0) {
PLOG(ERROR) << "cap_set_flag(PERMITTED|EFFECTIVE, " << CAP_SETPCAP << ") failed";
return false;
}
}
- if (cap_set_proc(caps) != 0) {
+ if (cap_set_proc(caps.get()) != 0) {
PLOG(ERROR) << "cap_set_proc(" << to_keep.to_ulong() << ") failed";
return false;
}
@@ -192,3 +193,6 @@
// See http://man7.org/linux/man-pages/man7/capabilities.7.html.
return SetAmbientCaps(to_keep);
}
+
+} // namespace init
+} // namespace android
diff --git a/init/capabilities.h b/init/capabilities.h
index abd7fb2..ede85c3 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -15,16 +15,28 @@
#ifndef _INIT_CAPABILITIES_H
#define _INIT_CAPABILITIES_H
-#include <linux/capability.h>
+#include <sys/capability.h>
#include <bitset>
#include <string>
+#include <type_traits>
+
+namespace android {
+namespace init {
+
+struct CapDeleter {
+ void operator()(cap_t caps) const { cap_free(caps); }
+};
using CapSet = std::bitset<CAP_LAST_CAP + 1>;
+using ScopedCaps = std::unique_ptr<std::remove_pointer<cap_t>::type, CapDeleter>;
int LookupCap(const std::string& cap_name);
bool CapAmbientSupported();
unsigned int GetLastValidCap();
bool SetCapsForExec(const CapSet& to_keep);
+} // namespace init
+} // namespace android
+
#endif // _INIT_CAPABILITIES_H
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
index 6e457cd..6265687 100644
--- a/init/descriptors.cpp
+++ b/init/descriptors.cpp
@@ -19,18 +19,20 @@
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
-#include <sys/types.h>
#include <unistd.h>
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/android_get_control_file.h>
#include <cutils/sockets.h>
-#include "init.h"
-#include "log.h"
#include "util.h"
+namespace android {
+namespace init {
+
DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& context)
: name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) {
@@ -58,8 +60,8 @@
std::for_each(publishedName.begin(), publishedName.end(),
[] (char& c) { c = isalnum(c) ? c : '_'; });
- std::string val = android::base::StringPrintf("%d", fd);
- add_environment(publishedName.c_str(), val.c_str());
+ std::string val = std::to_string(fd);
+ setenv(publishedName.c_str(), val.c_str(), 1);
// make sure we don't close on exec
fcntl(fd, F_SETFD, 0);
@@ -74,14 +76,16 @@
}
void SocketInfo::Clean() const {
- unlink(android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s", name().c_str()).c_str());
+ std::string path = android::base::StringPrintf("%s/%s", ANDROID_SOCKET_DIR, name().c_str());
+ unlink(path.c_str());
}
int SocketInfo::Create(const std::string& context) const {
- int flags = ((type() == "stream" ? SOCK_STREAM :
- (type() == "dgram" ? SOCK_DGRAM :
- SOCK_SEQPACKET)));
- return create_socket(name().c_str(), flags, perm(), uid(), gid(), context.c_str());
+ auto types = android::base::Split(type(), "+");
+ int flags =
+ ((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
+ bool passcred = types.size() > 1 && types[1] == "passcred";
+ return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str());
}
const std::string SocketInfo::key() const {
@@ -123,3 +127,6 @@
const std::string FileInfo::key() const {
return ANDROID_FILE_ENV_PREFIX;
}
+
+} // namespace init
+} // namespace android
diff --git a/init/descriptors.h b/init/descriptors.h
index ff276fb..3bdddfe 100644
--- a/init/descriptors.h
+++ b/init/descriptors.h
@@ -22,6 +22,9 @@
#include <string>
+namespace android {
+namespace init {
+
class DescriptorInfo {
public:
DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
@@ -75,4 +78,7 @@
virtual const std::string key() const override;
};
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/devices.cpp b/init/devices.cpp
index 39571ac..af6b50a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2014 The Android Open Source Project
+ * Copyright (C) 2007 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,238 +14,227 @@
* limitations under the License.
*/
-#include <dirent.h>
+#include "devices.h"
+
#include <errno.h>
-#include <fcntl.h>
#include <fnmatch.h>
-#include <libgen.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/sendfile.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <sys/wait.h>
+#include <sys/sysmacros.h>
#include <unistd.h>
-#include <linux/netlink.h>
-
#include <memory>
-#include <thread>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-#include <selinux/android.h>
-#include <selinux/avc.h>
-
-#include <private/android_filesystem_config.h>
-
-#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <cutils/list.h>
-#include <cutils/uevent.h>
+#include <android-base/strings.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+#include <selinux/selinux.h>
-#include "devices.h"
-#include "ueventd_parser.h"
+#include "selinux.h"
+#include "ueventd.h"
#include "util.h"
-#include "log.h"
-#define SYSFS_PREFIX "/sys"
-static const char *firmware_dirs[] = { "/etc/firmware",
- "/vendor/firmware",
- "/firmware/image" };
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#endif
-extern struct selabel_handle *sehandle;
+using android::base::Basename;
+using android::base::Dirname;
+using android::base::Readlink;
+using android::base::Realpath;
+using android::base::StartsWith;
+using android::base::StringPrintf;
-static android::base::unique_fd device_fd;
+namespace android {
+namespace init {
-struct perms_ {
- char *name;
- char *attr;
- mode_t perm;
- unsigned int uid;
- unsigned int gid;
- unsigned short prefix;
- unsigned short wildcard;
-};
+/* Given a path that may start with a PCI device, populate the supplied buffer
+ * with the PCI domain/bus number and the peripheral ID and return 0.
+ * If it doesn't start with a PCI device, or there is some error, return -1 */
+static bool FindPciDevicePrefix(const std::string& path, std::string* result) {
+ result->clear();
-struct perm_node {
- struct perms_ dp;
- struct listnode plist;
-};
+ if (!StartsWith(path, "/devices/pci")) return false;
-struct platform_node {
- char *name;
- char *path;
- int path_len;
- struct listnode list;
-};
+ /* Beginning of the prefix is the initial "pci" after "/devices/" */
+ std::string::size_type start = 9;
-static list_declare(sys_perms);
-static list_declare(dev_perms);
-static list_declare(platform_names);
+ /* End of the prefix is two path '/' later, capturing the domain/bus number
+ * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
+ auto end = path.find('/', start);
+ if (end == std::string::npos) return false;
-int add_dev_perms(const char *name, const char *attr,
- mode_t perm, unsigned int uid, unsigned int gid,
- unsigned short prefix,
- unsigned short wildcard) {
- struct perm_node *node = (perm_node*) calloc(1, sizeof(*node));
- if (!node)
- return -ENOMEM;
+ end = path.find('/', end + 1);
+ if (end == std::string::npos) return false;
- node->dp.name = strdup(name);
- if (!node->dp.name) {
- free(node);
- return -ENOMEM;
+ auto length = end - start;
+ if (length <= 4) {
+ // The minimum string that will get to this check is 'pci/', which is malformed,
+ // so return false
+ return false;
}
- if (attr) {
- node->dp.attr = strdup(attr);
- if (!node->dp.attr) {
- free(node->dp.name);
- free(node);
- return -ENOMEM;
- }
- }
-
- node->dp.perm = perm;
- node->dp.uid = uid;
- node->dp.gid = gid;
- node->dp.prefix = prefix;
- node->dp.wildcard = wildcard;
-
- if (attr)
- list_add_tail(&sys_perms, &node->plist);
- else
- list_add_tail(&dev_perms, &node->plist);
-
- return 0;
+ *result = path.substr(start, length);
+ return true;
}
-static bool perm_path_matches(const char *path, struct perms_ *dp)
-{
- if (dp->prefix) {
- if (strncmp(path, dp->name, strlen(dp->name)) == 0)
+/* Given a path that may start with a virtual block device, populate
+ * the supplied buffer with the virtual block device ID and return 0.
+ * If it doesn't start with a virtual block device, or there is some
+ * error, return -1 */
+static bool FindVbdDevicePrefix(const std::string& path, std::string* result) {
+ result->clear();
+
+ if (!StartsWith(path, "/devices/vbd-")) return false;
+
+ /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
+ std::string::size_type start = 13;
+
+ /* End of the prefix is one path '/' later, capturing the
+ virtual block device ID. Example: 768 */
+ auto end = path.find('/', start);
+ if (end == std::string::npos) return false;
+
+ auto length = end - start;
+ if (length == 0) return false;
+
+ *result = path.substr(start, length);
+ return true;
+}
+
+Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
+ : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
+ // Set 'prefix_' or 'wildcard_' based on the below cases:
+ //
+ // 1) No '*' in 'name' -> Neither are set and Match() checks a given path for strict
+ // equality with 'name'
+ //
+ // 2) '*' only appears as the last character in 'name' -> 'prefix'_ is set to true and
+ // Match() checks if 'name' is a prefix of a given path.
+ //
+ // 3) '*' appears elsewhere -> 'wildcard_' is set to true and Match() uses fnmatch()
+ // with FNM_PATHNAME to compare 'name' to a given path.
+
+ auto wildcard_position = name_.find('*');
+ if (wildcard_position != std::string::npos) {
+ if (wildcard_position == name_.length() - 1) {
+ prefix_ = true;
+ name_.pop_back();
+ } else {
+ wildcard_ = true;
+ }
+ }
+}
+
+bool Permissions::Match(const std::string& path) const {
+ if (prefix_) return StartsWith(path, name_.c_str());
+ if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
+ return path == name_;
+}
+
+bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
+ const std::string& subsystem) const {
+ std::string path_basename = Basename(path);
+ if (name().find(subsystem) != std::string::npos) {
+ if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
+ if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
+ }
+ return Match(path);
+}
+
+void SysfsPermissions::SetPermissions(const std::string& path) const {
+ std::string attribute_file = path + "/" + attribute_;
+ LOG(VERBOSE) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
+ << perm();
+
+ if (access(attribute_file.c_str(), F_OK) == 0) {
+ if (chown(attribute_file.c_str(), uid(), gid()) != 0) {
+ PLOG(ERROR) << "chown(" << attribute_file << ", " << uid() << ", " << gid()
+ << ") failed";
+ }
+ if (chmod(attribute_file.c_str(), perm()) != 0) {
+ PLOG(ERROR) << "chmod(" << attribute_file << ", " << perm() << ") failed";
+ }
+ }
+}
+
+// Given a path that may start with a platform device, find the parent platform device by finding a
+// parent directory with a 'subsystem' symlink that points to the platform bus.
+// If it doesn't start with a platform device, return false
+bool DeviceHandler::FindPlatformDevice(std::string path, std::string* platform_device_path) const {
+ platform_device_path->clear();
+
+ // Uevents don't contain the mount point, so we need to add it here.
+ path.insert(0, sysfs_mount_point_);
+
+ std::string directory = Dirname(path);
+
+ while (directory != "/" && directory != ".") {
+ std::string subsystem_link_path;
+ if (Realpath(directory + "/subsystem", &subsystem_link_path) &&
+ subsystem_link_path == sysfs_mount_point_ + "/bus/platform") {
+ // We need to remove the mount point that we added above before returning.
+ directory.erase(0, sysfs_mount_point_.size());
+ *platform_device_path = directory;
return true;
- } else if (dp->wildcard) {
- if (fnmatch(dp->name, path, FNM_PATHNAME) == 0)
- return true;
- } else {
- if (strcmp(path, dp->name) == 0)
- return true;
+ }
+
+ auto last_slash = path.rfind('/');
+ if (last_slash == std::string::npos) return false;
+
+ path.erase(last_slash);
+ directory = Dirname(path);
}
return false;
}
-static bool match_subsystem(perms_* dp, const char* pattern,
- const char* path, const char* subsystem) {
- if (!pattern || !subsystem || strstr(dp->name, subsystem) == NULL) {
- return false;
- }
-
- std::string subsys_path = android::base::StringPrintf(pattern, subsystem, basename(path));
- return perm_path_matches(subsys_path.c_str(), dp);
-}
-
-static void fixup_sys_perms(const char* upath, const char* subsystem) {
+void DeviceHandler::FixupSysPermissions(const std::string& upath,
+ const std::string& subsystem) const {
// upaths omit the "/sys" that paths in this list
// contain, so we prepend it...
- std::string path = std::string(SYSFS_PREFIX) + upath;
+ std::string path = "/sys" + upath;
- listnode* node;
- list_for_each(node, &sys_perms) {
- perms_* dp = &(node_to_item(node, perm_node, plist))->dp;
- if (match_subsystem(dp, SYSFS_PREFIX "/class/%s/%s", path.c_str(), subsystem)) {
- ; // matched
- } else if (match_subsystem(dp, SYSFS_PREFIX "/bus/%s/devices/%s", path.c_str(), subsystem)) {
- ; // matched
- } else if (!perm_path_matches(path.c_str(), dp)) {
- continue;
- }
-
- std::string attr_file = path + "/" + dp->attr;
- LOG(INFO) << "fixup " << attr_file
- << " " << dp->uid << " " << dp->gid << " " << std::oct << dp->perm;
- chown(attr_file.c_str(), dp->uid, dp->gid);
- chmod(attr_file.c_str(), dp->perm);
+ for (const auto& s : sysfs_permissions_) {
+ if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
}
- if (access(path.c_str(), F_OK) == 0) {
+ if (!skip_restorecon_ && access(path.c_str(), F_OK) == 0) {
LOG(VERBOSE) << "restorecon_recursive: " << path;
- restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+ if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+ PLOG(ERROR) << "selinux_android_restorecon(" << path << ") failed";
+ }
}
}
-static mode_t get_device_perm(const char *path, const char **links,
- unsigned *uid, unsigned *gid)
-{
- struct listnode *node;
- struct perm_node *perm_node;
- struct perms_ *dp;
-
- /* search the perms list in reverse so that ueventd.$hardware can
- * override ueventd.rc
- */
- list_for_each_reverse(node, &dev_perms) {
- bool match = false;
-
- perm_node = node_to_item(node, struct perm_node, plist);
- dp = &perm_node->dp;
-
- if (perm_path_matches(path, dp)) {
- match = true;
- } else {
- if (links) {
- int i;
- for (i = 0; links[i]; i++) {
- if (perm_path_matches(links[i], dp)) {
- match = true;
- break;
- }
- }
- }
- }
-
- if (match) {
- *uid = dp->uid;
- *gid = dp->gid;
- return dp->perm;
+std::tuple<mode_t, uid_t, gid_t> DeviceHandler::GetDevicePermissions(
+ const std::string& path, const std::vector<std::string>& links) const {
+ // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
+ for (auto it = dev_permissions_.crbegin(); it != dev_permissions_.crend(); ++it) {
+ if (it->Match(path) || std::any_of(links.cbegin(), links.cend(),
+ [it](const auto& link) { return it->Match(link); })) {
+ return {it->perm(), it->uid(), it->gid()};
}
}
/* Default if nothing found. */
- *uid = 0;
- *gid = 0;
- return 0600;
+ return {0600, 0, 0};
}
-static void make_device(const char *path,
- const char */*upath*/,
- int block, int major, int minor,
- const char **links)
-{
- unsigned uid;
- unsigned gid;
- mode_t mode;
- dev_t dev;
- char *secontext = NULL;
+void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, int minor,
+ const std::vector<std::string>& links) const {
+ auto[mode, uid, gid] = GetDevicePermissions(path, links);
+ mode |= (block ? S_IFBLK : S_IFCHR);
- mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
-
- if (sehandle) {
- if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) {
- PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
- return;
- }
- setfscreatecon(secontext);
+ std::string secontext;
+ if (!SelabelLookupFileContextBestMatch(path, links, mode, &secontext)) {
+ PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
+ return;
+ }
+ if (!secontext.empty()) {
+ setfscreatecon(secontext.c_str());
}
- dev = makedev(major, minor);
+ dev_t dev = makedev(major, minor);
/* Temporarily change egid to avoid race condition setting the gid of the
* device node. Unforunately changing the euid would prevent creation of
* some device nodes, so the uid has to be set with chown() and is still
@@ -257,832 +246,185 @@
}
/* If the node already exists update its SELinux label to handle cases when
* it was created with the wrong context during coldboot procedure. */
- if (mknod(path, mode, dev) && (errno == EEXIST) && secontext) {
-
+ if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {
char* fcon = nullptr;
- int rc = lgetfilecon(path, &fcon);
+ int rc = lgetfilecon(path.c_str(), &fcon);
if (rc < 0) {
PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
goto out;
}
- bool different = strcmp(fcon, secontext) != 0;
+ bool different = fcon != secontext;
freecon(fcon);
- if (different && lsetfilecon(path, secontext)) {
- PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path << "' device";
+ if (different && lsetfilecon(path.c_str(), secontext.c_str())) {
+ PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
+ << "' device";
}
}
out:
- chown(path, uid, -1);
+ chown(path.c_str(), uid, -1);
if (setegid(AID_ROOT)) {
PLOG(FATAL) << "setegid(AID_ROOT) failed";
}
- if (secontext) {
- freecon(secontext);
- setfscreatecon(NULL);
+ if (!secontext.empty()) {
+ setfscreatecon(nullptr);
}
}
-static void add_platform_device(const char *path)
-{
- int path_len = strlen(path);
- struct platform_node *bus;
- const char *name = path;
+// replaces any unacceptable characters with '_', the
+// length of the resulting string is equal to the input string
+void SanitizePartitionName(std::string* string) {
+ const char* accept =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "_-.";
- if (!strncmp(path, "/devices/", 9)) {
- name += 9;
- if (!strncmp(name, "platform/", 9))
- name += 9;
- }
+ if (!string) return;
- LOG(VERBOSE) << "adding platform device " << name << " (" << path << ")";
-
- bus = (platform_node*) calloc(1, sizeof(struct platform_node));
- bus->path = strdup(path);
- bus->path_len = path_len;
- bus->name = bus->path + (name - path);
- list_add_tail(&platform_names, &bus->list);
-}
-
-/*
- * given a path that may start with a platform device, find the length of the
- * platform device prefix. If it doesn't start with a platform device, return
- * 0.
- */
-static struct platform_node *find_platform_device(const char *path)
-{
- int path_len = strlen(path);
- struct listnode *node;
- struct platform_node *bus;
-
- list_for_each_reverse(node, &platform_names) {
- bus = node_to_item(node, struct platform_node, list);
- if ((bus->path_len < path_len) &&
- (path[bus->path_len] == '/') &&
- !strncmp(path, bus->path, bus->path_len))
- return bus;
- }
-
- return NULL;
-}
-
-static void remove_platform_device(const char *path)
-{
- struct listnode *node;
- struct platform_node *bus;
-
- list_for_each_reverse(node, &platform_names) {
- bus = node_to_item(node, struct platform_node, list);
- if (!strcmp(path, bus->path)) {
- LOG(INFO) << "removing platform device " << bus->name;
- free(bus->path);
- list_remove(node);
- free(bus);
- return;
- }
+ std::string::size_type pos = 0;
+ while ((pos = string->find_first_not_of(accept, pos)) != std::string::npos) {
+ (*string)[pos] = '_';
}
}
-static void destroy_platform_devices() {
- struct listnode* node;
- struct listnode* n;
- struct platform_node* bus;
+std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
+ std::string device;
+ std::string type;
- list_for_each_safe(node, n, &platform_names) {
- list_remove(node);
- bus = node_to_item(node, struct platform_node, list);
- free(bus->path);
- free(bus);
- }
-}
+ if (FindPlatformDevice(uevent.path, &device)) {
+ // Skip /devices/platform or /devices/ if present
+ static const std::string devices_platform_prefix = "/devices/platform/";
+ static const std::string devices_prefix = "/devices/";
-/* Given a path that may start with a PCI device, populate the supplied buffer
- * with the PCI domain/bus number and the peripheral ID and return 0.
- * If it doesn't start with a PCI device, or there is some error, return -1 */
-static int find_pci_device_prefix(const char *path, char *buf, ssize_t buf_sz)
-{
- const char *start, *end;
-
- if (strncmp(path, "/devices/pci", 12))
- return -1;
-
- /* Beginning of the prefix is the initial "pci" after "/devices/" */
- start = path + 9;
-
- /* End of the prefix is two path '/' later, capturing the domain/bus number
- * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
- end = strchr(start, '/');
- if (!end)
- return -1;
- end = strchr(end + 1, '/');
- if (!end)
- return -1;
-
- /* Make sure we have enough room for the string plus null terminator */
- if (end - start + 1 > buf_sz)
- return -1;
-
- strncpy(buf, start, end - start);
- buf[end - start] = '\0';
- return 0;
-}
-
-/* Given a path that may start with a virtual block device, populate
- * the supplied buffer with the virtual block device ID and return 0.
- * If it doesn't start with a virtual block device, or there is some
- * error, return -1 */
-static int find_vbd_device_prefix(const char *path, char *buf, ssize_t buf_sz)
-{
- const char *start, *end;
-
- /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
- if (strncmp(path, "/devices/vbd-", 13))
- return -1;
-
- /* End of the prefix is one path '/' later, capturing the
- virtual block device ID. Example: 768 */
- start = path + 13;
- end = strchr(start, '/');
- if (!end)
- return -1;
-
- /* Make sure we have enough room for the string plus null terminator */
- if (end - start + 1 > buf_sz)
- return -1;
-
- strncpy(buf, start, end - start);
- buf[end - start] = '\0';
- return 0;
-}
-
-static void parse_event(const char *msg, struct uevent *uevent)
-{
- uevent->action = "";
- uevent->path = "";
- uevent->subsystem = "";
- uevent->firmware = "";
- uevent->major = -1;
- uevent->minor = -1;
- uevent->partition_name = NULL;
- uevent->partition_num = -1;
- uevent->device_name = NULL;
-
- /* currently ignoring SEQNUM */
- while(*msg) {
- if(!strncmp(msg, "ACTION=", 7)) {
- msg += 7;
- uevent->action = msg;
- } else if(!strncmp(msg, "DEVPATH=", 8)) {
- msg += 8;
- uevent->path = msg;
- } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
- msg += 10;
- uevent->subsystem = msg;
- } else if(!strncmp(msg, "FIRMWARE=", 9)) {
- msg += 9;
- uevent->firmware = msg;
- } else if(!strncmp(msg, "MAJOR=", 6)) {
- msg += 6;
- uevent->major = atoi(msg);
- } else if(!strncmp(msg, "MINOR=", 6)) {
- msg += 6;
- uevent->minor = atoi(msg);
- } else if(!strncmp(msg, "PARTN=", 6)) {
- msg += 6;
- uevent->partition_num = atoi(msg);
- } else if(!strncmp(msg, "PARTNAME=", 9)) {
- msg += 9;
- uevent->partition_name = msg;
- } else if(!strncmp(msg, "DEVNAME=", 8)) {
- msg += 8;
- uevent->device_name = msg;
+ if (StartsWith(device, devices_platform_prefix.c_str())) {
+ device = device.substr(devices_platform_prefix.length());
+ } else if (StartsWith(device, devices_prefix.c_str())) {
+ device = device.substr(devices_prefix.length());
}
- /* advance to after the next \0 */
- while(*msg++)
- ;
- }
-
- if (LOG_UEVENTS) {
- LOG(INFO) << android::base::StringPrintf("event { '%s', '%s', '%s', '%s', %d, %d }",
- uevent->action, uevent->path, uevent->subsystem,
- uevent->firmware, uevent->major, uevent->minor);
- }
-}
-
-static char **get_character_device_symlinks(struct uevent *uevent)
-{
- const char *parent;
- const char *slash;
- char **links;
- int link_num = 0;
- int width;
- struct platform_node *pdev;
-
- pdev = find_platform_device(uevent->path);
- if (!pdev)
- return NULL;
-
- links = (char**) malloc(sizeof(char *) * 2);
- if (!links)
- return NULL;
- memset(links, 0, sizeof(char *) * 2);
-
- /* skip "/devices/platform/<driver>" */
- parent = strchr(uevent->path + pdev->path_len, '/');
- if (!parent)
- goto err;
-
- if (!strncmp(parent, "/usb", 4)) {
- /* skip root hub name and device. use device interface */
- while (*++parent && *parent != '/');
- if (*parent)
- while (*++parent && *parent != '/');
- if (!*parent)
- goto err;
- slash = strchr(++parent, '/');
- if (!slash)
- goto err;
- width = slash - parent;
- if (width <= 0)
- goto err;
-
- if (asprintf(&links[link_num], "/dev/usb/%s%.*s", uevent->subsystem, width, parent) > 0)
- link_num++;
- else
- links[link_num] = NULL;
- mkdir("/dev/usb", 0755);
- }
- else {
- goto err;
- }
-
- return links;
-err:
- free(links);
- return NULL;
-}
-
-char** get_block_device_symlinks(struct uevent* uevent) {
- const char *device;
- struct platform_node *pdev;
- const char *slash;
- const char *type;
- char buf[256];
- char link_path[256];
- int link_num = 0;
- char *p;
-
- pdev = find_platform_device(uevent->path);
- if (pdev) {
- device = pdev->name;
type = "platform";
- } else if (!find_pci_device_prefix(uevent->path, buf, sizeof(buf))) {
- device = buf;
+ } else if (FindPciDevicePrefix(uevent.path, &device)) {
type = "pci";
- } else if (!find_vbd_device_prefix(uevent->path, buf, sizeof(buf))) {
- device = buf;
+ } else if (FindVbdDevicePrefix(uevent.path, &device)) {
type = "vbd";
} else {
- return NULL;
+ return {};
}
- char **links = (char**) malloc(sizeof(char *) * 4);
- if (!links)
- return NULL;
- memset(links, 0, sizeof(char *) * 4);
+ std::vector<std::string> links;
LOG(VERBOSE) << "found " << type << " device " << device;
- snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device);
+ auto link_path = "/dev/block/" + type + "/" + device;
- if (uevent->partition_name) {
- p = strdup(uevent->partition_name);
- sanitize(p);
- if (strcmp(uevent->partition_name, p)) {
- LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '" << p << "'";
+ if (!uevent.partition_name.empty()) {
+ std::string partition_name_sanitized(uevent.partition_name);
+ SanitizePartitionName(&partition_name_sanitized);
+ if (partition_name_sanitized != uevent.partition_name) {
+ LOG(VERBOSE) << "Linking partition '" << uevent.partition_name << "' as '"
+ << partition_name_sanitized << "'";
}
- if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
- link_num++;
- else
- links[link_num] = NULL;
- free(p);
+ links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
}
- if (uevent->partition_num >= 0) {
- if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0)
- link_num++;
- else
- links[link_num] = NULL;
+ if (uevent.partition_num >= 0) {
+ links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent.partition_num));
}
- slash = strrchr(uevent->path, '/');
- if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0)
- link_num++;
- else
- links[link_num] = NULL;
+ auto last_slash = uevent.path.rfind('/');
+ links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1));
return links;
}
-static void make_link_init(const char* oldpath, const char* newpath) {
- const char* slash = strrchr(newpath, '/');
- if (!slash) return;
+void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,
+ int major, int minor, const std::vector<std::string>& links) const {
+ if (action == "add") {
+ MakeDevice(devpath, block, major, minor, links);
+ for (const auto& link : links) {
+ if (!mkdir_recursive(Dirname(link), 0755)) {
+ PLOG(ERROR) << "Failed to create directory " << Dirname(link);
+ }
- if (mkdir_recursive(dirname(newpath), 0755)) {
- PLOG(ERROR) << "Failed to create directory " << dirname(newpath);
- }
-
- if (symlink(oldpath, newpath) && errno != EEXIST) {
- PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
- }
-}
-
-static void remove_link(const char* oldpath, const char* newpath) {
- std::string path;
- if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath);
-}
-
-static void handle_device(const char *action, const char *devpath,
- const char *path, int block, int major, int minor, char **links)
-{
- if(!strcmp(action, "add")) {
- make_device(devpath, path, block, major, minor, (const char **)links);
- if (links) {
- for (int i = 0; links[i]; i++) {
- make_link_init(devpath, links[i]);
+ if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) {
+ PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
}
}
}
- if(!strcmp(action, "remove")) {
- if (links) {
- for (int i = 0; links[i]; i++) {
- remove_link(devpath, links[i]);
+ if (action == "remove") {
+ for (const auto& link : links) {
+ std::string link_path;
+ if (Readlink(link, &link_path) && link_path == devpath) {
+ unlink(link.c_str());
}
}
- unlink(devpath);
+ unlink(devpath.c_str());
+ }
+}
+
+void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
+ if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
+ FixupSysPermissions(uevent.path, uevent.subsystem);
}
- if (links) {
- for (int i = 0; links[i]; i++) {
- free(links[i]);
+ // if it's not a /dev device, nothing to do
+ if (uevent.major < 0 || uevent.minor < 0) return;
+
+ std::string devpath;
+ std::vector<std::string> links;
+ bool block = false;
+
+ if (uevent.subsystem == "block") {
+ block = true;
+ devpath = "/dev/block/" + Basename(uevent.path);
+
+ if (StartsWith(uevent.path, "/devices")) {
+ links = GetBlockDeviceSymlinks(uevent);
}
- free(links);
- }
-}
-
-static void handle_platform_device_event(struct uevent *uevent)
-{
- const char *path = uevent->path;
-
- if (!strcmp(uevent->action, "add"))
- add_platform_device(path);
- else if (!strcmp(uevent->action, "remove"))
- remove_platform_device(path);
-}
-
-static const char *parse_device_name(struct uevent *uevent, unsigned int len)
-{
- const char *name;
-
- /* if it's not a /dev device, nothing else to do */
- if((uevent->major < 0) || (uevent->minor < 0))
- return NULL;
-
- /* do we have a name? */
- name = strrchr(uevent->path, '/');
- if(!name)
- return NULL;
- name++;
-
- /* too-long names would overrun our buffer */
- if(strlen(name) > len) {
- LOG(ERROR) << "DEVPATH=" << name << " exceeds " << len << "-character limit on filename; ignoring event";
- return NULL;
- }
-
- return name;
-}
-
-#define DEVPATH_LEN 96
-#define MAX_DEV_NAME 64
-
-static void handle_block_device_event(struct uevent *uevent)
-{
- const char *base = "/dev/block/";
- const char *name;
- char devpath[DEVPATH_LEN];
- char **links = NULL;
-
- name = parse_device_name(uevent, MAX_DEV_NAME);
- if (!name)
- return;
-
- snprintf(devpath, sizeof(devpath), "%s%s", base, name);
- make_dir(base, 0755);
-
- if (!strncmp(uevent->path, "/devices/", 9))
- links = get_block_device_symlinks(uevent);
-
- handle_device(uevent->action, devpath, uevent->path, 1,
- uevent->major, uevent->minor, links);
-}
-
-static bool assemble_devpath(char *devpath, const char *dirname,
- const char *devname)
-{
- int s = snprintf(devpath, DEVPATH_LEN, "%s/%s", dirname, devname);
- if (s < 0) {
- PLOG(ERROR) << "failed to assemble device path; ignoring event";
- return false;
- } else if (s >= DEVPATH_LEN) {
- LOG(ERROR) << dirname << "/" << devname
- << " exceeds " << DEVPATH_LEN << "-character limit on path; ignoring event";
- return false;
- }
- return true;
-}
-
-static void mkdir_recursive_for_devpath(const char *devpath)
-{
- char dir[DEVPATH_LEN];
- char *slash;
-
- strcpy(dir, devpath);
- slash = strrchr(dir, '/');
- *slash = '\0';
- mkdir_recursive(dir, 0755);
-}
-
-static void handle_generic_device_event(struct uevent *uevent)
-{
- const char *base;
- const char *name;
- char devpath[DEVPATH_LEN] = {0};
- char **links = NULL;
-
- name = parse_device_name(uevent, MAX_DEV_NAME);
- if (!name)
- return;
-
- struct ueventd_subsystem *subsystem =
- ueventd_subsystem_find_by_name(uevent->subsystem);
-
- if (subsystem) {
- const char *devname;
-
- switch (subsystem->devname_src) {
- case DEVNAME_UEVENT_DEVNAME:
- devname = uevent->device_name;
- break;
-
- case DEVNAME_UEVENT_DEVPATH:
- devname = name;
- break;
-
- default:
- LOG(ERROR) << uevent->subsystem << " subsystem's devpath option is not set; ignoring event";
- return;
+ } else if (const auto subsystem =
+ std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
+ subsystem != subsystems_.cend()) {
+ devpath = subsystem->ParseDevPath(uevent);
+ } else if (uevent.subsystem == "usb") {
+ if (!uevent.device_name.empty()) {
+ devpath = "/dev/" + uevent.device_name;
+ } else {
+ // This imitates the file system that would be created
+ // if we were using devfs instead.
+ // Minors are broken up into groups of 128, starting at "001"
+ int bus_id = uevent.minor / 128 + 1;
+ int device_id = uevent.minor % 128 + 1;
+ devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
}
-
- if (!assemble_devpath(devpath, subsystem->dirname, devname))
- return;
- mkdir_recursive_for_devpath(devpath);
- } else if (!strncmp(uevent->subsystem, "usb", 3)) {
- if (!strcmp(uevent->subsystem, "usb")) {
- if (uevent->device_name) {
- if (!assemble_devpath(devpath, "/dev", uevent->device_name))
- return;
- mkdir_recursive_for_devpath(devpath);
- }
- else {
- /* This imitates the file system that would be created
- * if we were using devfs instead.
- * Minors are broken up into groups of 128, starting at "001"
- */
- int bus_id = uevent->minor / 128 + 1;
- int device_id = uevent->minor % 128 + 1;
- /* build directories */
- make_dir("/dev/bus", 0755);
- make_dir("/dev/bus/usb", 0755);
- snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
- make_dir(devpath, 0755);
- snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
- }
- } else {
- /* ignore other USB events */
- return;
- }
- } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
- base = "/dev/graphics/";
- make_dir(base, 0755);
- } else if (!strncmp(uevent->subsystem, "drm", 3)) {
- base = "/dev/dri/";
- make_dir(base, 0755);
- } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
- base = "/dev/oncrpc/";
- make_dir(base, 0755);
- } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
- base = "/dev/adsp/";
- make_dir(base, 0755);
- } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
- base = "/dev/msm_camera/";
- make_dir(base, 0755);
- } else if(!strncmp(uevent->subsystem, "input", 5)) {
- base = "/dev/input/";
- make_dir(base, 0755);
- } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
- base = "/dev/mtd/";
- make_dir(base, 0755);
- } else if(!strncmp(uevent->subsystem, "sound", 5)) {
- base = "/dev/snd/";
- make_dir(base, 0755);
- } else if(!strncmp(uevent->subsystem, "misc", 4) && !strncmp(name, "log_", 4)) {
- LOG(INFO) << "kernel logger is deprecated";
- base = "/dev/log/";
- make_dir(base, 0755);
- name += 4;
- } else
- base = "/dev/";
- links = get_character_device_symlinks(uevent);
-
- if (!devpath[0])
- snprintf(devpath, sizeof(devpath), "%s%s", base, name);
-
- handle_device(uevent->action, devpath, uevent->path, 0,
- uevent->major, uevent->minor, links);
-}
-
-static void handle_device_event(struct uevent *uevent)
-{
- if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online"))
- fixup_sys_perms(uevent->path, uevent->subsystem);
-
- if (!strncmp(uevent->subsystem, "block", 5)) {
- handle_block_device_event(uevent);
- } else if (!strncmp(uevent->subsystem, "platform", 8)) {
- handle_platform_device_event(uevent);
+ } else if (StartsWith(uevent.subsystem, "usb")) {
+ // ignore other USB events
+ return;
} else {
- handle_generic_device_event(uevent);
+ devpath = "/dev/" + Basename(uevent.path);
}
+
+ mkdir_recursive(Dirname(devpath), 0755);
+
+ HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
}
-static void load_firmware(uevent* uevent, const std::string& root,
- int fw_fd, size_t fw_size,
- int loading_fd, int data_fd) {
- // Start transfer.
- android::base::WriteFully(loading_fd, "1", 1);
+DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
+ std::vector<SysfsPermissions> sysfs_permissions,
+ std::vector<Subsystem> subsystems, bool skip_restorecon)
+ : dev_permissions_(std::move(dev_permissions)),
+ sysfs_permissions_(std::move(sysfs_permissions)),
+ subsystems_(std::move(subsystems)),
+ skip_restorecon_(skip_restorecon),
+ sysfs_mount_point_("/sys") {}
- // Copy the firmware.
- int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
- if (rc == -1) {
- PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent->firmware << "' }";
- }
+DeviceHandler::DeviceHandler()
+ : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
+ std::vector<Subsystem>{}, false) {}
- // Tell the firmware whether to abort or commit.
- const char* response = (rc != -1) ? "0" : "-1";
- android::base::WriteFully(loading_fd, response, strlen(response));
-}
-
-static int is_booting() {
- return access("/dev/.booting", F_OK) == 0;
-}
-
-static void process_firmware_event(uevent* uevent) {
- int booting = is_booting();
-
- LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
-
- std::string root = android::base::StringPrintf("/sys%s", uevent->path);
- std::string loading = root + "/loading";
- std::string data = root + "/data";
-
- android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY|O_CLOEXEC));
- if (loading_fd == -1) {
- PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent->firmware;
- return;
- }
-
- android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY|O_CLOEXEC));
- if (data_fd == -1) {
- PLOG(ERROR) << "couldn't open firmware data fd for " << uevent->firmware;
- return;
- }
-
-try_loading_again:
- for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
- std::string file = android::base::StringPrintf("%s/%s", firmware_dirs[i], uevent->firmware);
- android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY|O_CLOEXEC));
- struct stat sb;
- if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
- load_firmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
- return;
- }
- }
-
- if (booting) {
- // If we're not fully booted, we may be missing
- // filesystems needed for firmware, wait and retry.
- std::this_thread::sleep_for(100ms);
- booting = is_booting();
- goto try_loading_again;
- }
-
- LOG(ERROR) << "firmware: could not find firmware for " << uevent->firmware;
-
- // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
- write(loading_fd, "-1", 2);
-}
-
-static void handle_firmware_event(uevent* uevent) {
- if (strcmp(uevent->subsystem, "firmware")) return;
- if (strcmp(uevent->action, "add")) return;
-
- // Loading the firmware in a child means we can do that in parallel...
- // (We ignore SIGCHLD rather than wait for our children.)
- pid_t pid = fork();
- if (pid == 0) {
- Timer t;
- process_firmware_event(uevent);
- LOG(INFO) << "loading " << uevent->path << " took " << t;
- _exit(EXIT_SUCCESS);
- } else if (pid == -1) {
- PLOG(ERROR) << "could not fork to process firmware event for " << uevent->firmware;
- }
-}
-
-static bool inline should_stop_coldboot(coldboot_action_t act)
-{
- return (act == COLDBOOT_STOP || act == COLDBOOT_FINISH);
-}
-
-#define UEVENT_MSG_LEN 2048
-
-static inline coldboot_action_t handle_device_fd_with(
- std::function<coldboot_action_t(uevent* uevent)> handle_uevent)
-{
- char msg[UEVENT_MSG_LEN+2];
- int n;
- while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
- if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
- continue;
-
- msg[n] = '\0';
- msg[n+1] = '\0';
-
- struct uevent uevent;
- parse_event(msg, &uevent);
- coldboot_action_t act = handle_uevent(&uevent);
- if (should_stop_coldboot(act))
- return act;
- }
-
- return COLDBOOT_CONTINUE;
-}
-
-coldboot_action_t handle_device_fd(coldboot_callback fn)
-{
- coldboot_action_t ret = handle_device_fd_with(
- [&](uevent* uevent) -> coldboot_action_t {
- if (selinux_status_updated() > 0) {
- struct selabel_handle *sehandle2;
- sehandle2 = selinux_android_file_context_handle();
- if (sehandle2) {
- selabel_close(sehandle);
- sehandle = sehandle2;
- }
- }
-
- // default is to always create the devices
- coldboot_action_t act = COLDBOOT_CREATE;
- if (fn) {
- act = fn(uevent);
- }
-
- if (act == COLDBOOT_CREATE || act == COLDBOOT_STOP) {
- handle_device_event(uevent);
- handle_firmware_event(uevent);
- }
-
- return act;
- });
-
- return ret;
-}
-
-/* Coldboot walks parts of the /sys tree and pokes the uevent files
-** to cause the kernel to regenerate device add events that happened
-** before init's device manager was started
-**
-** We drain any pending events from the netlink socket every time
-** we poke another uevent file to make sure we don't overrun the
-** socket's buffer.
-*/
-
-static coldboot_action_t do_coldboot(DIR *d, coldboot_callback fn)
-{
- struct dirent *de;
- int dfd, fd;
- coldboot_action_t act = COLDBOOT_CONTINUE;
-
- dfd = dirfd(d);
-
- fd = openat(dfd, "uevent", O_WRONLY);
- if (fd >= 0) {
- write(fd, "add\n", 4);
- close(fd);
- act = handle_device_fd(fn);
- if (should_stop_coldboot(act))
- return act;
- }
-
- while (!should_stop_coldboot(act) && (de = readdir(d))) {
- DIR *d2;
-
- if(de->d_type != DT_DIR || de->d_name[0] == '.')
- continue;
-
- fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
- if(fd < 0)
- continue;
-
- d2 = fdopendir(fd);
- if(d2 == 0)
- close(fd);
- else {
- act = do_coldboot(d2, fn);
- closedir(d2);
- }
- }
-
- // default is always to continue looking for uevents
- return act;
-}
-
-static coldboot_action_t coldboot(const char *path, coldboot_callback fn)
-{
- std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
- if (d) {
- return do_coldboot(d.get(), fn);
- }
-
- return COLDBOOT_CONTINUE;
-}
-
-void device_init(const char* path, coldboot_callback fn) {
- if (!sehandle) {
- sehandle = selinux_android_file_context_handle();
- }
- // open uevent socket and selinux status only if it hasn't been
- // done before
- if (device_fd == -1) {
- /* is 256K enough? udev uses 16MB! */
- device_fd.reset(uevent_open_socket(256 * 1024, true));
- if (device_fd == -1) {
- return;
- }
- fcntl(device_fd, F_SETFL, O_NONBLOCK);
- selinux_status_open(true);
- }
-
- if (access(COLDBOOT_DONE, F_OK) == 0) {
- LOG(VERBOSE) << "Skipping coldboot, already done!";
- return;
- }
-
- Timer t;
- coldboot_action_t act;
- if (!path) {
- act = coldboot("/sys/class", fn);
- if (!should_stop_coldboot(act)) {
- act = coldboot("/sys/block", fn);
- if (!should_stop_coldboot(act)) {
- act = coldboot("/sys/devices", fn);
- }
- }
- } else {
- act = coldboot(path, fn);
- }
-
- // If we have a callback, then do as it says. If no, then the default is
- // to always create COLDBOOT_DONE file.
- if (!fn || (act == COLDBOOT_FINISH)) {
- close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
- }
-
- LOG(INFO) << "Coldboot took " << t;
-}
-
-void device_close() {
- destroy_platform_devices();
- device_fd.reset();
- selinux_status_close();
-}
-
-int get_device_fd() {
- return device_fd;
-}
+} // namespace init
+} // namespace android
diff --git a/init/devices.h b/init/devices.h
index 3f2cde4..1f8f1e8 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,44 +17,122 @@
#ifndef _INIT_DEVICES_H
#define _INIT_DEVICES_H
-#include <functional>
#include <sys/stat.h>
+#include <sys/types.h>
-enum coldboot_action_t {
- // coldboot continues without creating the device for the uevent
- COLDBOOT_CONTINUE = 0,
- // coldboot continues after creating the device for the uevent
- COLDBOOT_CREATE,
- // coldboot stops after creating the device for uevent but doesn't
- // create the COLDBOOT_DONE file
- COLDBOOT_STOP,
- // same as COLDBOOT_STOP, but creates the COLDBOOT_DONE file
- COLDBOOT_FINISH
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <selinux/label.h>
+
+#include "uevent.h"
+
+namespace android {
+namespace init {
+
+class Permissions {
+ public:
+ Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid);
+
+ bool Match(const std::string& path) const;
+
+ mode_t perm() const { return perm_; }
+ uid_t uid() const { return uid_; }
+ gid_t gid() const { return gid_; }
+
+ protected:
+ const std::string& name() const { return name_; }
+
+ private:
+ std::string name_;
+ mode_t perm_;
+ uid_t uid_;
+ gid_t gid_;
+ bool prefix_;
+ bool wildcard_;
};
-struct uevent {
- const char* action;
- const char* path;
- const char* subsystem;
- const char* firmware;
- const char* partition_name;
- const char* device_name;
- int partition_num;
- int major;
- int minor;
+class SysfsPermissions : public Permissions {
+ public:
+ SysfsPermissions(const std::string& name, const std::string& attribute, mode_t perm, uid_t uid,
+ gid_t gid)
+ : Permissions(name, perm, uid, gid), attribute_(attribute) {}
+
+ bool MatchWithSubsystem(const std::string& path, const std::string& subsystem) const;
+ void SetPermissions(const std::string& path) const;
+
+ private:
+ const std::string attribute_;
};
-typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback;
-extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr);
-extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr);
-extern void device_close();
+class Subsystem {
+ public:
+ friend class SubsystemParser;
-extern int add_dev_perms(const char *name, const char *attr,
- mode_t perm, unsigned int uid,
- unsigned int gid, unsigned short prefix,
- unsigned short wildcard);
-int get_device_fd();
+ Subsystem() {}
+ Subsystem(std::string name) : name_(std::move(name)) {}
-char** get_block_device_symlinks(struct uevent* uevent);
+ // Returns the full path for a uevent of a device that is a member of this subsystem,
+ // according to the rules parsed from ueventd.rc
+ std::string ParseDevPath(const Uevent& uevent) const {
+ std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
+ ? uevent.device_name
+ : android::base::Basename(uevent.path);
-#endif /* _INIT_DEVICES_H */
+ return dir_name_ + "/" + devname;
+ }
+
+ bool operator==(const std::string& string_name) const { return name_ == string_name; }
+
+ private:
+ enum class DevnameSource {
+ DEVNAME_UEVENT_DEVNAME,
+ DEVNAME_UEVENT_DEVPATH,
+ };
+
+ std::string name_;
+ std::string dir_name_ = "/dev";
+ DevnameSource devname_source_;
+};
+
+class DeviceHandler {
+ public:
+ friend class DeviceHandlerTester;
+
+ DeviceHandler();
+ DeviceHandler(std::vector<Permissions> dev_permissions,
+ std::vector<SysfsPermissions> sysfs_permissions,
+ std::vector<Subsystem> subsystems, bool skip_restorecon);
+ ~DeviceHandler(){};
+
+ void HandleDeviceEvent(const Uevent& uevent);
+
+ std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
+ void set_skip_restorecon(bool value) { skip_restorecon_ = value; }
+
+ private:
+ bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
+ std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
+ const std::string& path, const std::vector<std::string>& links) const;
+ void MakeDevice(const std::string& path, bool block, int major, int minor,
+ const std::vector<std::string>& links) const;
+ void HandleDevice(const std::string& action, const std::string& devpath, bool block, int major,
+ int minor, const std::vector<std::string>& links) const;
+ void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
+
+ std::vector<Permissions> dev_permissions_;
+ std::vector<SysfsPermissions> sysfs_permissions_;
+ std::vector<Subsystem> subsystems_;
+ bool skip_restorecon_;
+ std::string sysfs_mount_point_;
+};
+
+// Exposed for testing
+void SanitizePartitionName(std::string* string);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
new file mode 100644
index 0000000..eba00cb
--- /dev/null
+++ b/init/devices_test.cpp
@@ -0,0 +1,315 @@
+/*
+ * 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 "devices.h"
+
+#include <android-base/scopeguard.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "util.h"
+
+using namespace std::string_literals;
+
+namespace android {
+namespace init {
+
+class DeviceHandlerTester {
+ public:
+ void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent,
+ const std::vector<std::string> expected_links) {
+ TemporaryDir fake_sys_root;
+ device_handler_.sysfs_mount_point_ = fake_sys_root.path;
+
+ std::string platform_device_dir = fake_sys_root.path + platform_device;
+ mkdir_recursive(platform_device_dir, 0777);
+
+ std::string platform_bus = fake_sys_root.path + "/bus/platform"s;
+ mkdir_recursive(platform_bus, 0777);
+ symlink(platform_bus.c_str(), (platform_device_dir + "/subsystem").c_str());
+
+ mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777);
+
+ std::vector<std::string> result;
+ result = device_handler_.GetBlockDeviceSymlinks(uevent);
+
+ auto expected_size = expected_links.size();
+ ASSERT_EQ(expected_size, result.size());
+ if (expected_size == 0) return;
+
+ // Explicitly iterate so the results are visible if a failure occurs
+ for (unsigned int i = 0; i < expected_size; ++i) {
+ EXPECT_EQ(expected_links[i], result[i]);
+ }
+ }
+
+ private:
+ DeviceHandler device_handler_;
+};
+
+TEST(device_handler, get_block_device_symlinks_success_platform) {
+ // These are actual paths from bullhead
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ Uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0",
+ .partition_name = "",
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition) {
+ // These are actual paths from bullhead
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ Uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = "modem",
+ .partition_num = 1,
+ };
+ std::vector<std::string> expected_result{
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+ "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+ };
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_num) {
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ Uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = "",
+ .partition_num = 1,
+ };
+ std::vector<std::string> expected_result{
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+ "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+ };
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_name) {
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ Uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = "modem",
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+ "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+ };
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_pci) {
+ const char* platform_device = "/devices/do/not/match";
+ Uevent uevent = {
+ .path = "/devices/pci0000:00/0000:00:1f.2/mmcblk0", .partition_name = "", .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
+}
+
+TEST(device_handler, get_block_device_symlinks_pci_bad_format) {
+ const char* platform_device = "/devices/do/not/match";
+ Uevent uevent = {
+ .path = "/devices/pci//mmcblk0", .partition_name = "", .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{};
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
+}
+
+TEST(device_handler, get_block_device_symlinks_success_vbd) {
+ const char* platform_device = "/devices/do/not/match";
+ Uevent uevent = {
+ .path = "/devices/vbd-1234/mmcblk0", .partition_name = "", .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
+}
+
+TEST(device_handler, get_block_device_symlinks_vbd_bad_format) {
+ const char* platform_device = "/devices/do/not/match";
+ Uevent uevent = {
+ .path = "/devices/vbd-/mmcblk0", .partition_name = "", .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{};
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
+}
+
+TEST(device_handler, get_block_device_symlinks_no_matches) {
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ Uevent uevent = {
+ .path = "/devices/soc.0/not_the_device/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = "",
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result;
+
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
+}
+
+TEST(device_handler, sanitize_null) {
+ SanitizePartitionName(nullptr);
+}
+
+TEST(device_handler, sanitize_empty) {
+ std::string empty;
+ SanitizePartitionName(&empty);
+ EXPECT_EQ(0u, empty.size());
+}
+
+TEST(device_handler, sanitize_allgood) {
+ std::string good =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "_-.";
+ std::string good_copy = good;
+ SanitizePartitionName(&good);
+ EXPECT_EQ(good_copy, good);
+}
+
+TEST(device_handler, sanitize_somebad) {
+ std::string string = "abc!@#$%^&*()";
+ SanitizePartitionName(&string);
+ EXPECT_EQ("abc__________", string);
+}
+
+TEST(device_handler, sanitize_allbad) {
+ std::string string = "!@#$%^&*()";
+ SanitizePartitionName(&string);
+ EXPECT_EQ("__________", string);
+}
+
+TEST(device_handler, sanitize_onebad) {
+ std::string string = ")";
+ SanitizePartitionName(&string);
+ EXPECT_EQ("_", string);
+}
+
+TEST(device_handler, DevPermissionsMatchNormal) {
+ // Basic from ueventd.rc
+ // /dev/null 0666 root root
+ Permissions permissions("/dev/null", 0666, 0, 0);
+ EXPECT_TRUE(permissions.Match("/dev/null"));
+ EXPECT_FALSE(permissions.Match("/dev/nullsuffix"));
+ EXPECT_FALSE(permissions.Match("/dev/nul"));
+ EXPECT_EQ(0666U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(0U, permissions.gid());
+}
+
+TEST(device_handler, DevPermissionsMatchPrefix) {
+ // Prefix from ueventd.rc
+ // /dev/dri/* 0666 root graphics
+ Permissions permissions("/dev/dri/*", 0666, 0, 1000);
+ EXPECT_TRUE(permissions.Match("/dev/dri/some_dri_device"));
+ EXPECT_TRUE(permissions.Match("/dev/dri/some_other_dri_device"));
+ EXPECT_TRUE(permissions.Match("/dev/dri/"));
+ EXPECT_FALSE(permissions.Match("/dev/dr/non_match"));
+ EXPECT_EQ(0666U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(device_handler, DevPermissionsMatchWildcard) {
+ // Wildcard example
+ // /dev/device*name 0666 root graphics
+ Permissions permissions("/dev/device*name", 0666, 0, 1000);
+ EXPECT_TRUE(permissions.Match("/dev/devicename"));
+ EXPECT_TRUE(permissions.Match("/dev/device123name"));
+ EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+ EXPECT_FALSE(permissions.Match("/dev/device123name/subdevice"));
+ EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+ EXPECT_EQ(0666U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(device_handler, DevPermissionsMatchWildcardPrefix) {
+ // Wildcard+Prefix example
+ // /dev/device*name* 0666 root graphics
+ Permissions permissions("/dev/device*name*", 0666, 0, 1000);
+ EXPECT_TRUE(permissions.Match("/dev/devicename"));
+ EXPECT_TRUE(permissions.Match("/dev/device123name"));
+ EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+ EXPECT_TRUE(permissions.Match("/dev/device123namesomething"));
+ // FNM_PATHNAME doesn't match '/' with *
+ EXPECT_FALSE(permissions.Match("/dev/device123name/something"));
+ EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+ EXPECT_EQ(0666U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemNormal) {
+ // /sys/devices/virtual/input/input* enable 0660 root input
+ SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001);
+ EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/input0", "input"));
+ EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/not_input0", "input"));
+ EXPECT_EQ(0660U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(1001U, permissions.gid());
+}
+
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemClass) {
+ // /sys/class/input/event* enable 0660 root input
+ SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001);
+ EXPECT_TRUE(permissions.MatchWithSubsystem(
+ "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "input"));
+ EXPECT_FALSE(permissions.MatchWithSubsystem(
+ "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/not_event0", "input"));
+ EXPECT_FALSE(permissions.MatchWithSubsystem(
+ "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "not_input"));
+ EXPECT_EQ(0660U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(1001U, permissions.gid());
+}
+
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemBus) {
+ // /sys/bus/i2c/devices/i2c-* enable 0660 root input
+ SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001);
+ EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "i2c"));
+ EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/not-i2c", "i2c"));
+ EXPECT_FALSE(
+ permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "not_i2c"));
+ EXPECT_EQ(0660U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(1001U, permissions.gid());
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
new file mode 100644
index 0000000..b686885
--- /dev/null
+++ b/init/firmware_handler.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 "firmware_handler.h"
+
+#include <fcntl.h>
+#include <sys/sendfile.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+#include <thread>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+using android::base::Timer;
+using android::base::unique_fd;
+using android::base::WriteFully;
+
+namespace android {
+namespace init {
+
+static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
+ int loading_fd, int data_fd) {
+ // Start transfer.
+ WriteFully(loading_fd, "1", 1);
+
+ // Copy the firmware.
+ int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
+ if (rc == -1) {
+ PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent.firmware
+ << "' }";
+ }
+
+ // Tell the firmware whether to abort or commit.
+ const char* response = (rc != -1) ? "0" : "-1";
+ WriteFully(loading_fd, response, strlen(response));
+}
+
+static bool IsBooting() {
+ return access("/dev/.booting", F_OK) == 0;
+}
+
+static void ProcessFirmwareEvent(const Uevent& uevent) {
+ int booting = IsBooting();
+
+ LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
+
+ std::string root = "/sys" + uevent.path;
+ std::string loading = root + "/loading";
+ std::string data = root + "/data";
+
+ unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
+ if (loading_fd == -1) {
+ PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware;
+ return;
+ }
+
+ unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
+ if (data_fd == -1) {
+ PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware;
+ return;
+ }
+
+ static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
+ "/firmware/image/"};
+
+try_loading_again:
+ for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
+ std::string file = firmware_dirs[i] + uevent.firmware;
+ unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
+ struct stat sb;
+ if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
+ LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+ return;
+ }
+ }
+
+ if (booting) {
+ // If we're not fully booted, we may be missing
+ // filesystems needed for firmware, wait and retry.
+ std::this_thread::sleep_for(100ms);
+ booting = IsBooting();
+ goto try_loading_again;
+ }
+
+ LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
+
+ // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
+ write(loading_fd, "-1", 2);
+}
+
+void HandleFirmwareEvent(const Uevent& uevent) {
+ if (uevent.subsystem != "firmware" || uevent.action != "add") return;
+
+ // Loading the firmware in a child means we can do that in parallel...
+ auto pid = fork();
+ if (pid == -1) {
+ PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
+ }
+ if (pid == 0) {
+ Timer t;
+ ProcessFirmwareEvent(uevent);
+ LOG(INFO) << "loading " << uevent.path << " took " << t;
+ _exit(EXIT_SUCCESS);
+ }
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/signal_handler.h b/init/firmware_handler.h
similarity index 67%
copy from init/signal_handler.h
copy to init/firmware_handler.h
index 449b4af..e456ac4 100644
--- a/init/signal_handler.h
+++ b/init/firmware_handler.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2007 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,9 +14,17 @@
* limitations under the License.
*/
-#ifndef _INIT_SIGNAL_HANDLER_H_
-#define _INIT_SIGNAL_HANDLER_H_
+#ifndef _INIT_FIRMWARE_HANDLER_H
+#define _INIT_FIRMWARE_HANDLER_H
-void signal_handler_init(void);
+#include "uevent.h"
+
+namespace android {
+namespace init {
+
+void HandleFirmwareEvent(const Uevent& uevent);
+
+} // namespace init
+} // namespace android
#endif
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index d52247b..e335fd1 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -16,39 +16,41 @@
#include "import_parser.h"
-#include "errno.h"
+#include <android-base/logging.h>
-#include <string>
-#include <vector>
-
-#include "log.h"
#include "util.h"
-bool ImportParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+namespace android {
+namespace init {
+
+Result<Success> ImportParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
if (args.size() != 2) {
- *err = "single argument needed for import\n";
- return false;
+ return Error() << "single argument needed for import\n";
}
std::string conf_file;
bool ret = expand_props(args[1], &conf_file);
if (!ret) {
- *err = "error while expanding import";
- return false;
+ return Error() << "error while expanding import";
}
LOG(INFO) << "Added '" << conf_file << "' to import list";
- imports_.emplace_back(std::move(conf_file));
- return true;
+ if (filename_.empty()) filename_ = filename;
+ imports_.emplace_back(std::move(conf_file), line);
+ return Success();
}
-void ImportParser::EndFile(const std::string& filename) {
+void ImportParser::EndFile() {
auto current_imports = std::move(imports_);
imports_.clear();
- for (const auto& s : current_imports) {
- if (!Parser::GetInstance().ParseConfig(s)) {
- PLOG(ERROR) << "could not import file '" << s << "' from '" << filename << "'";
+ for (const auto& [import, line_num] : current_imports) {
+ if (!parser_->ParseConfig(import)) {
+ PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
+ << "'";
}
}
}
+
+} // namespace init
+} // namespace android
diff --git a/init/import_parser.h b/init/import_parser.h
index 0e91025..5a2f894 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -17,27 +17,30 @@
#ifndef _INIT_IMPORT_PARSER_H
#define _INIT_IMPORT_PARSER_H
-#include "init_parser.h"
-
#include <string>
#include <vector>
+#include "parser.h"
+
+namespace android {
+namespace init {
+
class ImportParser : public SectionParser {
-public:
- ImportParser() {
- }
- bool ParseSection(const std::vector<std::string>& args,
- std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override {
- return true;
- }
- void EndSection() override {
- }
- void EndFile(const std::string& filename) override;
-private:
- std::vector<std::string> imports_;
+ public:
+ ImportParser(Parser* parser) : parser_(parser) {}
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ void EndFile() override;
+
+ private:
+ Parser* parser_;
+ // Store filename for later error reporting.
+ std::string filename_;
+ // Vector of imports and their line numbers for later error reporting.
+ std::vector<std::pair<std::string, int>> imports_;
};
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/init.cpp b/init/init.cpp
index bb6355a..c3e08fb 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -14,82 +14,108 @@
* limitations under the License.
*/
-#include <ctype.h>
+#include "init.h"
+
#include <dirent.h>
-#include <errno.h>
#include <fcntl.h>
-#include <inttypes.h>
-#include <keyutils.h>
-#include <libgen.h>
#include <paths.h>
+#include <seccomp_policy.h>
#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/mount.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
+#include <sys/signalfd.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
-#include <sys/un.h>
-#include <sys/wait.h>
#include <unistd.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-#include <selinux/android.h>
-
+#include <android-base/chrono_utils.h>
#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 <cutils/android_reboot.h>
+#include <keyutils.h>
#include <libavb/libavb.h>
#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
-#include <fstream>
#include <memory>
-#include <vector>
+#include <optional>
-#include "action.h"
-#include "bootchart.h"
-#include "devices.h"
#include "import_parser.h"
-#include "init.h"
#include "init_first_stage.h"
-#include "init_parser.h"
#include "keychords.h"
#include "log.h"
#include "property_service.h"
#include "reboot.h"
-#include "service.h"
-#include "signal_handler.h"
+#include "security.h"
+#include "selinux.h"
+#include "sigchld_handler.h"
#include "ueventd.h"
#include "util.h"
#include "watchdogd.h"
-using android::base::GetProperty;
-using android::base::StringPrintf;
+using namespace std::string_literals;
-struct selabel_handle *sehandle;
-struct selabel_handle *sehandle_prop;
+using android::base::boot_clock;
+using android::base::GetProperty;
+using android::base::Timer;
+
+namespace android {
+namespace init {
static int property_triggers_enabled = 0;
static char qemu[32];
std::string default_console = "/dev/console";
-static time_t process_needs_restart_at;
-
-const char *ENV[32];
static int epoll_fd = -1;
+static int sigterm_signal_fd = -1;
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
static std::string wait_prop_name;
static std::string wait_prop_value;
+static bool shutting_down;
+
+std::vector<std::string> late_import_paths;
+
+void DumpState() {
+ ServiceList::GetInstance().DumpState();
+ ActionManager::GetInstance().DumpState();
+}
+
+Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
+ Parser parser;
+
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager));
+ parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
+
+ return parser;
+}
+
+static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
+ Parser parser = CreateParser(action_manager, service_list);
+
+ std::string bootscript = GetProperty("ro.boot.init_rc", "");
+ if (bootscript.empty()) {
+ parser.ParseConfig("/init.rc");
+ if (!parser.ParseConfig("/system/etc/init")) {
+ late_import_paths.emplace_back("/system/etc/init");
+ }
+ if (!parser.ParseConfig("/vendor/etc/init")) {
+ late_import_paths.emplace_back("/vendor/etc/init");
+ }
+ if (!parser.ParseConfig("/odm/etc/init")) {
+ late_import_paths.emplace_back("/odm/etc/init");
+ }
+ } else {
+ parser.ParseConfig(bootscript);
+ }
+}
void register_epoll_handler(int fd, void (*fn)()) {
epoll_event ev;
@@ -100,38 +126,6 @@
}
}
-/* add_environment - add "key=value" to the current environment */
-int add_environment(const char *key, const char *val)
-{
- size_t n;
- size_t key_len = strlen(key);
-
- /* The last environment entry is reserved to terminate the list */
- for (n = 0; n < (arraysize(ENV) - 1); n++) {
-
- /* Delete any existing entry for this key */
- if (ENV[n] != NULL) {
- size_t entry_key_len = strcspn(ENV[n], "=");
- if ((entry_key_len == key_len) && (strncmp(ENV[n], key, entry_key_len) == 0)) {
- free((char*)ENV[n]);
- ENV[n] = NULL;
- }
- }
-
- /* Add entry if a free slot is available */
- if (ENV[n] == NULL) {
- char* entry;
- asprintf(&entry, "%s=%s", key, val);
- ENV[n] = entry;
- return 0;
- }
- }
-
- LOG(ERROR) << "No env. room to store: '" << key << "':'" << val << "'";
-
- return -1;
-}
-
bool start_waiting_for_property(const char *name, const char *value)
{
if (waiting_for_prop) {
@@ -149,42 +143,65 @@
return true;
}
+void ResetWaitForProp() {
+ wait_prop_name.clear();
+ wait_prop_value.clear();
+ waiting_for_prop.reset();
+}
+
void property_changed(const std::string& name, const std::string& value) {
// If the property is sys.powerctl, we bypass the event queue and immediately handle it.
// This is to ensure that init will always and immediately shutdown/reboot, regardless of
// if there are other pending events to process or if init is waiting on an exec service or
// waiting on a property.
- if (name == "sys.powerctl") HandlePowerctlMessage(value);
+ // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
+ // commands to be executed.
+ if (name == "sys.powerctl") {
+ if (HandlePowerctlMessage(value)) {
+ shutting_down = true;
+ }
+ }
- if (property_triggers_enabled)
- ActionManager::GetInstance().QueuePropertyTrigger(name, value);
+ if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
+
if (waiting_for_prop) {
if (wait_prop_name == name && wait_prop_value == value) {
- wait_prop_name.clear();
- wait_prop_value.clear();
LOG(INFO) << "Wait for property took " << *waiting_for_prop;
- waiting_for_prop.reset();
+ ResetWaitForProp();
}
}
}
-static void restart_processes()
-{
- process_needs_restart_at = 0;
- ServiceManager::GetInstance().ForEachServiceWithFlags(SVC_RESTARTING, [](Service* s) {
- s->RestartIfNeeded(&process_needs_restart_at);
- });
+static std::optional<boot_clock::time_point> RestartProcesses() {
+ std::optional<boot_clock::time_point> next_process_restart_time;
+ for (const auto& s : ServiceList::GetInstance()) {
+ if (!(s->flags() & SVC_RESTARTING)) continue;
+
+ auto restart_time = s->time_started() + 5s;
+ if (boot_clock::now() > restart_time) {
+ if (auto result = s->Start(); !result) {
+ LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
+ }
+ } else {
+ if (!next_process_restart_time || restart_time < *next_process_restart_time) {
+ next_process_restart_time = restart_time;
+ }
+ }
+ }
+ return next_process_restart_time;
}
void handle_control_message(const std::string& msg, const std::string& name) {
- Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
+ Service* svc = ServiceList::GetInstance().FindService(name);
if (svc == nullptr) {
LOG(ERROR) << "no such service '" << name << "'";
return;
}
if (msg == "start") {
- svc->Start();
+ if (auto result = svc->Start(); !result) {
+ LOG(ERROR) << "Could not ctl.start service '" << name << "': " << result.error();
+ }
} else if (msg == "stop") {
svc->Stop();
} else if (msg == "restart") {
@@ -194,7 +211,7 @@
}
}
-static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
+static Result<Success> wait_for_coldboot_done_action(const std::vector<std::string>& args) {
Timer t;
LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
@@ -208,233 +225,24 @@
// because any build that slow isn't likely to boot at all, and we'd
// rather any test lab devices fail back to the bootloader.
if (wait_for_file(COLDBOOT_DONE, 60s) < 0) {
- LOG(ERROR) << "Timed out waiting for " COLDBOOT_DONE;
- panic();
+ LOG(FATAL) << "Timed out waiting for " COLDBOOT_DONE;
}
- property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration_ms()).c_str());
- return 0;
+ property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
+ return Success();
}
-/*
- * Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
- * by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
- * Does nothing if Hardware RNG is not present.
- *
- * Since we don't yet trust the quality of Hardware RNG, these bytes are not
- * mixed into the primary pool of Linux RNG and the entropy estimate is left
- * unmodified.
- *
- * If the HW RNG device /dev/hw_random is present, we require that at least
- * 512 bytes read from it are written into Linux RNG. QA is expected to catch
- * devices/configurations where these I/O operations are blocking for a long
- * time. We do not reboot or halt on failures, as this is a best-effort
- * attempt.
- */
-static int mix_hwrng_into_linux_rng_action(const std::vector<std::string>& args)
-{
- int result = -1;
- int hwrandom_fd = -1;
- int urandom_fd = -1;
- char buf[512];
- ssize_t chunk_size;
- size_t total_bytes_written = 0;
-
- hwrandom_fd = TEMP_FAILURE_RETRY(
- open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
- if (hwrandom_fd == -1) {
- if (errno == ENOENT) {
- LOG(ERROR) << "/dev/hw_random not found";
- // It's not an error to not have a Hardware RNG.
- result = 0;
- } else {
- PLOG(ERROR) << "Failed to open /dev/hw_random";
- }
- goto ret;
- }
-
- urandom_fd = TEMP_FAILURE_RETRY(
- open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
- if (urandom_fd == -1) {
- PLOG(ERROR) << "Failed to open /dev/urandom";
- goto ret;
- }
-
- while (total_bytes_written < sizeof(buf)) {
- chunk_size = TEMP_FAILURE_RETRY(
- read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
- if (chunk_size == -1) {
- PLOG(ERROR) << "Failed to read from /dev/hw_random";
- goto ret;
- } else if (chunk_size == 0) {
- LOG(ERROR) << "Failed to read from /dev/hw_random: EOF";
- goto ret;
- }
-
- chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
- if (chunk_size == -1) {
- PLOG(ERROR) << "Failed to write to /dev/urandom";
- goto ret;
- }
- total_bytes_written += chunk_size;
- }
-
- LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
- result = 0;
-
-ret:
- if (hwrandom_fd != -1) {
- close(hwrandom_fd);
- }
- if (urandom_fd != -1) {
- close(urandom_fd);
- }
- return result;
-}
-
-static void security_failure() {
- LOG(ERROR) << "Security failure...";
- panic();
-}
-
-static bool set_highest_available_option_value(std::string path, int min, int max)
-{
- std::ifstream inf(path, std::fstream::in);
- if (!inf) {
- LOG(ERROR) << "Cannot open for reading: " << path;
- return false;
- }
-
- int current = max;
- while (current >= min) {
- // try to write out new value
- std::string str_val = std::to_string(current);
- std::ofstream of(path, std::fstream::out);
- if (!of) {
- LOG(ERROR) << "Cannot open for writing: " << path;
- return false;
- }
- of << str_val << std::endl;
- of.close();
-
- // check to make sure it was recorded
- inf.seekg(0);
- std::string str_rec;
- inf >> str_rec;
- if (str_val.compare(str_rec) == 0) {
- break;
- }
- current--;
- }
- inf.close();
-
- if (current < min) {
- LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
- return false;
- }
- return true;
-}
-
-#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
-#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
-
-/* __attribute__((unused)) due to lack of mips support: see mips block
- * in set_mmap_rnd_bits_action */
-static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
- std::string path;
- if (compat) {
- path = MMAP_RND_COMPAT_PATH;
- } else {
- path = MMAP_RND_PATH;
- }
-
- return set_highest_available_option_value(path, min, start);
-}
-
-/*
- * Set /proc/sys/vm/mmap_rnd_bits and potentially
- * /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
- * Returns -1 if unable to set these to an acceptable value.
- *
- * To support this sysctl, the following upstream commits are needed:
- *
- * d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
- * e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
- * 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
- * 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
- * ec9ee4acd97c drivers: char: random: add get_random_long()
- * 5ef11c35ce86 mm: ASLR: use get_random_long()
- */
-static int set_mmap_rnd_bits_action(const std::vector<std::string>& args)
-{
- int ret = -1;
-
- /* values are arch-dependent */
-#if defined(__aarch64__)
- /* arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE */
- if (set_mmap_rnd_bits_min(33, 24, false)
- && set_mmap_rnd_bits_min(16, 16, true)) {
- ret = 0;
- }
-#elif defined(__x86_64__)
- /* x86_64 supports 28 - 32 bits */
- if (set_mmap_rnd_bits_min(32, 32, false)
- && set_mmap_rnd_bits_min(16, 16, true)) {
- ret = 0;
- }
-#elif defined(__arm__) || defined(__i386__)
- /* check to see if we're running on 64-bit kernel */
- bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
- /* supported 32-bit architecture must have 16 bits set */
- if (set_mmap_rnd_bits_min(16, 16, h64)) {
- ret = 0;
- }
-#elif defined(__mips__) || defined(__mips64__)
- // TODO: add mips support b/27788820
- ret = 0;
-#else
- LOG(ERROR) << "Unknown architecture";
-#endif
-
- if (ret == -1) {
- LOG(ERROR) << "Unable to set adequate mmap entropy value!";
- security_failure();
- }
- return ret;
-}
-
-#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
-#define KPTR_RESTRICT_MINVALUE 2
-#define KPTR_RESTRICT_MAXVALUE 4
-
-/* Set kptr_restrict to the highest available level.
- *
- * Aborts if unable to set this to an acceptable value.
- */
-static int set_kptr_restrict_action(const std::vector<std::string>& args)
-{
- std::string path = KPTR_RESTRICT_PATH;
-
- if (!set_highest_available_option_value(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
- LOG(ERROR) << "Unable to set adequate kptr_restrict value!";
- security_failure();
- }
- return 0;
-}
-
-static int keychord_init_action(const std::vector<std::string>& args)
-{
+static Result<Success> keychord_init_action(const std::vector<std::string>& args) {
keychord_init();
- return 0;
+ return Success();
}
-static int console_init_action(const std::vector<std::string>& args)
-{
+static Result<Success> console_init_action(const std::vector<std::string>& args) {
std::string console = GetProperty("ro.boot.console", "");
if (!console.empty()) {
default_console = "/dev/" + console;
}
- return 0;
+ return Success();
}
static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
@@ -442,14 +250,14 @@
if (for_emulator) {
// In the emulator, export any kernel option with the "ro.kernel." prefix.
- property_set(StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
+ property_set("ro.kernel." + key, value);
return;
}
if (key == "qemu") {
strlcpy(qemu, value.c_str(), sizeof(qemu));
} else if (android::base::StartsWith(key, "androidboot.")) {
- property_set(StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(), value.c_str());
+ property_set("ro.boot." + key.substr(12), value);
}
}
@@ -480,7 +288,7 @@
};
for (size_t i = 0; i < arraysize(prop_map); i++) {
std::string value = GetProperty(prop_map[i].src_prop, "");
- property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
+ property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value);
}
}
@@ -489,7 +297,7 @@
return;
}
- std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(kAndroidDtDir.c_str()), closedir);
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
if (!dir) return;
std::string dt_file;
@@ -499,13 +307,12 @@
continue;
}
- std::string file_name = kAndroidDtDir + dp->d_name;
+ std::string file_name = get_android_dt_dir() + dp->d_name;
android::base::ReadFileToString(file_name, &dt_file);
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
- std::string property_name = StringPrintf("ro.boot.%s", dp->d_name);
- property_set(property_name.c_str(), dt_file.c_str());
+ property_set("ro.boot."s + dp->d_name, dt_file);
}
}
@@ -517,391 +324,24 @@
if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}
-static int property_enable_triggers_action(const std::vector<std::string>& args)
-{
+static Result<Success> property_enable_triggers_action(const std::vector<std::string>& args) {
/* Enable property triggers. */
property_triggers_enabled = 1;
- return 0;
+ return Success();
}
-static int queue_property_triggers_action(const std::vector<std::string>& args)
-{
+static Result<Success> queue_property_triggers_action(const std::vector<std::string>& args) {
ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
- ActionManager::GetInstance().QueueAllPropertyTriggers();
- return 0;
+ ActionManager::GetInstance().QueueAllPropertyActions();
+ return Success();
}
-static void selinux_init_all_handles(void)
-{
- sehandle = selinux_android_file_context_handle();
- selinux_android_set_sehandle(sehandle);
- sehandle_prop = selinux_android_prop_context_handle();
-}
-
-enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
-
-static selinux_enforcing_status selinux_status_from_cmdline() {
- selinux_enforcing_status status = SELINUX_ENFORCING;
-
- import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
- if (key == "androidboot.selinux" && value == "permissive") {
- status = SELINUX_PERMISSIVE;
+static void global_seccomp() {
+ import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
+ LOG(FATAL) << "Failed to globally enable seccomp!";
}
});
-
- return status;
-}
-
-static bool selinux_is_enforcing(void)
-{
- if (ALLOW_PERMISSIVE_SELINUX) {
- return selinux_status_from_cmdline() == SELINUX_ENFORCING;
- }
- return true;
-}
-
-static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
-
- property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
-
- if (!d || !d->name || !d->cr) {
- LOG(ERROR) << "audit_callback invoked with null data arguments!";
- return 0;
- }
-
- snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name,
- d->cr->pid, d->cr->uid, d->cr->gid);
- return 0;
-}
-
-/*
- * Forks, executes the provided program in the child, and waits for the completion in the parent.
- * Child's stderr is captured and logged using LOG(ERROR).
- *
- * Returns true if the child exited with status code 0, returns false otherwise.
- */
-static bool fork_execve_and_wait_for_completion(const char* filename, char* const argv[],
- char* const envp[]) {
- // Create a pipe used for redirecting child process's output.
- // * pipe_fds[0] is the FD the parent will use for reading.
- // * pipe_fds[1] is the FD the child will use for writing.
- int pipe_fds[2];
- if (pipe(pipe_fds) == -1) {
- PLOG(ERROR) << "Failed to create pipe";
- return false;
- }
-
- pid_t child_pid = fork();
- if (child_pid == -1) {
- PLOG(ERROR) << "Failed to fork for " << filename;
- return false;
- }
-
- if (child_pid == 0) {
- // fork succeeded -- this is executing in the child process
-
- // Close the pipe FD not used by this process
- TEMP_FAILURE_RETRY(close(pipe_fds[0]));
-
- // Redirect stderr to the pipe FD provided by the parent
- if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
- PLOG(ERROR) << "Failed to redirect stderr of " << filename;
- _exit(127);
- return false;
- }
- TEMP_FAILURE_RETRY(close(pipe_fds[1]));
-
- if (execve(filename, argv, envp) == -1) {
- PLOG(ERROR) << "Failed to execve " << filename;
- return false;
- }
- // Unreachable because execve will have succeeded and replaced this code
- // with child process's code.
- _exit(127);
- return false;
- } else {
- // fork succeeded -- this is executing in the original/parent process
-
- // Close the pipe FD not used by this process
- TEMP_FAILURE_RETRY(close(pipe_fds[1]));
-
- // Log the redirected output of the child process.
- // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
- // As a result, we're buffering all output and logging it in one go at the end of the
- // invocation, instead of logging it as it comes in.
- const int child_out_fd = pipe_fds[0];
- std::string child_output;
- if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
- PLOG(ERROR) << "Failed to capture full output of " << filename;
- }
- TEMP_FAILURE_RETRY(close(child_out_fd));
- if (!child_output.empty()) {
- // Log captured output, line by line, because LOG expects to be invoked for each line
- std::istringstream in(child_output);
- std::string line;
- while (std::getline(in, line)) {
- LOG(ERROR) << filename << ": " << line;
- }
- }
-
- // Wait for child to terminate
- int status;
- if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
- PLOG(ERROR) << "Failed to wait for " << filename;
- return false;
- }
-
- if (WIFEXITED(status)) {
- int status_code = WEXITSTATUS(status);
- if (status_code == 0) {
- return true;
- } else {
- LOG(ERROR) << filename << " exited with status " << status_code;
- }
- } else if (WIFSIGNALED(status)) {
- LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
- } else if (WIFSTOPPED(status)) {
- LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
- } else {
- LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
- }
-
- return false;
- }
-}
-
-static bool read_first_line(const char* file, std::string* line) {
- line->clear();
-
- std::string contents;
- if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
- return false;
- }
- std::istringstream in(contents);
- std::getline(in, *line);
- return true;
-}
-
-static bool selinux_find_precompiled_split_policy(std::string* file) {
- file->clear();
-
- static constexpr const char precompiled_sepolicy[] = "/vendor/etc/selinux/precompiled_sepolicy";
- if (access(precompiled_sepolicy, R_OK) == -1) {
- return false;
- }
- std::string actual_plat_id;
- if (!read_first_line("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256",
- &actual_plat_id)) {
- PLOG(INFO) << "Failed to read "
- "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
- return false;
- }
- std::string precompiled_plat_id;
- if (!read_first_line("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
- &precompiled_plat_id)) {
- PLOG(INFO) << "Failed to read "
- "/vendor/etc/selinux/"
- "precompiled_sepolicy.plat_and_mapping.sha256";
- return false;
- }
- if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
- return false;
- }
-
- *file = precompiled_sepolicy;
- return true;
-}
-
-static bool selinux_get_vendor_mapping_version(std::string* plat_vers) {
- if (!read_first_line("/vendor/etc/selinux/plat_sepolicy_vers.txt", plat_vers)) {
- PLOG(ERROR) << "Failed to read /vendor/etc/selinux/plat_sepolicy_vers.txt";
- return false;
- }
- if (plat_vers->empty()) {
- LOG(ERROR) << "No version present in plat_sepolicy_vers.txt";
- return false;
- }
- return true;
-}
-
-static constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
-
-static bool selinux_is_split_policy_device() { return access(plat_policy_cil_file, R_OK) != -1; }
-
-/*
- * Loads SELinux policy split across platform/system and non-platform/vendor files.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_split_policy() {
- // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
- // * platform -- policy needed due to logic contained in the system image,
- // * non-platform -- policy needed due to logic contained in the vendor image,
- // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
- // with newer versions of platform policy.
- //
- // secilc is invoked to compile the above three policy files into a single monolithic policy
- // file. This file is then loaded into the kernel.
-
- // Load precompiled policy from vendor image, if a matching policy is found there. The policy
- // must match the platform policy on the system image.
- std::string precompiled_sepolicy_file;
- if (selinux_find_precompiled_split_policy(&precompiled_sepolicy_file)) {
- android::base::unique_fd fd(
- open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
- if (fd != -1) {
- if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
- LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
- return false;
- }
- return true;
- }
- }
- // No suitable precompiled policy could be loaded
-
- LOG(INFO) << "Compiling SELinux policy";
-
- // Determine the highest policy language version supported by the kernel
- set_selinuxmnt("/sys/fs/selinux");
- int max_policy_version = security_policyvers();
- if (max_policy_version == -1) {
- PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
- return false;
- }
-
- // We store the output of the compilation on /dev because this is the most convenient tmpfs
- // storage mount available this early in the boot sequence.
- char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
- android::base::unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
- if (compiled_sepolicy_fd < 0) {
- PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
- return false;
- }
-
- // Determine which mapping file to include
- std::string vend_plat_vers;
- if (!selinux_get_vendor_mapping_version(&vend_plat_vers)) {
- return false;
- }
- std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
- // clang-format off
- const char* compile_args[] = {
- "/system/bin/secilc",
- plat_policy_cil_file,
- "-M", "true", "-G", "-N",
- // Target the highest policy language version supported by the kernel
- "-c", std::to_string(max_policy_version).c_str(),
- mapping_file.c_str(),
- "/vendor/etc/selinux/nonplat_sepolicy.cil",
- "-o", compiled_sepolicy,
- // We don't care about file_contexts output by the compiler
- "-f", "/sys/fs/selinux/null", // /dev/null is not yet available
- nullptr};
- // clang-format on
-
- if (!fork_execve_and_wait_for_completion(compile_args[0], (char**)compile_args, (char**)ENV)) {
- unlink(compiled_sepolicy);
- return false;
- }
- unlink(compiled_sepolicy);
-
- LOG(INFO) << "Loading compiled SELinux policy";
- if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
- LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
- return false;
- }
-
- return true;
-}
-
-/*
- * Loads SELinux policy from a monolithic file.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_monolithic_policy() {
- LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
- if (selinux_android_load_policy() < 0) {
- PLOG(ERROR) << "Failed to load monolithic SELinux policy";
- return false;
- }
- return true;
-}
-
-/*
- * Loads SELinux policy into the kernel.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_policy() {
- return selinux_is_split_policy_device() ? selinux_load_split_policy()
- : selinux_load_monolithic_policy();
-}
-
-static void selinux_initialize(bool in_kernel_domain) {
- Timer t;
-
- selinux_callback cb;
- cb.func_log = selinux_klog_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
- cb.func_audit = audit_callback;
- selinux_set_callback(SELINUX_CB_AUDIT, cb);
-
- if (in_kernel_domain) {
- LOG(INFO) << "Loading SELinux policy";
- if (!selinux_load_policy()) {
- panic();
- }
-
- bool kernel_enforcing = (security_getenforce() == 1);
- bool is_enforcing = selinux_is_enforcing();
- if (kernel_enforcing != is_enforcing) {
- if (security_setenforce(is_enforcing)) {
- PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
- security_failure();
- }
- }
-
- if (!write_file("/sys/fs/selinux/checkreqprot", "0")) {
- security_failure();
- }
-
- // init's first stage can't set properties, so pass the time to the second stage.
- setenv("INIT_SELINUX_TOOK", std::to_string(t.duration_ms()).c_str(), 1);
- } else {
- selinux_init_all_handles();
- }
-}
-
-// The files and directories that were created before initial sepolicy load
-// need to have their security context restored to the proper value.
-// This must happen before /dev is populated by ueventd.
-static void selinux_restore_context() {
- LOG(INFO) << "Running restorecon...";
- restorecon("/dev");
- restorecon("/dev/kmsg");
- restorecon("/dev/socket");
- restorecon("/dev/random");
- restorecon("/dev/urandom");
- restorecon("/dev/__properties__");
-
- restorecon("/file_contexts.bin");
- restorecon("/plat_file_contexts");
- restorecon("/nonplat_file_contexts");
- restorecon("/plat_property_contexts");
- restorecon("/nonplat_property_contexts");
- restorecon("/plat_seapp_contexts");
- restorecon("/nonplat_seapp_contexts");
- restorecon("/plat_service_contexts");
- restorecon("/nonplat_service_contexts");
- restorecon("/plat_hwservice_contexts");
- restorecon("/nonplat_hwservice_contexts");
- restorecon("/sepolicy");
- restorecon("/vndservice_contexts");
-
- restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
- restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
- restorecon("/dev/device-mapper");
}
// Set the UDC controller for the ConfigFS USB Gadgets.
@@ -920,7 +360,7 @@
}
}
-static void install_reboot_signal_handlers() {
+static void InstallRebootSignalHandlers() {
// Instead of panic'ing the kernel as is the default behavior when init crashes,
// we prefer to reboot to bootloader on development builds, as this will prevent
// boot looping bad configurations and allow both developers and test farms to easily
@@ -928,9 +368,18 @@
struct sigaction action;
memset(&action, 0, sizeof(action));
sigfillset(&action.sa_mask);
- action.sa_handler = [](int) {
- // panic() reboots to bootloader
- panic();
+ action.sa_handler = [](int signal) {
+ // These signal handlers are also caught for processes forked from init, however we do not
+ // want them to trigger reboot, so we directly call _exit() for children processes here.
+ if (getpid() != 1) {
+ _exit(signal);
+ }
+
+ // Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
+ // RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
+ // and probably good enough given this is already an error case and only enabled for
+ // development builds.
+ RebootSystem(ANDROID_RB_RESTART2, "bootloader");
};
action.sa_flags = SA_RESTART;
sigaction(SIGABRT, &action, nullptr);
@@ -945,6 +394,41 @@
sigaction(SIGTRAP, &action, nullptr);
}
+static void HandleSigtermSignal() {
+ signalfd_siginfo siginfo;
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(sigterm_signal_fd, &siginfo, sizeof(siginfo)));
+ if (bytes_read != sizeof(siginfo)) {
+ PLOG(ERROR) << "Failed to read siginfo from sigterm_signal_fd";
+ return;
+ }
+
+ if (siginfo.ssi_pid != 0) {
+ // Drop any userspace SIGTERM requests.
+ LOG(DEBUG) << "Ignoring SIGTERM from pid " << siginfo.ssi_pid;
+ return;
+ }
+
+ LOG(INFO) << "Handling SIGTERM, shutting system down";
+ HandlePowerctlMessage("shutdown");
+}
+
+static void InstallSigtermHandler() {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGTERM);
+
+ if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
+ PLOG(FATAL) << "failed to block SIGTERM";
+ }
+
+ sigterm_signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
+ if (sigterm_signal_fd == -1) {
+ PLOG(FATAL) << "failed to create signalfd for SIGTERM";
+ }
+
+ register_epoll_handler(sigterm_signal_fd, HandleSigtermSignal);
+}
+
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
@@ -955,11 +439,9 @@
}
if (REBOOT_BOOTLOADER_ON_PANIC) {
- install_reboot_signal_handlers();
+ InstallRebootSignalHandlers();
}
- add_environment("PATH", _PATH_DEFPATH);
-
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
if (is_first_stage) {
@@ -968,6 +450,8 @@
// Clear the umask.
umask(0);
+ clearenv();
+ setenv("PATH", _PATH_DEFPATH, 1);
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
@@ -982,7 +466,13 @@
setgroups(arraysize(groups), groups);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
+
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
+
+ if constexpr (WORLD_WRITABLE_KMSG) {
+ mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
+ }
+
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
@@ -993,27 +483,29 @@
LOG(INFO) << "init first stage started!";
if (!DoFirstStageMount()) {
- LOG(ERROR) << "Failed to mount required partitions early ...";
- panic();
+ LOG(FATAL) << "Failed to mount required partitions early ...";
}
SetInitAvbVersionInRecovery();
+ // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
+ global_seccomp();
+
// Set up SELinux, loading the SELinux policy.
- selinux_initialize(true);
+ SelinuxSetupKernelLogging();
+ SelinuxInitialize();
// We're in the kernel domain, so re-exec init to transition to the init domain now
// that the SELinux policy has been loaded.
- if (restorecon("/init") == -1) {
- PLOG(ERROR) << "restorecon failed";
- security_failure();
+ if (selinux_android_restorecon("/init", 0) == -1) {
+ PLOG(FATAL) << "restorecon failed of /init failed";
}
setenv("INIT_SECOND_STAGE", "true", 1);
static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
- setenv("INIT_STARTED_AT", StringPrintf("%" PRIu64, start_ms).c_str(), 1);
+ setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
char* path = argv[0];
char* args[] = { path, nullptr };
@@ -1021,8 +513,7 @@
// execv() only returns if an error happened, in which case we
// panic and never fall through this conditional.
- PLOG(ERROR) << "execv(\"" << path << "\") failed";
- security_failure();
+ PLOG(FATAL) << "execv(\"" << path << "\") failed";
}
// At this point we're in the second stage of init.
@@ -1032,7 +523,7 @@
// Set up a session keyring that all processes will have access to. It
// will hold things like FBE encryption keys. No process should override
// its session keyring.
- keyctl(KEYCTL_GET_KEYRING_ID, KEY_SPEC_SESSION_KEYRING, 1);
+ keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
@@ -1063,8 +554,9 @@
unsetenv("INIT_AVB_VERSION");
// Now set up SELinux for second stage.
- selinux_initialize(false);
- selinux_restore_context();
+ SelinuxSetupKernelLogging();
+ SelabelInitialize();
+ SelinuxRestoreContext();
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
@@ -1072,7 +564,13 @@
exit(1);
}
- signal_handler_init();
+ sigchld_handler_init();
+
+ if (!IsRebootCapable()) {
+ // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
+ // In that case, receiving SIGTERM will cause the system to shut down.
+ InstallSigtermHandler();
+ }
property_load_boot_defaults();
export_oem_lock_status();
@@ -1082,39 +580,23 @@
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
- Parser& parser = Parser::GetInstance();
- parser.AddSectionParser("service",std::make_unique<ServiceParser>());
- parser.AddSectionParser("on", std::make_unique<ActionParser>());
- parser.AddSectionParser("import", std::make_unique<ImportParser>());
- std::string bootscript = GetProperty("ro.boot.init_rc", "");
- if (bootscript.empty()) {
- parser.ParseConfig("/init.rc");
- parser.set_is_system_etc_init_loaded(
- parser.ParseConfig("/system/etc/init"));
- parser.set_is_vendor_etc_init_loaded(
- parser.ParseConfig("/vendor/etc/init"));
- parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
- } else {
- parser.ParseConfig(bootscript);
- parser.set_is_system_etc_init_loaded(true);
- parser.set_is_vendor_etc_init_loaded(true);
- parser.set_is_odm_etc_init_loaded(true);
- }
+ ActionManager& am = ActionManager::GetInstance();
+ ServiceList& sm = ServiceList::GetInstance();
+
+ LoadBootScripts(am, sm);
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
- if (false) parser.DumpState();
-
- ActionManager& am = ActionManager::GetInstance();
+ if (false) DumpState();
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
- am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
- am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
- am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
+ am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
+ am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
+ am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");
@@ -1123,7 +605,7 @@
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
- am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+ am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
@@ -1140,16 +622,20 @@
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
- if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+ if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
- if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
- restart_processes();
+ if (!(waiting_for_prop || Service::is_exec_service_running())) {
+ if (!shutting_down) {
+ auto next_process_restart_time = RestartProcesses();
- // If there's a process that needs restarting, wake up in time for that.
- if (process_needs_restart_at != 0) {
- epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
- if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+ // If there's a process that needs restarting, wake up in time for that.
+ if (next_process_restart_time) {
+ epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
+ *next_process_restart_time - boot_clock::now())
+ .count();
+ if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+ }
}
// If there's more work to do, wake up again immediately.
@@ -1167,3 +653,10 @@
return 0;
}
+
+} // namespace init
+} // namespace android
+
+int main(int argc, char** argv) {
+ android::init::main(argc, argv);
+}
diff --git a/init/init.h b/init/init.h
index 1da3350..b757c1d 100644
--- a/init/init.h
+++ b/init/init.h
@@ -18,11 +18,22 @@
#define _INIT_INIT_H
#include <string>
+#include <vector>
-extern const char *ENV[32];
+#include "action.h"
+#include "parser.h"
+#include "service.h"
+
+namespace android {
+namespace init {
+
+// Note: These globals are *only* valid in init, so they should not be used in ueventd,
+// watchdogd, or any files that may be included in those, such as devices.cpp and util.cpp.
+// TODO: Have an Init class and remove all globals.
extern std::string default_console;
-extern struct selabel_handle *sehandle;
-extern struct selabel_handle *sehandle_prop;
+extern std::vector<std::string> late_import_paths;
+
+Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
void handle_control_message(const std::string& msg, const std::string& arg);
@@ -30,8 +41,13 @@
void register_epoll_handler(int fd, void (*fn)());
-int add_environment(const char* key, const char* val);
-
bool start_waiting_for_property(const char *name, const char *value);
+void DumpState();
+
+void ResetWaitForProp();
+
+} // namespace init
+} // namespace android
+
#endif /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index bcc8d1b..0f7e38f 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -19,11 +19,13 @@
#include <stdlib.h>
#include <unistd.h>
+#include <chrono>
#include <memory>
#include <set>
#include <string>
#include <vector>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
@@ -31,8 +33,15 @@
#include "devices.h"
#include "fs_mgr.h"
#include "fs_mgr_avb.h"
+#include "uevent.h"
+#include "uevent_listener.h"
#include "util.h"
+using android::base::Timer;
+
+namespace android {
+namespace init {
+
// Class Declarations
// ------------------
class FirstStageMount {
@@ -47,22 +56,23 @@
bool InitDevices();
protected:
- void InitRequiredDevices();
- void InitVerityDevice(const std::string& verity_device);
+ bool InitRequiredDevices();
+ bool InitVerityDevice(const std::string& verity_device);
bool MountPartitions();
- virtual coldboot_action_t ColdbootCallback(uevent* uevent);
+ virtual ListenerAction UeventCallback(const Uevent& uevent);
// Pure virtual functions.
virtual bool GetRequiredDevices() = 0;
virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
bool need_dm_verity_;
- // Device tree fstab entries.
+
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
- // Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
std::vector<fstab_rec*> mount_fstab_recs_;
std::set<std::string> required_devices_partition_names_;
+ DeviceHandler device_handler_;
+ UeventListener uevent_listener_;
};
class FirstStageMountVBootV1 : public FirstStageMount {
@@ -83,7 +93,7 @@
~FirstStageMountVBootV2() override = default;
protected:
- coldboot_action_t ColdbootCallback(uevent* uevent) override;
+ ListenerAction UeventCallback(const Uevent& uevent) override;
bool GetRequiredDevices() override;
bool SetUpDmVerity(fstab_rec* fstab_rec) override;
bool InitAvbHandle();
@@ -111,12 +121,10 @@
LOG(ERROR) << "Failed to read fstab from device tree";
return;
}
- for (auto mount_point : {"/system", "/vendor", "/odm"}) {
- fstab_rec* fstab_rec =
- fs_mgr_get_entry_for_mount_point(device_tree_fstab_.get(), mount_point);
- if (fstab_rec != nullptr) {
- mount_fstab_recs_.push_back(fstab_rec);
- }
+ // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
+ // for easier manipulation later, e.g., range-base for loop.
+ for (int i = 0; i < device_tree_fstab_->num_entries; i++) {
+ mount_fstab_recs_.push_back(&device_tree_fstab_->recs[i]);
}
}
@@ -140,86 +148,119 @@
}
bool FirstStageMount::InitDevices() {
- if (!GetRequiredDevices()) return false;
-
- InitRequiredDevices();
-
- // InitRequiredDevices() will remove found partitions from required_devices_partition_names_.
- // So if it isn't empty here, it means some partitions are not found.
- if (!required_devices_partition_names_.empty()) {
- LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
- << android::base::Join(required_devices_partition_names_, ", ");
- return false;
- } else {
- return true;
- }
+ return GetRequiredDevices() && InitRequiredDevices();
}
// Creates devices with uevent->partition_name matching one in the member variable
// required_devices_partition_names_. Found partitions will then be removed from it
// for the subsequent member function to check which devices are NOT created.
-void FirstStageMount::InitRequiredDevices() {
+bool FirstStageMount::InitRequiredDevices() {
if (required_devices_partition_names_.empty()) {
- return;
+ return true;
}
if (need_dm_verity_) {
const std::string dm_path = "/devices/virtual/misc/device-mapper";
- device_init(("/sys" + dm_path).c_str(), [&dm_path](uevent* uevent) -> coldboot_action_t {
- if (uevent->path && uevent->path == dm_path) return COLDBOOT_STOP;
- return COLDBOOT_CONTINUE; // dm_path not found, continue to find it.
- });
+ bool found = false;
+ auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
+ if (uevent.path == dm_path) {
+ device_handler_.HandleDeviceEvent(uevent);
+ found = true;
+ return ListenerAction::kStop;
+ }
+ return ListenerAction::kContinue;
+ };
+ uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
+ if (!found) {
+ LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
+ Timer t;
+ uevent_listener_.Poll(dm_callback, 10s);
+ LOG(INFO) << "Wait for device-mapper returned after " << t;
+ }
+ if (!found) {
+ LOG(ERROR) << "device-mapper device not found after polling timeout";
+ return false;
+ }
}
- device_init(nullptr,
- [this](uevent* uevent) -> coldboot_action_t { return ColdbootCallback(uevent); });
+ auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+ uevent_listener_.RegenerateUevents(uevent_callback);
- device_close();
+ // UeventCallback() will remove found partitions from required_devices_partition_names_.
+ // So if it isn't empty here, it means some partitions are not found.
+ if (!required_devices_partition_names_.empty()) {
+ LOG(INFO) << __PRETTY_FUNCTION__
+ << ": partition(s) not found in /sys, waiting for their uevent(s): "
+ << android::base::Join(required_devices_partition_names_, ", ");
+ Timer t;
+ uevent_listener_.Poll(uevent_callback, 10s);
+ LOG(INFO) << "Wait for partitions returned after " << t;
+ }
+
+ if (!required_devices_partition_names_.empty()) {
+ LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
+ << android::base::Join(required_devices_partition_names_, ", ");
+ return false;
+ }
+
+ return true;
}
-coldboot_action_t FirstStageMount::ColdbootCallback(uevent* uevent) {
- // We need platform devices to create symlinks.
- if (!strncmp(uevent->subsystem, "platform", 8)) {
- return COLDBOOT_CREATE;
- }
-
+ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent) {
// Ignores everything that is not a block device.
- if (strncmp(uevent->subsystem, "block", 5)) {
- return COLDBOOT_CONTINUE;
+ if (uevent.subsystem != "block") {
+ return ListenerAction::kContinue;
}
- if (uevent->partition_name) {
+ if (!uevent.partition_name.empty()) {
// Matches partition name to create device nodes.
// Both required_devices_partition_names_ and uevent->partition_name have A/B
// suffix when A/B is used.
- auto iter = required_devices_partition_names_.find(uevent->partition_name);
+ auto iter = required_devices_partition_names_.find(uevent.partition_name);
if (iter != required_devices_partition_names_.end()) {
- LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
+ LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
required_devices_partition_names_.erase(iter);
+ device_handler_.HandleDeviceEvent(uevent);
if (required_devices_partition_names_.empty()) {
- return COLDBOOT_STOP; // Found all partitions, stop coldboot.
+ return ListenerAction::kStop;
} else {
- return COLDBOOT_CREATE; // Creates this device and continue to find others.
+ return ListenerAction::kContinue;
}
}
}
// Not found a partition or find an unneeded partition, continue to find others.
- return COLDBOOT_CONTINUE;
+ return ListenerAction::kContinue;
}
// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-void FirstStageMount::InitVerityDevice(const std::string& verity_device) {
+bool FirstStageMount::InitVerityDevice(const std::string& verity_device) {
const std::string device_name(basename(verity_device.c_str()));
const std::string syspath = "/sys/block/" + device_name;
+ bool found = false;
- device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
- if (uevent->device_name && uevent->device_name == device_name) {
+ auto verity_callback = [&device_name, &verity_device, this, &found](const Uevent& uevent) {
+ if (uevent.device_name == device_name) {
LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
- return COLDBOOT_STOP;
+ device_handler_.HandleDeviceEvent(uevent);
+ found = true;
+ return ListenerAction::kStop;
}
- return COLDBOOT_CONTINUE;
- });
- device_close();
+ return ListenerAction::kContinue;
+ };
+
+ uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
+ if (!found) {
+ LOG(INFO) << "dm-verity device not found in /sys, waiting for its uevent";
+ Timer t;
+ uevent_listener_.Poll(verity_callback, 10s);
+ LOG(INFO) << "wait for dm-verity device returned after " << t;
+ }
+ if (!found) {
+ LOG(ERROR) << "dm-verity device not found after polling timeout";
+ return false;
+ }
+
+ return true;
}
bool FirstStageMount::MountPartitions() {
@@ -280,14 +321,18 @@
bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
if (fs_mgr_is_verified(fstab_rec)) {
int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
- if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
- LOG(INFO) << "Verity disabled for '" << fstab_rec->mount_point << "'";
- } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
- // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
- // Needs to create it because ueventd isn't started in init first stage.
- InitVerityDevice(fstab_rec->blk_device);
- } else {
- return false;
+ switch (ret) {
+ case FS_MGR_SETUP_VERITY_SKIPPED:
+ case FS_MGR_SETUP_VERITY_DISABLED:
+ LOG(INFO) << "Verity disabled/skipped for '" << fstab_rec->mount_point << "'";
+ return true;
+ case FS_MGR_SETUP_VERITY_SUCCESS:
+ // The exact block device name (fstab_rec->blk_device) is changed to
+ // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
+ // first stage.
+ return InitVerityDevice(fstab_rec->blk_device);
+ default:
+ return false;
}
}
return true; // Returns true to mount the partition.
@@ -345,46 +390,49 @@
return true;
}
-coldboot_action_t FirstStageMountVBootV2::ColdbootCallback(uevent* uevent) {
- // Invokes the parent function to see if any desired partition has been found.
- // If yes, record the by-name symlink for creating FsManagerAvbHandle later.
- coldboot_action_t parent_callback_ret = FirstStageMount::ColdbootCallback(uevent);
-
- // Skips the uevent if the parent function returns COLDBOOT_CONTINUE (meaning
- // that the uevent was skipped) or there is no uevent->partition_name to
- // create the by-name symlink.
- if (parent_callback_ret != COLDBOOT_CONTINUE && uevent->partition_name) {
- // get_block_device_symlinks() will return three symlinks at most, depending on
+ListenerAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
+ // Check if this uevent corresponds to one of the required partitions and store its symlinks if
+ // so, in order to create FsManagerAvbHandle later.
+ // Note that the parent callback removes partitions from the list of required partitions
+ // as it finds them, so this must happen first.
+ if (!uevent.partition_name.empty() &&
+ required_devices_partition_names_.find(uevent.partition_name) !=
+ required_devices_partition_names_.end()) {
+ // GetBlockDeviceSymlinks() will return three symlinks at most, depending on
// the content of uevent. by-name symlink will be at [0] if uevent->partition_name
// is not empty. e.g.,
// - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
// - /dev/block/platform/soc.0/f9824900.sdhci/by-num/p1
// - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
- char** links = get_block_device_symlinks(uevent);
- if (links && links[0]) {
- auto[it, inserted] = by_name_symlink_map_.emplace(uevent->partition_name, links[0]);
+ std::vector<std::string> links = device_handler_.GetBlockDeviceSymlinks(uevent);
+ if (!links.empty()) {
+ auto[it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
if (!inserted) {
- LOG(ERROR) << "Partition '" << uevent->partition_name
+ LOG(ERROR) << "Partition '" << uevent.partition_name
<< "' already existed in the by-name symlink map with a value of '"
<< it->second << "', new value '" << links[0] << "' will be ignored.";
}
}
}
- return parent_callback_ret;
+ return FirstStageMount::UeventCallback(uevent);
}
bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
if (fs_mgr_is_avb(fstab_rec)) {
if (!InitAvbHandle()) return false;
- if (avb_handle_->hashtree_disabled()) {
- LOG(INFO) << "avb hashtree disabled for '" << fstab_rec->mount_point << "'";
- } else if (avb_handle_->SetUpAvb(fstab_rec, false /* wait_for_verity_dev */)) {
- // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
- // Needs to create it because ueventd isn't started in init first stage.
- InitVerityDevice(fstab_rec->blk_device);
- } else {
- return false;
+ SetUpAvbHashtreeResult hashtree_result =
+ avb_handle_->SetUpAvbHashtree(fstab_rec, false /* wait_for_verity_dev */);
+ switch (hashtree_result) {
+ case SetUpAvbHashtreeResult::kDisabled:
+ return true; // Returns true to mount the partition.
+ case SetUpAvbHashtreeResult::kSuccess:
+ // The exact block device name (fstab_rec->blk_device) is changed to
+ // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
+ // first stage.
+ return InitVerityDevice(fstab_rec->blk_device);
+ default:
+ return false;
}
}
return true; // Returns true to mount the partition.
@@ -412,7 +460,7 @@
// Public functions
// ----------------
-// Mounts /system, /vendor, and/or /odm if they are present in the fstab provided by device tree.
+// Mounts partitions specified by fstab in device tree.
bool DoFirstStageMount() {
// Skips first stage mount if we're in recovery mode.
if (IsRecoveryMode()) {
@@ -464,3 +512,6 @@
}
setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
}
+
+} // namespace init
+} // namespace android
diff --git a/init/init_first_stage.h b/init/init_first_stage.h
index 170a24c..c7a3867 100644
--- a/init/init_first_stage.h
+++ b/init/init_first_stage.h
@@ -17,7 +17,13 @@
#ifndef _INIT_FIRST_STAGE_H
#define _INIT_FIRST_STAGE_H
+namespace android {
+namespace init {
+
bool DoFirstStageMount();
void SetInitAvbVersionInRecovery();
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
deleted file mode 100644
index a192862..0000000
--- a/init/init_parser.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2010 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 <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-
-#include "action.h"
-#include "init_parser.h"
-#include "log.h"
-#include "parser.h"
-#include "service.h"
-#include "util.h"
-
-#include <android-base/stringprintf.h>
-
-Parser::Parser() {
-}
-
-Parser& Parser::GetInstance() {
- static Parser instance;
- return instance;
-}
-
-void Parser::AddSectionParser(const std::string& name,
- std::unique_ptr<SectionParser> parser) {
- section_parsers_[name] = std::move(parser);
-}
-
-void Parser::ParseData(const std::string& filename, const std::string& data) {
- //TODO: Use a parser with const input and remove this copy
- std::vector<char> data_copy(data.begin(), data.end());
- data_copy.push_back('\0');
-
- parse_state state;
- state.filename = filename.c_str();
- state.line = 0;
- state.ptr = &data_copy[0];
- state.nexttoken = 0;
-
- SectionParser* section_parser = nullptr;
- std::vector<std::string> args;
-
- for (;;) {
- switch (next_token(&state)) {
- case T_EOF:
- if (section_parser) {
- section_parser->EndSection();
- }
- return;
- case T_NEWLINE:
- state.line++;
- if (args.empty()) {
- break;
- }
- if (section_parsers_.count(args[0])) {
- if (section_parser) {
- section_parser->EndSection();
- }
- section_parser = section_parsers_[args[0]].get();
- std::string ret_err;
- if (!section_parser->ParseSection(args, &ret_err)) {
- parse_error(&state, "%s\n", ret_err.c_str());
- section_parser = nullptr;
- }
- } else if (section_parser) {
- std::string ret_err;
- if (!section_parser->ParseLineSection(args, state.filename,
- state.line, &ret_err)) {
- parse_error(&state, "%s\n", ret_err.c_str());
- }
- }
- args.clear();
- break;
- case T_TEXT:
- args.emplace_back(state.text);
- break;
- }
- }
-}
-
-bool Parser::ParseConfigFile(const std::string& path) {
- LOG(INFO) << "Parsing file " << path << "...";
- Timer t;
- std::string data;
- if (!read_file(path, &data)) {
- return false;
- }
-
- data.push_back('\n'); // TODO: fix parse_config.
- ParseData(path, data);
- for (const auto& sp : section_parsers_) {
- sp.second->EndFile(path);
- }
-
- LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
- return true;
-}
-
-bool Parser::ParseConfigDir(const std::string& path) {
- LOG(INFO) << "Parsing directory " << path << "...";
- std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
- if (!config_dir) {
- PLOG(ERROR) << "Could not import directory '" << path << "'";
- return false;
- }
- dirent* current_file;
- std::vector<std::string> files;
- while ((current_file = readdir(config_dir.get()))) {
- // Ignore directories and only process regular files.
- if (current_file->d_type == DT_REG) {
- std::string current_path =
- android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
- files.emplace_back(current_path);
- }
- }
- // Sort first so we load files in a consistent order (bug 31996208)
- std::sort(files.begin(), files.end());
- for (const auto& file : files) {
- if (!ParseConfigFile(file)) {
- LOG(ERROR) << "could not import file '" << file << "'";
- }
- }
- return true;
-}
-
-bool Parser::ParseConfig(const std::string& path) {
- if (is_dir(path.c_str())) {
- return ParseConfigDir(path);
- }
- return ParseConfigFile(path);
-}
-
-void Parser::DumpState() const {
- ServiceManager::GetInstance().DumpState();
- ActionManager::GetInstance().DumpState();
-}
diff --git a/init/init_parser.h b/init/init_parser.h
deleted file mode 100644
index f66ba52..0000000
--- a/init/init_parser.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2010 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 _INIT_INIT_PARSER_H_
-#define _INIT_INIT_PARSER_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-class SectionParser {
-public:
- virtual ~SectionParser() {
- }
- virtual bool ParseSection(const std::vector<std::string>& args,
- std::string* err) = 0;
- virtual bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const = 0;
- virtual void EndSection() = 0;
- virtual void EndFile(const std::string& filename) = 0;
-};
-
-class Parser {
-public:
- static Parser& GetInstance();
- void DumpState() const;
- bool ParseConfig(const std::string& path);
- void AddSectionParser(const std::string& name,
- std::unique_ptr<SectionParser> parser);
- void set_is_system_etc_init_loaded(bool loaded) {
- is_system_etc_init_loaded_ = loaded;
- }
- void set_is_vendor_etc_init_loaded(bool loaded) {
- is_vendor_etc_init_loaded_ = loaded;
- }
- void set_is_odm_etc_init_loaded(bool loaded) {
- is_odm_etc_init_loaded_ = loaded;
- }
- bool is_system_etc_init_loaded() { return is_system_etc_init_loaded_; }
- bool is_vendor_etc_init_loaded() { return is_vendor_etc_init_loaded_; }
- bool is_odm_etc_init_loaded() { return is_odm_etc_init_loaded_; }
-
-private:
- Parser();
-
- void ParseData(const std::string& filename, const std::string& data);
- bool ParseConfigFile(const std::string& path);
- bool ParseConfigDir(const std::string& path);
-
- std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
- bool is_system_etc_init_loaded_ = false;
- bool is_vendor_etc_init_loaded_ = false;
- bool is_odm_etc_init_loaded_ = false;
-};
-
-#endif
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
deleted file mode 100644
index 52aaa37..0000000
--- a/init/init_parser_test.cpp
+++ /dev/null
@@ -1,135 +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 "init_parser.h"
-
-#include "init.h"
-#include "service.h"
-#include "util.h"
-
-#include <errno.h>
-#include <gtest/gtest.h>
-
-#include <string>
-#include <vector>
-
-TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
- ServiceManager& sm = ServiceManager::GetInstance();
- std::vector<std::string> args;
- // Nothing.
- ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-
- // No arguments to 'exec'.
- args.push_back("exec");
- ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-
- // No command in "exec --".
- args.push_back("--");
- ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-}
-
-TEST(init_parser, make_exec_oneshot_service_too_many_supplementary_gids) {
- ServiceManager& sm = ServiceManager::GetInstance();
- std::vector<std::string> args;
- args.push_back("exec");
- args.push_back("seclabel");
- args.push_back("root"); // uid.
- args.push_back("root"); // gid.
- for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
- args.push_back("root"); // Supplementary gid.
- }
- args.push_back("--");
- args.push_back("/system/bin/id");
- ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-}
-
-static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid,
- bool gid, bool supplementary_gids) {
- ServiceManager& sm = ServiceManager::GetInstance();
- std::vector<std::string> args;
- args.push_back("exec");
- if (seclabel) {
- args.push_back("u:r:su:s0"); // seclabel
- if (uid) {
- args.push_back("log"); // uid
- if (gid) {
- args.push_back("shell"); // gid
- if (supplementary_gids) {
- args.push_back("system"); // supplementary gid 0
- args.push_back("adb"); // supplementary gid 1
- }
- }
- }
- }
- if (dash_dash) {
- args.push_back("--");
- }
- args.push_back("/system/bin/toybox");
- args.push_back("id");
- Service* svc = sm.MakeExecOneshotService(args);
- ASSERT_NE(nullptr, svc);
-
- if (seclabel) {
- ASSERT_EQ("u:r:su:s0", svc->seclabel());
- } else {
- ASSERT_EQ("", svc->seclabel());
- }
- if (uid) {
- ASSERT_EQ(decode_uid("log"), svc->uid());
- } else {
- ASSERT_EQ(0U, svc->uid());
- }
- if (gid) {
- ASSERT_EQ(decode_uid("shell"), svc->gid());
- } else {
- ASSERT_EQ(0U, svc->gid());
- }
- if (supplementary_gids) {
- ASSERT_EQ(2U, svc->supp_gids().size());
- ASSERT_EQ(decode_uid("system"), svc->supp_gids()[0]);
- ASSERT_EQ(decode_uid("adb"), svc->supp_gids()[1]);
- } else {
- ASSERT_EQ(0U, svc->supp_gids().size());
- }
-
- ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
- ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
- ASSERT_EQ("id", svc->args()[1]);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_everything) {
- Test_make_exec_oneshot_service(true, true, true, true, true);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid_gid) {
- Test_make_exec_oneshot_service(true, true, true, true, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid) {
- Test_make_exec_oneshot_service(true, true, true, false, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_seclabel) {
- Test_make_exec_oneshot_service(true, true, false, false, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_just_command) {
- Test_make_exec_oneshot_service(true, false, false, false, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_just_command_no_dash) {
- Test_make_exec_oneshot_service(false, false, false, false, false);
-}
diff --git a/init/init_test.cpp b/init/init_test.cpp
new file mode 100644
index 0000000..27659f9
--- /dev/null
+++ b/init/init_test.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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 <functional>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "action.h"
+#include "builtins.h"
+#include "import_parser.h"
+#include "keyword_map.h"
+#include "parser.h"
+#include "util.h"
+
+namespace android {
+namespace init {
+
+class TestFunctionMap : public KeywordMap<BuiltinFunction> {
+ public:
+ // Helper for argument-less functions
+ using BuiltinFunctionNoArgs = std::function<void(void)>;
+ void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
+ Add(name, 0, 0, [function](const std::vector<std::string>&) {
+ function();
+ return Success();
+ });
+ }
+
+ void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
+ const BuiltinFunction function) {
+ builtin_functions_[name] = make_tuple(min_parameters, max_parameters, function);
+ }
+
+ private:
+ Map builtin_functions_ = {};
+
+ const Map& map() const override { return builtin_functions_; }
+};
+
+using ActionManagerCommand = std::function<void(ActionManager&)>;
+
+void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
+ const std::vector<ActionManagerCommand>& commands) {
+ ActionManager am;
+
+ Action::set_function_map(&test_function_map);
+
+ Parser parser;
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+ parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
+
+ ASSERT_TRUE(parser.ParseConfig(init_script_file));
+
+ for (const auto& command : commands) {
+ command(am);
+ }
+
+ while (am.HasMoreCommands()) {
+ am.ExecuteOneCommand();
+ }
+}
+
+void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
+ const std::vector<ActionManagerCommand>& commands) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+ TestInit(tf.path, test_function_map, commands);
+}
+
+TEST(init, SimpleEventTrigger) {
+ bool expect_true = false;
+ std::string init_script =
+ R"init(
+on boot
+pass_test
+)init";
+
+ TestFunctionMap test_function_map;
+ test_function_map.Add("pass_test", [&expect_true]() { expect_true = true; });
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+
+ TestInitText(init_script, test_function_map, commands);
+
+ EXPECT_TRUE(expect_true);
+}
+
+TEST(init, EventTriggerOrder) {
+ std::string init_script =
+ R"init(
+on boot
+execute_first
+
+on boot && property:ro.hardware=*
+execute_second
+
+on boot
+execute_third
+
+)init";
+
+ int num_executed = 0;
+ TestFunctionMap test_function_map;
+ test_function_map.Add("execute_first", [&num_executed]() { EXPECT_EQ(0, num_executed++); });
+ test_function_map.Add("execute_second", [&num_executed]() { EXPECT_EQ(1, num_executed++); });
+ test_function_map.Add("execute_third", [&num_executed]() { EXPECT_EQ(2, num_executed++); });
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+
+ TestInitText(init_script, test_function_map, commands);
+}
+
+TEST(init, EventTriggerOrderMultipleFiles) {
+ // 6 total files, which should have their triggers executed in the following order:
+ // 1: start - original script parsed
+ // 2: first_import - immediately imported by first_script
+ // 3: dir_a - file named 'a.rc' in dir; dir is imported after first_import
+ // 4: a_import - file imported by dir_a
+ // 5: dir_b - file named 'b.rc' in dir
+ // 6: last_import - imported after dir is imported
+
+ TemporaryFile first_import;
+ ASSERT_TRUE(first_import.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 2", first_import.fd));
+
+ TemporaryFile dir_a_import;
+ ASSERT_TRUE(dir_a_import.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 4", dir_a_import.fd));
+
+ TemporaryFile last_import;
+ ASSERT_TRUE(last_import.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 6", last_import.fd));
+
+ TemporaryDir dir;
+ // clang-format off
+ std::string dir_a_script = "import " + std::string(dir_a_import.path) + "\n"
+ "on boot\n"
+ "execute 3";
+ // clang-format on
+ // WriteFile() ensures the right mode is set
+ ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script));
+
+ ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
+
+ // clang-format off
+ std::string start_script = "import " + std::string(first_import.path) + "\n"
+ "import " + std::string(dir.path) + "\n"
+ "import " + std::string(last_import.path) + "\n"
+ "on boot\n"
+ "execute 1";
+ // clang-format on
+ TemporaryFile start;
+ ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+ int num_executed = 0;
+ auto execute_command = [&num_executed](const std::vector<std::string>& args) {
+ EXPECT_EQ(2U, args.size());
+ EXPECT_EQ(++num_executed, std::stoi(args[1]));
+ return Success();
+ };
+
+ TestFunctionMap test_function_map;
+ test_function_map.Add("execute", 1, 1, execute_command);
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+
+ TestInit(start.path, test_function_map, commands);
+
+ EXPECT_EQ(6, num_executed);
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 5801ea8..e686ce1 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -14,20 +14,22 @@
* limitations under the License.
*/
-#include <errno.h>
+#include "keychords.h"
+
#include <fcntl.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/keychord.h>
#include <unistd.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include "init.h"
-#include "log.h"
-#include "service.h"
+
+namespace android {
+namespace init {
static struct input_keychord *keychords = 0;
static int keychords_count = 0;
@@ -77,10 +79,13 @@
// Only handle keychords if adb is enabled.
std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
if (adb_enabled == "running") {
- Service* svc = ServiceManager::GetInstance().FindServiceByKeychord(id);
+ Service* svc = ServiceList::GetInstance().FindService(id, &Service::keychord_id);
if (svc) {
- LOG(INFO) << "Starting service " << svc->name() << " from keychord " << id;
- svc->Start();
+ LOG(INFO) << "Starting service '" << svc->name() << "' from keychord " << id;
+ if (auto result = svc->Start(); !result) {
+ LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord " << id
+ << ": " << result.error();
+ }
} else {
LOG(ERROR) << "Service for keychord " << id << " not found";
}
@@ -90,7 +95,9 @@
}
void keychord_init() {
- ServiceManager::GetInstance().ForEachService(add_service_keycodes);
+ for (const auto& service : ServiceList::GetInstance()) {
+ add_service_keycodes(service.get());
+ }
// Nothing to do if no services require keychords.
if (!keychords) {
@@ -114,3 +121,6 @@
register_epoll_handler(keychord_fd, handle_keychord);
}
+
+} // namespace init
+} // namespace android
diff --git a/init/keychords.h b/init/keychords.h
index d2723b7..1c34098 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -17,9 +17,15 @@
#ifndef _INIT_KEYCHORDS_H_
#define _INIT_KEYCHORDS_H_
-struct service;
+#include "service.h"
-void add_service_keycodes(service*);
+namespace android {
+namespace init {
+
+void add_service_keycodes(Service* svc);
void keychord_init();
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/keyutils.h b/init/keyutils.h
deleted file mode 100644
index de01beb..0000000
--- a/init/keyutils.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.
- */
-
-/* Miniature version of a header-only keyutils.h (no library required) */
-
-#ifndef _INIT_KEYUTILS_H_
-#define _INIT_KEYUTILS_H_
-
-#ifndef KEYUTILS_H /* walk away if the _real_ one exists */
-
-#include <linux/keyctl.h>
-#include <stdarg.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
-static inline long keyctl(int cmd, ...) {
- va_list va;
- unsigned long arg2, arg3, arg4, arg5;
-
- va_start(va, cmd);
- arg2 = va_arg(va, unsigned long);
- arg3 = va_arg(va, unsigned long);
- arg4 = va_arg(va, unsigned long);
- arg5 = va_arg(va, unsigned long);
- va_end(va);
- return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
-}
-
-#endif
-
-#endif
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 693d82a..c95fc73 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -22,24 +22,31 @@
#include <android-base/stringprintf.h>
+#include "result.h"
+
+namespace android {
+namespace init {
+
template <typename Function>
class KeywordMap {
-public:
+ public:
using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
- using Map = const std::map<std::string, FunctionInfo>;
+ using Map = std::map<std::string, FunctionInfo>;
virtual ~KeywordMap() {
}
- const Function FindFunction(const std::string& keyword,
- size_t num_args,
- std::string* err) const {
+ const Result<Function> FindFunction(const std::vector<std::string>& args) const {
using android::base::StringPrintf;
+ if (args.empty()) return Error() << "Keyword needed, but not provided";
+
+ auto& keyword = args[0];
+ auto num_args = args.size() - 1;
+
auto function_info_it = map().find(keyword);
if (function_info_it == map().end()) {
- *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
- return nullptr;
+ return Error() << StringPrintf("Invalid keyword '%s'", keyword.c_str());
}
auto function_info = function_info_it->second;
@@ -47,31 +54,30 @@
auto min_args = std::get<0>(function_info);
auto max_args = std::get<1>(function_info);
if (min_args == max_args && num_args != min_args) {
- *err = StringPrintf("%s requires %zu argument%s",
- keyword.c_str(), min_args,
- (min_args > 1 || min_args == 0) ? "s" : "");
- return nullptr;
+ return Error() << StringPrintf("%s requires %zu argument%s", keyword.c_str(), min_args,
+ (min_args > 1 || min_args == 0) ? "s" : "");
}
if (num_args < min_args || num_args > max_args) {
if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
- *err = StringPrintf("%s requires at least %zu argument%s",
- keyword.c_str(), min_args,
- min_args > 1 ? "s" : "");
+ return Error() << StringPrintf("%s requires at least %zu argument%s",
+ keyword.c_str(), min_args, min_args > 1 ? "s" : "");
} else {
- *err = StringPrintf("%s requires between %zu and %zu arguments",
- keyword.c_str(), min_args, max_args);
+ return Error() << StringPrintf("%s requires between %zu and %zu arguments",
+ keyword.c_str(), min_args, max_args);
}
- return nullptr;
}
return std::get<Function>(function_info);
}
-private:
-//Map of keyword ->
-//(minimum number of arguments, maximum number of arguments, function pointer)
- virtual Map& map() const = 0;
+ private:
+ // Map of keyword ->
+ // (minimum number of arguments, maximum number of arguments, function pointer)
+ virtual const Map& map() const = 0;
};
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/log.cpp b/init/log.cpp
index 0615730..6198fc2 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -19,16 +19,45 @@
#include <fcntl.h>
#include <linux/audit.h>
#include <string.h>
+#include <unistd.h>
#include <android-base/logging.h>
+#include <cutils/android_reboot.h>
#include <selinux/selinux.h>
+#include "reboot.h"
+
+namespace android {
+namespace init {
+
+static void InitAborter(const char* abort_message) {
+ // When init forks, it continues to use this aborter for LOG(FATAL), but we want children to
+ // simply abort instead of trying to reboot the system.
+ if (getpid() != 1) {
+ android::base::DefaultAborter(abort_message);
+ return;
+ }
+
+ // DoReboot() does a lot to try to shutdown the system cleanly. If something happens to call
+ // LOG(FATAL) in the shutdown path, we want to catch this and immediately use the syscall to
+ // reboot instead of recursing here.
+ static bool has_aborted = false;
+ if (!has_aborted) {
+ has_aborted = true;
+ // Do not queue "shutdown" trigger since we want to shutdown immediately and it's not likely
+ // that we can even run the ActionQueue at this point.
+ DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
+ } else {
+ RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+ }
+}
+
void InitKernelLogging(char* argv[]) {
// Make stdin/stdout/stderr all point to /dev/null.
int fd = open("/sys/fs/selinux/null", O_RDWR);
if (fd == -1) {
int saved_errno = errno;
- android::base::InitLogging(argv, &android::base::KernelLogger);
+ android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
errno = saved_errno;
PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
}
@@ -37,7 +66,7 @@
dup2(fd, 2);
if (fd > 2) close(fd);
- android::base::InitLogging(argv, &android::base::KernelLogger);
+ android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
}
int selinux_klog_callback(int type, const char *fmt, ...) {
@@ -55,3 +84,6 @@
android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
return 0;
}
+
+} // namespace init
+} // namespace android
diff --git a/init/log.h b/init/log.h
index 8fa6d74..5a4eba6 100644
--- a/init/log.h
+++ b/init/log.h
@@ -17,10 +17,16 @@
#ifndef _INIT_LOG_H_
#define _INIT_LOG_H_
-#include <android-base/logging.h>
+#include <sys/cdefs.h>
+
+namespace android {
+namespace init {
void InitKernelLogging(char* argv[]);
int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/parser.cpp b/init/parser.cpp
index 45862b7..8a4e798 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -1,141 +1,155 @@
+/*
+ * Copyright (C) 2010 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 "parser.h"
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
+#include <dirent.h>
-#include "log.h"
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
-void parse_error(struct parse_state *state, const char *fmt, ...)
-{
- va_list ap;
- char buf[128];
- int off;
+#include "tokenizer.h"
+#include "util.h"
- snprintf(buf, sizeof(buf), "%s: %d: ", state->filename, state->line);
- buf[127] = 0;
- off = strlen(buf);
+namespace android {
+namespace init {
- va_start(ap, fmt);
- vsnprintf(buf + off, 128 - off, fmt, ap);
- va_end(ap);
- buf[127] = 0;
- LOG(ERROR) << buf;
+Parser::Parser() {}
+
+void Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser) {
+ section_parsers_[name] = std::move(parser);
}
-int next_token(struct parse_state *state)
-{
- char *x = state->ptr;
- char *s;
-
- if (state->nexttoken) {
- int t = state->nexttoken;
- state->nexttoken = 0;
- return t;
- }
-
- for (;;) {
- switch (*x) {
- case 0:
- state->ptr = x;
- return T_EOF;
- case '\n':
- x++;
- state->ptr = x;
- return T_NEWLINE;
- case ' ':
- case '\t':
- case '\r':
- x++;
- continue;
- case '#':
- while (*x && (*x != '\n')) x++;
- if (*x == '\n') {
- state->ptr = x+1;
- return T_NEWLINE;
- } else {
- state->ptr = x;
- return T_EOF;
- }
- default:
- goto text;
- }
- }
-
-textdone:
- state->ptr = x;
- *s = 0;
- return T_TEXT;
-text:
- state->text = s = x;
-textresume:
- for (;;) {
- switch (*x) {
- case 0:
- goto textdone;
- case ' ':
- case '\t':
- case '\r':
- x++;
- goto textdone;
- case '\n':
- state->nexttoken = T_NEWLINE;
- x++;
- goto textdone;
- case '"':
- x++;
- for (;;) {
- switch (*x) {
- case 0:
- /* unterminated quoted thing */
- state->ptr = x;
- return T_EOF;
- case '"':
- x++;
- goto textresume;
- default:
- *s++ = *x++;
- }
- }
- break;
- case '\\':
- x++;
- switch (*x) {
- case 0:
- goto textdone;
- case 'n':
- *s++ = '\n';
- break;
- case 'r':
- *s++ = '\r';
- break;
- case 't':
- *s++ = '\t';
- break;
- case '\\':
- *s++ = '\\';
- break;
- case '\r':
- /* \ <cr> <lf> -> line continuation */
- if (x[1] != '\n') {
- x++;
- continue;
- }
- case '\n':
- /* \ <lf> -> line continuation */
- state->line++;
- x++;
- /* eat any extra whitespace */
- while((*x == ' ') || (*x == '\t')) x++;
- continue;
- default:
- /* unknown escape -- just copy */
- *s++ = *x++;
- }
- continue;
- default:
- *s++ = *x++;
- }
- }
- return T_EOF;
+void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
+ line_callbacks_.emplace_back(prefix, callback);
}
+
+void Parser::ParseData(const std::string& filename, const std::string& data) {
+ // TODO: Use a parser with const input and remove this copy
+ std::vector<char> data_copy(data.begin(), data.end());
+ data_copy.push_back('\0');
+
+ parse_state state;
+ state.line = 0;
+ state.ptr = &data_copy[0];
+ state.nexttoken = 0;
+
+ SectionParser* section_parser = nullptr;
+ std::vector<std::string> args;
+
+ for (;;) {
+ switch (next_token(&state)) {
+ case T_EOF:
+ if (section_parser) section_parser->EndSection();
+ return;
+ case T_NEWLINE:
+ state.line++;
+ if (args.empty()) break;
+ // If we have a line matching a prefix we recognize, call its callback and unset any
+ // current section parsers. This is meant for /sys/ and /dev/ line entries for
+ // uevent.
+ for (const auto& [prefix, callback] : line_callbacks_) {
+ if (android::base::StartsWith(args[0], prefix.c_str())) {
+ if (section_parser) section_parser->EndSection();
+
+ if (auto result = callback(std::move(args)); !result) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
+ }
+ section_parser = nullptr;
+ break;
+ }
+ }
+ if (section_parsers_.count(args[0])) {
+ if (section_parser) section_parser->EndSection();
+ section_parser = section_parsers_[args[0]].get();
+ if (auto result =
+ section_parser->ParseSection(std::move(args), filename, state.line);
+ !result) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
+ section_parser = nullptr;
+ }
+ } else if (section_parser) {
+ if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
+ !result) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
+ }
+ }
+ args.clear();
+ break;
+ case T_TEXT:
+ args.emplace_back(state.text);
+ break;
+ }
+ }
+}
+
+bool Parser::ParseConfigFile(const std::string& path) {
+ LOG(INFO) << "Parsing file " << path << "...";
+ android::base::Timer t;
+ auto config_contents = ReadFile(path);
+ if (!config_contents) {
+ LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
+ return false;
+ }
+
+ config_contents->push_back('\n'); // TODO: fix parse_config.
+ ParseData(path, *config_contents);
+ for (const auto& [section_name, section_parser] : section_parsers_) {
+ section_parser->EndFile();
+ }
+
+ LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
+ return true;
+}
+
+bool Parser::ParseConfigDir(const std::string& path) {
+ LOG(INFO) << "Parsing directory " << path << "...";
+ std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
+ if (!config_dir) {
+ PLOG(ERROR) << "Could not import directory '" << path << "'";
+ return false;
+ }
+ dirent* current_file;
+ std::vector<std::string> files;
+ while ((current_file = readdir(config_dir.get()))) {
+ // Ignore directories and only process regular files.
+ if (current_file->d_type == DT_REG) {
+ std::string current_path =
+ android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
+ files.emplace_back(current_path);
+ }
+ }
+ // Sort first so we load files in a consistent order (bug 31996208)
+ std::sort(files.begin(), files.end());
+ for (const auto& file : files) {
+ if (!ParseConfigFile(file)) {
+ LOG(ERROR) << "could not import file '" << file << "'";
+ }
+ }
+ return true;
+}
+
+bool Parser::ParseConfig(const std::string& path) {
+ if (is_dir(path.c_str())) {
+ return ParseConfigDir(path);
+ }
+ return ParseConfigFile(path);
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/parser.h b/init/parser.h
index 95e1164..4ab24a4 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -14,27 +14,79 @@
* limitations under the License.
*/
-#ifndef PARSER_H_
-#define PARSER_H_
+#ifndef _INIT_PARSER_H_
+#define _INIT_PARSER_H_
-#define T_EOF 0
-#define T_TEXT 1
-#define T_NEWLINE 2
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
-struct parse_state
-{
- char *ptr;
- char *text;
- int line;
- int nexttoken;
- void *context;
- void (*parse_line)(struct parse_state *state, int nargs, char **args);
- const char *filename;
- void *priv;
+#include "result.h"
+
+// SectionParser is an interface that can parse a given 'section' in init.
+//
+// You can implement up to 4 functions below, with ParseSection() being mandatory.
+// The first two function return bool with false indicating a failure and has a std::string* err
+// parameter into which an error string can be written. It will be reported along with the
+// filename and line number of where the error occurred.
+//
+// 1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
+// int line, std::string* err)
+// This function is called when a section is first encountered.
+//
+// 2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
+// This function is called on each subsequent line until the next section is encountered.
+//
+// 3) bool EndSection()
+// This function is called either when a new section is found or at the end of the file.
+// It indicates that parsing of the current section is complete and any relevant objects should
+// be committed.
+//
+// 4) bool EndFile()
+// This function is called at the end of the file.
+// It indicates that the parsing has completed and any relevant objects should be committed.
+
+namespace android {
+namespace init {
+
+class SectionParser {
+ public:
+ virtual ~SectionParser() {}
+ virtual Result<Success> ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) = 0;
+ virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
+ virtual void EndSection(){};
+ virtual void EndFile(){};
};
-void dump_parser_state(void);
-int next_token(struct parse_state *state);
-void parse_error(struct parse_state *state, const char *fmt, ...);
+class Parser {
+ public:
+ // LineCallback is the type for callbacks that can parse a line starting with a given prefix.
+ //
+ // They take the form of bool Callback(std::vector<std::string>&& args, std::string* err)
+ //
+ // Similar to ParseSection() and ParseLineSection(), this function returns bool with false
+ // indicating a failure and has an std::string* err parameter into which an error string can
+ // be written.
+ using LineCallback = std::function<Result<Success>(std::vector<std::string>&&)>;
-#endif /* PARSER_H_ */
+ Parser();
+
+ bool ParseConfig(const std::string& path);
+ void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
+ void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+
+ private:
+ void ParseData(const std::string& filename, const std::string& data);
+ bool ParseConfigFile(const std::string& path);
+ bool ParseConfigDir(const std::string& path);
+
+ std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
+ std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
+};
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
new file mode 100644
index 0000000..66fa011
--- /dev/null
+++ b/init/persistent_properties.cpp
@@ -0,0 +1,341 @@
+/*
+ * 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 "persistent_properties.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/system_properties.h>
+#include <sys/types.h>
+
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+using android::base::ReadFdToString;
+using android::base::StartsWith;
+using android::base::WriteStringToFd;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+std::string persistent_property_filename = "/data/property/persistent_properties";
+
+namespace {
+
+constexpr const uint32_t kMagic = 0x8495E0B4;
+constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
+
+Result<std::vector<std::pair<std::string, std::string>>> LoadLegacyPersistentProperties() {
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
+ if (!dir) {
+ return ErrnoError() << "Unable to open persistent property directory \""
+ << kLegacyPersistentPropertyDir << "\"";
+ }
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties;
+ dirent* entry;
+ while ((entry = readdir(dir.get())) != nullptr) {
+ if (!StartsWith(entry->d_name, "persist.")) {
+ continue;
+ }
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
+
+ unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW));
+ if (fd == -1) {
+ PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
+ continue;
+ }
+
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
+ continue;
+ }
+
+ // File must not be accessible to others, be owned by root/root, and
+ // not be a hard link to any other file.
+ if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 ||
+ sb.st_nlink != 1) {
+ PLOG(ERROR) << "skipping insecure property file " << entry->d_name
+ << " (uid=" << sb.st_uid << " gid=" << sb.st_gid << " nlink=" << sb.st_nlink
+ << " mode=" << std::oct << sb.st_mode << ")";
+ continue;
+ }
+
+ std::string value;
+ if (ReadFdToString(fd, &value)) {
+ persistent_properties.emplace_back(entry->d_name, value);
+ } else {
+ PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
+ }
+ }
+ return persistent_properties;
+}
+
+void RemoveLegacyPersistentPropertyFiles() {
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
+ if (!dir) {
+ PLOG(ERROR) << "Unable to open persistent property directory \""
+ << kLegacyPersistentPropertyDir << "\"";
+ return;
+ }
+
+ dirent* entry;
+ while ((entry = readdir(dir.get())) != nullptr) {
+ if (!StartsWith(entry->d_name, "persist.")) {
+ continue;
+ }
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
+ unlinkat(dirfd(dir.get()), entry->d_name, 0);
+ }
+}
+
+std::vector<std::pair<std::string, std::string>> LoadPersistentPropertiesFromMemory() {
+ std::vector<std::pair<std::string, std::string>> properties;
+ __system_property_foreach(
+ [](const prop_info* pi, void* cookie) {
+ __system_property_read_callback(
+ pi,
+ [](void* cookie, const char* name, const char* value, unsigned serial) {
+ if (StartsWith(name, "persist.")) {
+ auto properties =
+ reinterpret_cast<std::vector<std::pair<std::string, std::string>>*>(
+ cookie);
+ properties->emplace_back(name, value);
+ }
+ },
+ cookie);
+ },
+ &properties);
+ return properties;
+}
+
+class PersistentPropertyFileParser {
+ public:
+ PersistentPropertyFileParser(const std::string& contents) : contents_(contents), position_(0) {}
+ Result<std::vector<std::pair<std::string, std::string>>> Parse();
+
+ private:
+ Result<std::string> ReadString();
+ Result<uint32_t> ReadUint32();
+
+ const std::string& contents_;
+ size_t position_;
+};
+
+Result<std::vector<std::pair<std::string, std::string>>> PersistentPropertyFileParser::Parse() {
+ std::vector<std::pair<std::string, std::string>> result;
+
+ if (auto magic = ReadUint32(); magic) {
+ if (*magic != kMagic) {
+ return Error() << "Magic value '0x" << std::hex << *magic
+ << "' does not match expected value '0x" << kMagic << "'";
+ }
+ } else {
+ return Error() << "Could not read magic value: " << magic.error();
+ }
+
+ if (auto version = ReadUint32(); version) {
+ if (*version != 1) {
+ return Error() << "Version '" << *version
+ << "' does not match any compatible version: (1)";
+ }
+ } else {
+ return Error() << "Could not read version: " << version.error();
+ }
+
+ auto num_properties = ReadUint32();
+ if (!num_properties) {
+ return Error() << "Could not read num_properties: " << num_properties.error();
+ }
+
+ while (position_ < contents_.size()) {
+ auto key = ReadString();
+ if (!key) {
+ return Error() << "Could not read key: " << key.error();
+ }
+ if (!StartsWith(*key, "persist.")) {
+ return Error() << "Property '" << *key << "' does not starts with 'persist.'";
+ }
+ auto value = ReadString();
+ if (!value) {
+ return Error() << "Could not read value: " << value.error();
+ }
+ result.emplace_back(*key, *value);
+ }
+
+ if (result.size() != *num_properties) {
+ return Error() << "Mismatch of number of persistent properties read, " << result.size()
+ << " and number of persistent properties expected, " << *num_properties;
+ }
+
+ return result;
+}
+
+Result<std::string> PersistentPropertyFileParser::ReadString() {
+ auto string_length = ReadUint32();
+ if (!string_length) {
+ return Error() << "Could not read size for string";
+ }
+
+ if (position_ + *string_length > contents_.size()) {
+ return Error() << "String size would cause it to overflow the input buffer";
+ }
+ auto result = std::string(contents_, position_, *string_length);
+ position_ += *string_length;
+ return result;
+}
+
+Result<uint32_t> PersistentPropertyFileParser::ReadUint32() {
+ if (position_ + 3 > contents_.size()) {
+ return Error() << "Input buffer not large enough to read uint32_t";
+ }
+ uint32_t result = *reinterpret_cast<const uint32_t*>(&contents_[position_]);
+ position_ += sizeof(uint32_t);
+ return result;
+}
+
+} // namespace
+
+Result<std::vector<std::pair<std::string, std::string>>> LoadPersistentPropertyFile() {
+ const std::string temp_filename = persistent_property_filename + ".tmp";
+ if (access(temp_filename.c_str(), F_OK) == 0) {
+ LOG(INFO)
+ << "Found temporary property file while attempting to persistent system properties"
+ " a previous persistent property write may have failed";
+ unlink(temp_filename.c_str());
+ }
+ auto file_contents = ReadFile(persistent_property_filename);
+ if (!file_contents) {
+ return Error() << "Unable to read persistent property file: " << file_contents.error();
+ }
+ auto parsed_contents = PersistentPropertyFileParser(*file_contents).Parse();
+ if (!parsed_contents) {
+ // If the file cannot be parsed, then we don't have any recovery mechanisms, so we delete
+ // it to allow for future writes to take place successfully.
+ unlink(persistent_property_filename.c_str());
+ return Error() << "Unable to parse persistent property file: " << parsed_contents.error();
+ }
+ return parsed_contents;
+}
+
+std::string GenerateFileContents(
+ const std::vector<std::pair<std::string, std::string>>& persistent_properties) {
+ std::string result;
+
+ uint32_t magic = kMagic;
+ result.append(reinterpret_cast<char*>(&magic), sizeof(uint32_t));
+
+ uint32_t version = 1;
+ result.append(reinterpret_cast<char*>(&version), sizeof(uint32_t));
+
+ uint32_t num_properties = persistent_properties.size();
+ result.append(reinterpret_cast<char*>(&num_properties), sizeof(uint32_t));
+
+ for (const auto& [key, value] : persistent_properties) {
+ uint32_t key_length = key.length();
+ result.append(reinterpret_cast<char*>(&key_length), sizeof(uint32_t));
+ result.append(key);
+ uint32_t value_length = value.length();
+ result.append(reinterpret_cast<char*>(&value_length), sizeof(uint32_t));
+ result.append(value);
+ }
+ return result;
+}
+
+Result<Success> WritePersistentPropertyFile(
+ const std::vector<std::pair<std::string, std::string>>& persistent_properties) {
+ auto file_contents = GenerateFileContents(persistent_properties);
+
+ const std::string temp_filename = persistent_property_filename + ".tmp";
+ unique_fd fd(TEMP_FAILURE_RETRY(
+ open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
+ if (fd == -1) {
+ return ErrnoError() << "Could not open temporary properties file";
+ }
+ if (!WriteStringToFd(file_contents, fd)) {
+ return ErrnoError() << "Unable to write file contents";
+ }
+ fsync(fd);
+ fd.reset();
+
+ if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) {
+ int saved_errno = errno;
+ unlink(temp_filename.c_str());
+ return Error(saved_errno) << "Unable to rename persistent property file";
+ }
+ return Success();
+}
+
+// Persistent properties are not written often, so we rather not keep any data in memory and read
+// then rewrite the persistent property file for each update.
+void WritePersistentProperty(const std::string& name, const std::string& value) {
+ auto persistent_properties = LoadPersistentPropertyFile();
+ if (!persistent_properties) {
+ LOG(ERROR) << "Recovering persistent properties from memory: "
+ << persistent_properties.error();
+ persistent_properties = LoadPersistentPropertiesFromMemory();
+ }
+ auto it = std::find_if(persistent_properties->begin(), persistent_properties->end(),
+ [&name](const auto& entry) { return entry.first == name; });
+ if (it != persistent_properties->end()) {
+ *it = {name, value};
+ } else {
+ persistent_properties->emplace_back(name, value);
+ }
+
+ if (auto result = WritePersistentPropertyFile(*persistent_properties); !result) {
+ LOG(ERROR) << "Could not store persistent property: " << result.error();
+ }
+}
+
+std::vector<std::pair<std::string, std::string>> LoadPersistentProperties() {
+ auto persistent_properties = LoadPersistentPropertyFile();
+
+ if (!persistent_properties) {
+ LOG(ERROR) << "Could not load single persistent property file, trying legacy directory: "
+ << persistent_properties.error();
+ persistent_properties = LoadLegacyPersistentProperties();
+ if (!persistent_properties) {
+ LOG(ERROR) << "Unable to load legacy persistent properties: "
+ << persistent_properties.error();
+ return {};
+ }
+ if (auto result = WritePersistentPropertyFile(*persistent_properties); result) {
+ RemoveLegacyPersistentPropertyFiles();
+ } else {
+ LOG(ERROR) << "Unable to write single persistent property file: " << result.error();
+ // Fall through so that we still set the properties that we've read.
+ }
+ }
+
+ return *persistent_properties;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/persistent_properties.h b/init/persistent_properties.h
new file mode 100644
index 0000000..d84d9db
--- /dev/null
+++ b/init/persistent_properties.h
@@ -0,0 +1,42 @@
+/*
+ * 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 _INIT_PERSISTENT_PROPERTIES_H
+#define _INIT_PERSISTENT_PROPERTIES_H
+
+#include <string>
+#include <vector>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+std::vector<std::pair<std::string, std::string>> LoadPersistentProperties();
+void WritePersistentProperty(const std::string& name, const std::string& value);
+
+// Exposed only for testing
+Result<std::vector<std::pair<std::string, std::string>>> LoadPersistentPropertyFile();
+std::string GenerateFileContents(
+ const std::vector<std::pair<std::string, std::string>>& persistent_properties);
+Result<Success> WritePersistentPropertyFile(
+ const std::vector<std::pair<std::string, std::string>>& persistent_properties);
+extern std::string persistent_property_filename;
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
new file mode 100644
index 0000000..9ab5b22
--- /dev/null
+++ b/init/persistent_properties_test.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 "persistent_properties.h"
+
+#include <errno.h>
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "util.h"
+
+using namespace std::string_literals;
+
+namespace android {
+namespace init {
+
+TEST(persistent_properties, GeneratedContents) {
+ const std::vector<std::pair<std::string, std::string>> persistent_properties = {
+ {"persist.abc", ""},
+ {"persist.def", "test_success"},
+ };
+ auto generated_contents = GenerateFileContents(persistent_properties);
+
+ // Manually serialized contents below:
+ std::string file_contents;
+ // All values below are written and read as little endian.
+ // Add magic value: 0x8495E0B4
+ file_contents += "\xB4\xE0\x95\x84"s;
+ // Add version: 1
+ file_contents += "\x01\x00\x00\x00"s;
+ // Add number of properties: 2
+ file_contents += "\x02\x00\x00\x00"s;
+
+ // Add first key: persist.abc
+ file_contents += "\x0B\x00\x00\x00persist.abc"s;
+ // Add first value: (empty string)
+ file_contents += "\x00\x00\x00\x00"s;
+
+ // Add second key: persist.def
+ file_contents += "\x0B\x00\x00\x00persist.def"s;
+ // Add second value: test_success
+ file_contents += "\x0C\x00\x00\x00test_success"s;
+
+ EXPECT_EQ(file_contents, generated_contents);
+}
+
+TEST(persistent_properties, EndToEnd) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ persistent_property_filename = tf.path;
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties = {
+ {"persist.sys.locale", "en-US"},
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ {"persist.test.empty.value", ""},
+ {"persist.test.new.line", "abc\n\n\nabc"},
+ {"persist.test.numbers", "1234567890"},
+ {"persist.test.non.ascii", "\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F"},
+ // We don't currently allow for non-ascii keys for system properties, but this is a policy
+ // decision, not a technical limitation.
+ {"persist.\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F", "non-ascii-key"},
+ };
+
+ ASSERT_TRUE(WritePersistentPropertyFile(persistent_properties));
+
+ auto read_back_properties = LoadPersistentProperties();
+ EXPECT_EQ(persistent_properties, read_back_properties);
+}
+
+TEST(persistent_properties, BadMagic) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ persistent_property_filename = tf.path;
+
+ ASSERT_TRUE(WriteFile(tf.path, "ab"));
+
+ auto read_back_properties = LoadPersistentPropertyFile();
+
+ ASSERT_FALSE(read_back_properties);
+ EXPECT_EQ(
+ "Unable to parse persistent property file: Could not read magic value: Input buffer not "
+ "large enough to read uint32_t",
+ read_back_properties.error_string());
+
+ ASSERT_TRUE(WriteFile(tf.path, "\xFF\xFF\xFF\xFF"));
+
+ read_back_properties = LoadPersistentPropertyFile();
+
+ ASSERT_FALSE(read_back_properties);
+ EXPECT_EQ(
+ "Unable to parse persistent property file: Magic value '0xffffffff' does not match "
+ "expected value '0x8495e0b4'",
+ read_back_properties.error_string());
+}
+
+TEST(persistent_properties, AddProperty) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ persistent_property_filename = tf.path;
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties = {
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ };
+ ASSERT_TRUE(WritePersistentPropertyFile(persistent_properties));
+
+ WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ {"persist.sys.locale", "pt-BR"},
+ };
+
+ auto read_back_properties = LoadPersistentProperties();
+ EXPECT_EQ(persistent_properties_expected, read_back_properties);
+}
+
+TEST(persistent_properties, UpdateProperty) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ persistent_property_filename = tf.path;
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties = {
+ {"persist.sys.locale", "en-US"},
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ };
+ ASSERT_TRUE(WritePersistentPropertyFile(persistent_properties));
+
+ WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
+ {"persist.sys.locale", "pt-BR"},
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ };
+
+ auto read_back_properties = LoadPersistentProperties();
+ EXPECT_EQ(persistent_properties_expected, read_back_properties);
+}
+
+TEST(persistent_properties, UpdatePropertyBadParse) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ persistent_property_filename = tf.path;
+
+ ASSERT_TRUE(WriteFile(tf.path, "ab"));
+
+ WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+ auto read_back_properties = LoadPersistentProperties();
+ EXPECT_GT(read_back_properties.size(), 0U);
+
+ auto it = std::find_if(
+ read_back_properties.begin(), read_back_properties.end(), [](const auto& entry) {
+ return entry.first == "persist.sys.locale" && entry.second == "pt-BR";
+ });
+ EXPECT_FALSE(it == read_back_properties.end());
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 73954fe..5f5ed40 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -14,57 +14,63 @@
* limitations under the License.
*/
+#include "property_service.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
#include <inttypes.h>
+#include <limits.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
#include <string.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <dirent.h>
-#include <limits.h>
-#include <errno.h>
+#include <sys/mman.h>
#include <sys/poll.h>
-
-#include <memory>
-#include <vector>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <sys/mman.h>
+#include <memory>
+#include <queue>
+#include <vector>
-#include <selinux/android.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-
+#include <android-base/chrono_utils.h>
#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 <bootimg.h>
#include <fs_mgr.h>
-#include "bootimg.h"
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
-#include "property_service.h"
#include "init.h"
+#include "persistent_properties.h"
#include "util.h"
-#include "log.h"
using android::base::StringPrintf;
+using android::base::Timer;
-#define PERSISTENT_PROPERTY_DIR "/data/property"
#define RECOVERY_MOUNT_POINT "/recovery"
-static int persistent_properties_loaded = 0;
+namespace android {
+namespace init {
+
+static bool persistent_properties_loaded = false;
static int property_set_fd = -1;
+static struct selabel_handle* sehandle_prop;
+
void property_init() {
if (__system_property_area_init()) {
LOG(ERROR) << "Failed to initialize property area";
@@ -115,29 +121,6 @@
return check_mac_perms(ctl_name, sctx, cr);
}
-static void write_persistent_property(const char *name, const char *value)
-{
- char tempPath[PATH_MAX];
- char path[PATH_MAX];
- int fd;
-
- snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR);
- fd = mkstemp(tempPath);
- if (fd < 0) {
- PLOG(ERROR) << "Unable to write persistent property to temp file " << tempPath;
- return;
- }
- write(fd, value, strlen(value));
- fsync(fd);
- close(fd);
-
- snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
- if (rename(tempPath, path)) {
- PLOG(ERROR) << "Unable to rename persistent property file " << tempPath << " to " << path;
- unlink(tempPath);
- }
-}
-
bool is_legal_property_name(const std::string& name) {
size_t namelen = name.size();
@@ -163,7 +146,7 @@
return true;
}
-uint32_t property_set(const std::string& name, const std::string& value) {
+static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
size_t valuelen = value.size();
if (!is_legal_property_name(name)) {
@@ -177,12 +160,6 @@
return PROP_ERROR_INVALID_VALUE;
}
- if (name == "selinux.restorecon_recursive" && valuelen > 0) {
- if (restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
- LOG(ERROR) << "Failed to restorecon_recursive " << value;
- }
- }
-
prop_info* pi = (prop_info*) __system_property_find(name.c_str());
if (pi != nullptr) {
// ro.* properties are actually "write-once".
@@ -205,12 +182,91 @@
// Don't write properties to disk until after we have read all default
// properties to prevent them from being overwritten by default values.
if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
- write_persistent_property(name.c_str(), value.c_str());
+ WritePersistentProperty(name, value);
}
property_changed(name, value);
return PROP_SUCCESS;
}
+typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&);
+
+struct PropertyChildInfo {
+ pid_t pid;
+ PropertyAsyncFunc func;
+ std::string name;
+ std::string value;
+};
+
+static std::queue<PropertyChildInfo> property_children;
+
+static void PropertyChildLaunch() {
+ auto& info = property_children.front();
+ pid_t pid = fork();
+ if (pid < 0) {
+ LOG(ERROR) << "Failed to fork for property_set_async";
+ while (!property_children.empty()) {
+ property_children.pop();
+ }
+ return;
+ }
+ if (pid != 0) {
+ info.pid = pid;
+ } else {
+ if (info.func(info.name, info.value) != 0) {
+ LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
+ << "\") failed";
+ }
+ exit(0);
+ }
+}
+
+bool PropertyChildReap(pid_t pid) {
+ if (property_children.empty()) {
+ return false;
+ }
+ auto& info = property_children.front();
+ if (info.pid != pid) {
+ return false;
+ }
+ if (PropertySetImpl(info.name, info.value) != PROP_SUCCESS) {
+ LOG(ERROR) << "Failed to set async property " << info.name;
+ }
+ property_children.pop();
+ if (!property_children.empty()) {
+ PropertyChildLaunch();
+ }
+ return true;
+}
+
+static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
+ PropertyAsyncFunc func) {
+ if (value.empty()) {
+ return PropertySetImpl(name, value);
+ }
+
+ PropertyChildInfo info;
+ info.func = func;
+ info.name = name;
+ info.value = value;
+ property_children.push(info);
+ if (property_children.size() == 1) {
+ PropertyChildLaunch();
+ }
+ return PROP_SUCCESS;
+}
+
+static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) {
+ return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+}
+
+uint32_t property_set(const std::string& name, const std::string& value) {
+ if (name == "selinux.restorecon_recursive") {
+ return PropertySetAsync(name, value, RestoreconRecursiveAsync);
+ }
+
+ return PropertySetImpl(name, value);
+}
+
class SocketConnection {
public:
SocketConnection(int socket, const struct ucred& cred)
@@ -277,7 +333,7 @@
while (*timeout_ms > 0) {
Timer timer;
int nr = poll(ufds, 1, *timeout_ms);
- uint64_t millis = timer.duration_ms();
+ uint64_t millis = timer.duration().count();
*timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis;
if (nr > 0) {
@@ -364,6 +420,20 @@
}
} else {
if (check_mac_perms(name, source_ctx, &cr)) {
+ // sys.powerctl is a special property that is used to make the device reboot. We want to log
+ // any process that sets this property to be able to accurately blame the cause of a shutdown.
+ if (name == "sys.powerctl") {
+ std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
+ std::string process_cmdline;
+ std::string process_log_string;
+ if (android::base::ReadFileToString(cmdline_path, &process_cmdline)) {
+ // Since cmdline is null deliminated, .c_str() conveniently gives us just the process path.
+ process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
+ }
+ LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
+ << process_log_string;
+ }
+
uint32_t result = property_set(name, value);
if (!legacy_protocol) {
socket.SendUint32(result);
@@ -444,7 +514,7 @@
}
}
-static bool load_properties_from_file(const char *, const char *);
+static void load_properties_from_file(const char *, const char *);
/*
* Filter is used to decide which properties to load: NULL loads all keys,
@@ -508,72 +578,17 @@
// Filter is used to decide which properties to load: NULL loads all keys,
// "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
-static bool load_properties_from_file(const char* filename, const char* filter) {
+static void load_properties_from_file(const char* filename, const char* filter) {
Timer t;
- std::string data;
- if (!read_file(filename, &data)) {
- PLOG(WARNING) << "Couldn't load properties from " << filename;
- return false;
- }
- data.push_back('\n');
- load_properties(&data[0], filter);
- LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
- return true;
-}
-
-static void load_persistent_properties() {
- persistent_properties_loaded = 1;
-
- std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(PERSISTENT_PROPERTY_DIR), closedir);
- if (!dir) {
- PLOG(ERROR) << "Unable to open persistent property directory \""
- << PERSISTENT_PROPERTY_DIR << "\"";
+ auto file_contents = ReadFile(filename);
+ if (!file_contents) {
+ PLOG(WARNING) << "Couldn't load property file '" << filename
+ << "': " << file_contents.error();
return;
}
-
- struct dirent* entry;
- while ((entry = readdir(dir.get())) != NULL) {
- if (strncmp("persist.", entry->d_name, strlen("persist."))) {
- continue;
- }
- if (entry->d_type != DT_REG) {
- continue;
- }
-
- // Open the file and read the property value.
- int fd = openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW);
- if (fd == -1) {
- PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
- continue;
- }
-
- struct stat sb;
- if (fstat(fd, &sb) == -1) {
- PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
- close(fd);
- continue;
- }
-
- // File must not be accessible to others, be owned by root/root, and
- // not be a hard link to any other file.
- if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 || sb.st_nlink != 1) {
- PLOG(ERROR) << "skipping insecure property file " << entry->d_name
- << " (uid=" << sb.st_uid << " gid=" << sb.st_gid
- << " nlink=" << sb.st_nlink << " mode=" << std::oct << sb.st_mode << ")";
- close(fd);
- continue;
- }
-
- char value[PROP_VALUE_MAX];
- int length = read(fd, value, sizeof(value) - 1);
- if (length >= 0) {
- value[length] = 0;
- property_set(entry->d_name, value);
- } else {
- PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
- }
- close(fd);
- }
+ file_contents->push_back('\n');
+ load_properties(file_contents->data(), filter);
+ LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
}
// persist.sys.usb.config values can't be combined on build-time when property
@@ -593,13 +608,7 @@
}
void property_load_boot_defaults() {
- if (!load_properties_from_file("/system/etc/prop.default", NULL)) {
- // Try recovery path
- if (!load_properties_from_file("/prop.default", NULL)) {
- // Try legacy path
- load_properties_from_file("/default.prop", NULL);
- }
- }
+ load_properties_from_file("/default.prop", NULL);
load_properties_from_file("/odm/default.prop", NULL);
load_properties_from_file("/vendor/default.prop", NULL);
@@ -618,9 +627,24 @@
* has mounted /data.
*/
void load_persist_props(void) {
+ // Devices with FDE have load_persist_props called twice; the first time when the temporary
+ // /data partition is mounted and then again once /data is truly mounted. We do not want to
+ // read persistent properties from the temporary /data partition or mark persistent properties
+ // as having been loaded during the first call, so we return in that case.
+ std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
+ std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
+ if (crypto_state == "encrypted" && crypto_type == "block") {
+ static size_t num_calls = 0;
+ if (++num_calls == 1) return;
+ }
+
load_override_properties();
/* Read persistent properties after all default values have been loaded. */
- load_persistent_properties();
+ auto persistent_properties = LoadPersistentProperties();
+ for (const auto& [name, value] : persistent_properties) {
+ property_set(name, value);
+ }
+ persistent_properties_loaded = true;
property_set("ro.persistent_properties.ready", "true");
}
@@ -647,7 +671,7 @@
boot_img_hdr hdr;
if (android::base::ReadFully(fd, &hdr, sizeof(hdr))) {
std::string hex = bytes_to_hex(reinterpret_cast<uint8_t*>(hdr.id), sizeof(hdr.id));
- property_set("ro.recovery_id", hex.c_str());
+ property_set("ro.recovery_id", hex);
} else {
PLOG(ERROR) << "error reading /recovery";
}
@@ -663,11 +687,30 @@
load_recovery_id_prop();
}
+static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
+ property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
+
+ if (!d || !d->name || !d->cr) {
+ LOG(ERROR) << "AuditCallback invoked with null data arguments!";
+ return 0;
+ }
+
+ snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
+ d->cr->gid);
+ return 0;
+}
+
void start_property_service() {
+ sehandle_prop = selinux_android_prop_context_handle();
+
+ selinux_callback cb;
+ cb.func_audit = SelinuxAuditCallback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
property_set("ro.property_service.version", "2");
- property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- 0666, 0, 0, NULL);
+ property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ false, 0666, 0, 0, nullptr);
if (property_set_fd == -1) {
PLOG(ERROR) << "start_property_service socket creation failed";
exit(1);
@@ -677,3 +720,6 @@
register_epoll_handler(property_set_fd, handle_property_set_fd);
}
+
+} // namespace init
+} // namespace android
diff --git a/init/property_service.h b/init/property_service.h
index 994da63..a55e79c 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -17,16 +17,20 @@
#ifndef _INIT_PROPERTY_H
#define _INIT_PROPERTY_H
-#include <stddef.h>
#include <sys/socket.h>
-#include <sys/system_properties.h>
+
#include <string>
+namespace android {
+namespace init {
+
struct property_audit_data {
ucred *cr;
const char* name;
};
+extern bool PropertyChildReap(pid_t pid);
+
void property_init(void);
void property_load_boot_defaults(void);
void load_persist_props(void);
@@ -35,5 +39,7 @@
uint32_t property_set(const std::string& name, const std::string& value);
bool is_legal_property_name(const std::string& name);
+} // namespace init
+} // namespace android
#endif /* _INIT_PROPERTY_H */
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index 4d784aa..3a64e02 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -23,6 +23,9 @@
#include <gtest/gtest.h>
+namespace android {
+namespace init {
+
TEST(property_service, very_long_name_35166374) {
// Connect to the property service directly...
int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
@@ -46,3 +49,6 @@
ASSERT_EQ(static_cast<ssize_t>(sizeof(data)), send(fd, &data, sizeof(data), 0));
ASSERT_EQ(0, close(fd));
}
+
+} // namespace init
+} // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index aac1b5c..049c952 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -13,11 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+#include "reboot.h"
+
#include <dirent.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <mntent.h>
-#include <selinux/selinux.h>
+#include <sys/capability.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
@@ -29,11 +32,12 @@
#include <memory>
#include <set>
-#include <string>
#include <thread>
#include <vector>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -44,14 +48,19 @@
#include <fs_mgr.h>
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
+#include <selinux/selinux.h>
-#include "log.h"
+#include "capabilities.h"
+#include "init.h"
#include "property_service.h"
-#include "reboot.h"
#include "service.h"
-#include "util.h"
+#include "sigchld_handler.h"
using android::base::StringPrintf;
+using android::base::Timer;
+
+namespace android {
+namespace init {
// represents umount status during reboot / shutdown.
enum UmountStat {
@@ -156,12 +165,39 @@
}
static void LogShutdownTime(UmountStat stat, Timer* t) {
- LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration_ms()) << ":" << stat;
+ LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration().count()) << ":"
+ << stat;
}
-static void __attribute__((noreturn))
-RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
+bool IsRebootCapable() {
+ if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
+ PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
+ return true;
+ }
+
+ ScopedCaps caps(cap_get_proc());
+ if (!caps) {
+ PLOG(WARNING) << "cap_get_proc() failed";
+ return true;
+ }
+
+ cap_flag_value_t value = CAP_SET;
+ if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
+ PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
+ return true;
+ }
+ return value == CAP_SET;
+}
+
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
LOG(INFO) << "Reboot ending, jumping to kernel";
+
+ if (!IsRebootCapable()) {
+ // On systems where init does not have the capability of rebooting the
+ // device, just exit cleanly.
+ exit(0);
+ }
+
switch (cmd) {
case ANDROID_RB_POWEROFF:
reboot(RB_POWER_OFF);
@@ -177,7 +213,7 @@
break;
}
// In normal case, reboot should not return.
- PLOG(FATAL) << "reboot call returned";
+ PLOG(ERROR) << "reboot call returned";
abort();
}
@@ -197,7 +233,13 @@
LOG(INFO) << "mount entry " << mentry->mnt_fsname << ":" << mentry->mnt_dir << " opts "
<< mentry->mnt_opts << " type " << mentry->mnt_type;
} else if (MountEntry::IsBlockDevice(*mentry) && hasmntopt(mentry, "rw")) {
- blockDevPartitions->emplace(blockDevPartitions->begin(), *mentry);
+ std::string mount_dir(mentry->mnt_dir);
+ // These are R/O partitions changed to R/W after adb remount.
+ // Do not umount them as shutdown critical services may rely on them.
+ if (mount_dir != "/" && mount_dir != "/system" && mount_dir != "/vendor" &&
+ mount_dir != "/oem") {
+ blockDevPartitions->emplace(blockDevPartitions->begin(), *mentry);
+ }
} else if (MountEntry::IsEmulatedDevice(*mentry)) {
emulatedPartitions->emplace(emulatedPartitions->begin(), *mentry);
}
@@ -220,10 +262,8 @@
}
}
-static UmountStat UmountPartitions(int timeoutMs) {
+static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
Timer t;
- UmountStat stat = UMOUNT_STAT_TIMEOUT;
- int retry = 0;
/* data partition needs all pending writes to be completed and all emulated partitions
* umounted.If the current waiting is not good enough, give
* up and leave it to e2fsck after reboot to fix it.
@@ -235,25 +275,27 @@
return UMOUNT_STAT_ERROR;
}
if (block_devices.size() == 0) {
- stat = UMOUNT_STAT_SUCCESS;
- break;
+ return UMOUNT_STAT_SUCCESS;
}
- if ((timeoutMs < t.duration_ms()) && retry > 0) { // try umount at least once
- stat = UMOUNT_STAT_TIMEOUT;
- break;
+ bool unmount_done = true;
+ if (emulated_devices.size() > 0) {
+ unmount_done = std::all_of(emulated_devices.begin(), emulated_devices.end(),
+ [](auto& entry) { return entry.Umount(); });
+ if (unmount_done) {
+ sync();
+ }
}
- if (emulated_devices.size() > 0 &&
- std::all_of(emulated_devices.begin(), emulated_devices.end(),
- [](auto& entry) { return entry.Umount(); })) {
- sync();
+ unmount_done = std::all_of(block_devices.begin(), block_devices.end(),
+ [](auto& entry) { return entry.Umount(); }) &&
+ unmount_done;
+ if (unmount_done) {
+ return UMOUNT_STAT_SUCCESS;
}
- for (auto& entry : block_devices) {
- entry.Umount();
+ if ((timeout < t.duration())) { // try umount at least once
+ return UMOUNT_STAT_TIMEOUT;
}
- retry++;
std::this_thread::sleep_for(100ms);
}
- return stat;
}
static void KillAllProcesses() { android::base::WriteStringToFile("i", "/proc/sysrq-trigger"); }
@@ -267,7 +309,7 @@
*
* return true when umount was successful. false when timed out.
*/
-static UmountStat TryUmountAndFsck(bool runFsck, int timeoutMs) {
+static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeout) {
Timer t;
std::vector<MountEntry> block_devices;
std::vector<MountEntry> emulated_devices;
@@ -278,14 +320,14 @@
return UMOUNT_STAT_ERROR;
}
- UmountStat stat = UmountPartitions(timeoutMs - t.duration_ms());
+ UmountStat stat = UmountPartitions(timeout - t.duration());
if (stat != UMOUNT_STAT_SUCCESS) {
LOG(INFO) << "umount timeout, last resort, kill all and try";
- if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
+ if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
KillAllProcesses();
// even if it succeeds, still it is timeout and do not run fsck with all processes killed
- UmountPartitions(0);
- if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
+ UmountStat st = UmountPartitions(0ms);
+ if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
}
if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
@@ -298,75 +340,79 @@
return stat;
}
-static void __attribute__((noreturn)) DoThermalOff() {
- LOG(WARNING) << "Thermal system shutdown";
- sync();
- RebootSystem(ANDROID_RB_THERMOFF, "");
- abort();
-}
-
void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
bool runFsck) {
Timer t;
LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
- android::base::WriteStringToFile(StringPrintf("%s\n", reason.c_str()), LAST_REBOOT_REASON_FILE,
- S_IRUSR | S_IWUSR, AID_SYSTEM, AID_SYSTEM);
+ property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str());
+ sync();
- if (cmd == ANDROID_RB_THERMOFF) { // do not wait if it is thermal
- DoThermalOff();
- abort();
- }
+ bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
- constexpr unsigned int shutdownTimeoutDefault = 6;
- unsigned int shutdownTimeout = shutdownTimeoutDefault;
- if (SHUTDOWN_ZERO_TIMEOUT) { // eng build
- shutdownTimeout = 0;
- } else {
- shutdownTimeout =
- android::base::GetUintProperty("ro.build.shutdown_timeout", shutdownTimeoutDefault);
+ auto shutdown_timeout = 0ms;
+ if (!SHUTDOWN_ZERO_TIMEOUT) {
+ if (is_thermal_shutdown) {
+ constexpr unsigned int thermal_shutdown_timeout = 1;
+ shutdown_timeout = std::chrono::seconds(thermal_shutdown_timeout);
+ } else {
+ constexpr unsigned int shutdown_timeout_default = 6;
+ auto shutdown_timeout_property = android::base::GetUintProperty(
+ "ro.build.shutdown_timeout", shutdown_timeout_default);
+ shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
+ }
}
- LOG(INFO) << "Shutdown timeout: " << shutdownTimeout;
+ LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
// keep debugging tools until non critical ones are all gone.
const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
- const std::set<std::string> to_starts{"watchdogd", "vold", "ueventd"};
- ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
+ const std::set<std::string> to_starts{"watchdogd"};
+ for (const auto& s : ServiceList::GetInstance()) {
if (kill_after_apps.count(s->name())) {
s->SetShutdownCritical();
} else if (to_starts.count(s->name())) {
- s->Start();
+ if (auto result = s->Start(); !result) {
+ LOG(ERROR) << "Could not start shutdown 'to_start' service '" << s->name()
+ << "': " << result.error();
+ }
s->SetShutdownCritical();
+ } else if (s->IsShutdownCritical()) {
+ // Start shutdown critical service if not started.
+ if (auto result = s->Start(); !result) {
+ LOG(ERROR) << "Could not start shutdown critical service '" << s->name()
+ << "': " << result.error();
+ }
}
- });
+ }
- Service* bootAnim = ServiceManager::GetInstance().FindServiceByName("bootanim");
- Service* surfaceFlinger = ServiceManager::GetInstance().FindServiceByName("surfaceflinger");
+ Service* bootAnim = ServiceList::GetInstance().FindService("bootanim");
+ Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger");
if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
- ServiceManager::GetInstance().ForEachServiceInClass("animation", [](Service* s) {
- s->SetShutdownCritical(); // will not check animation class separately
- });
+ // will not check animation class separately
+ for (const auto& service : ServiceList::GetInstance()) {
+ if (service->classnames().count("animation")) service->SetShutdownCritical();
+ }
}
// optional shutdown step
// 1. terminate all services except shutdown critical ones. wait for delay to finish
- if (shutdownTimeout > 0) {
+ if (shutdown_timeout > 0ms) {
LOG(INFO) << "terminating init services";
// Ask all services to terminate except shutdown critical ones.
- ServiceManager::GetInstance().ForEachService([](Service* s) {
+ for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
if (!s->IsShutdownCritical()) s->Terminate();
- });
+ }
int service_count = 0;
- // Up to half as long as shutdownTimeout or 3 seconds, whichever is lower.
- unsigned int terminationWaitTimeout = std::min<unsigned int>((shutdownTimeout + 1) / 2, 3);
- while (t.duration_s() < terminationWaitTimeout) {
- ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+ // Only wait up to half of timeout here
+ auto termination_wait_timeout = shutdown_timeout / 2;
+ while (t.duration() < termination_wait_timeout) {
+ ReapAnyOutstandingChildren();
service_count = 0;
- ServiceManager::GetInstance().ForEachService([&service_count](Service* s) {
+ for (const auto& s : ServiceList::GetInstance()) {
// Count the number of services running except shutdown critical.
// Exclude the console as it will ignore the SIGTERM signal
// and not exit.
@@ -375,7 +421,7 @@
if (!s->IsShutdownCritical() && s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
service_count++;
}
- });
+ }
if (service_count == 0) {
// All terminable services terminated. We can exit early.
@@ -391,13 +437,13 @@
// minimum safety steps before restarting
// 2. kill all services except ones that are necessary for the shutdown sequence.
- ServiceManager::GetInstance().ForEachService([](Service* s) {
+ for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
if (!s->IsShutdownCritical()) s->Stop();
- });
- ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+ }
+ ReapAnyOutstandingChildren();
// 3. send volume shutdown to vold
- Service* voldService = ServiceManager::GetInstance().FindServiceByName("vold");
+ Service* voldService = ServiceList::GetInstance().FindService("vold");
if (voldService != nullptr && voldService->IsRunning()) {
ShutdownVold();
voldService->Stop();
@@ -405,15 +451,15 @@
LOG(INFO) << "vold not running, skipping vold shutdown";
}
// logcat stopped here
- ServiceManager::GetInstance().ForEachService([&kill_after_apps](Service* s) {
+ for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
if (kill_after_apps.count(s->name())) s->Stop();
- });
+ }
// 4. sync, try umount, and optionally run fsck for user shutdown
sync();
- UmountStat stat = TryUmountAndFsck(runFsck, shutdownTimeout * 1000 - t.duration_ms());
+ UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
// Follow what linux shutdown is doing: one more sync with little bit delay
sync();
- std::this_thread::sleep_for(100ms);
+ if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
LogShutdownTime(stat, &t);
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
RebootSystem(cmd, rebootTarget);
@@ -431,10 +477,15 @@
command_invalid = true;
} else if (cmd_params[0] == "shutdown") {
cmd = ANDROID_RB_POWEROFF;
- if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
- // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
- // Run fsck once the file system is remounted in read-only mode.
- run_fsck = true;
+ if (cmd_params.size() == 2) {
+ if (cmd_params[1] == "userrequested") {
+ // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
+ // Run fsck once the file system is remounted in read-only mode.
+ run_fsck = true;
+ } else if (cmd_params[1] == "thermal") {
+ // run_fsck is false to avoid delay
+ cmd = ANDROID_RB_THERMOFF;
+ }
}
} else if (cmd_params[0] == "reboot") {
cmd = ANDROID_RB_RESTART2;
@@ -450,13 +501,11 @@
<< err;
}
}
- // If there is an additional bootloader parameter, pass it along
- if (cmd_params.size() == 3) {
+ // If there is an additional parameter, pass it along
+ if ((cmd_params.size() == 3) && cmd_params[2].size()) {
reboot_target += "," + cmd_params[2];
}
}
- } else if (command == "thermal-shutdown") { // no additional parameter allowed
- cmd = ANDROID_RB_THERMOFF;
} else {
command_invalid = true;
}
@@ -465,6 +514,28 @@
return false;
}
- DoReboot(cmd, command, reboot_target, run_fsck);
+ LOG(INFO) << "Clear action queue and start shutdown trigger";
+ ActionManager::GetInstance().ClearQueue();
+ // Queue shutdown trigger first
+ ActionManager::GetInstance().QueueEventTrigger("shutdown");
+ // Queue built-in shutdown_done
+ auto shutdown_handler = [cmd, command, reboot_target,
+ run_fsck](const std::vector<std::string>&) {
+ DoReboot(cmd, command, reboot_target, run_fsck);
+ return Success();
+ };
+ ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
+
+ // Skip wait for prop if it is in progress
+ ResetWaitForProp();
+
+ // Clear EXEC flag if there is one pending
+ for (const auto& s : ServiceList::GetInstance()) {
+ s->UnSetExec();
+ }
+
return true;
}
+
+} // namespace init
+} // namespace android
diff --git a/init/reboot.h b/init/reboot.h
index cd6be39..1c58bd1 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -17,9 +17,17 @@
#ifndef _INIT_REBOOT_H
#define _INIT_REBOOT_H
+#include <string>
+
+namespace android {
+namespace init {
+
+// This is a wrapper around the actual reboot calls. DoReboot() should be preferred in most cases.
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget);
+
/* Reboot / shutdown the system.
* cmd ANDROID_RB_* as defined in android_reboot.h
- * reason Reason string like "reboot", "userrequested"
+ * reason Reason string like "reboot", "shutdown,userrequested"
* rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
* empty string.
* runFsck Whether to run fsck after umount is done.
@@ -30,4 +38,11 @@
// Parses and handles a setprop sys.powerctl message.
bool HandlePowerctlMessage(const std::string& command);
+// Determines whether the system is capable of rebooting. This is conservative,
+// so if any of the attempts to determine this fail, it will still return true.
+bool IsRebootCapable();
+
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/result.h b/init/result.h
new file mode 100644
index 0000000..fc03962
--- /dev/null
+++ b/init/result.h
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ */
+
+// This file contains classes for returning a successful result along with an optional
+// arbitrarily typed return value or for returning a failure result along with an optional string
+// indicating why the function failed.
+
+// There are 3 classes that implement this functionality and one additional helper type.
+//
+// Result<T> either contains a member of type T that can be accessed using similar semantics as
+// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
+// Result<T>::error().
+//
+// ResultError is a type that contains both a std::string describing the error and a copy of errno
+// from when the error occurred. ResultError can be used in an ostream directly to print its
+// string value.
+//
+// Success is a typedef that aids in creating Result<T> that do not contain a return value.
+// Result<Success> is the correct return type for a function that either returns successfully or
+// returns an error value. Returning Success() from a function that returns Result<Success> is the
+// correct way to indicate that a function without a return type has completed successfully.
+//
+// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
+// to T or from the constructor arguments for T. This allows you to return a type T directly from
+// a function that returns Result<T>.
+//
+// Error and ErrnoError are used to construct a Result<T> that has failed. The Error class takes
+// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
+// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
+// to the end of the failure string to aid in interacting with C APIs. Alternatively, an errno
+// value can be directly specified via the Error() constructor.
+//
+// ResultError can be used in the ostream when using Error to construct a Result<T>. In this case,
+// the string that the ResultError takes is passed through the stream normally, but the errno is
+// passed to the Result<T>. This can be used to pass errno from a failing C function up multiple
+// callers.
+//
+// ResultError can also directly construct a Result<T>. This is particularly useful if you have a
+// function that return Result<T> but you have a Result<U> and want to return its error. In this
+// case, you can return the .error() from the Result<U> to construct the Result<T>.
+
+// An example of how to use these is below:
+// Result<U> CalculateResult(const T& input) {
+// U output;
+// if (!SomeOtherCppFunction(input, &output)) {
+// return Error() << "SomeOtherCppFunction(" << input << ") failed";
+// }
+// if (!c_api_function(output)) {
+// return ErrnoError() << "c_api_function(" << output << ") failed";
+// }
+// return output;
+// }
+//
+// auto output = CalculateResult(input);
+// if (!output) return Error() << "CalculateResult failed: " << output.error();
+// UseOutput(*output);
+
+#ifndef _INIT_RESULT_H
+#define _INIT_RESULT_H
+
+#include <errno.h>
+
+#include <sstream>
+#include <string>
+#include <variant>
+
+namespace android {
+namespace init {
+
+struct ResultError {
+ template <typename T>
+ ResultError(T&& error_string, int error_errno)
+ : error_string(std::forward<T>(error_string)), error_errno(error_errno) {}
+
+ std::string error_string;
+ int error_errno;
+};
+
+inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
+ os << t.error_string;
+ return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os, ResultError&& t) {
+ os << std::move(t.error_string);
+ return os;
+}
+
+class Error {
+ public:
+ Error() : errno_(0), append_errno_(false) {}
+ Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
+
+ template <typename T>
+ Error&& operator<<(T&& t) {
+ ss_ << std::forward<T>(t);
+ return std::move(*this);
+ }
+
+ Error&& operator<<(const ResultError& result_error) {
+ ss_ << result_error.error_string;
+ errno_ = result_error.error_errno;
+ return std::move(*this);
+ }
+
+ Error&& operator<<(ResultError&& result_error) {
+ ss_ << std::move(result_error.error_string);
+ errno_ = result_error.error_errno;
+ return std::move(*this);
+ }
+
+ const std::string str() const {
+ std::string str = ss_.str();
+ if (append_errno_) {
+ if (str.empty()) {
+ return strerror(errno_);
+ }
+ return str + ": " + strerror(errno_);
+ }
+ return str;
+ }
+
+ int get_errno() const { return errno_; }
+
+ Error(const Error&) = delete;
+ Error(Error&&) = delete;
+ Error& operator=(const Error&) = delete;
+ Error& operator=(Error&&) = delete;
+
+ private:
+ std::stringstream ss_;
+ int errno_;
+ bool append_errno_;
+};
+
+inline Error ErrnoError() {
+ return Error(errno);
+}
+
+template <typename T>
+class Result {
+ public:
+ Result() {}
+
+ template <typename U, typename... V,
+ typename = std::enable_if_t<!(std::is_same_v<std::decay_t<U>, Result<T>> &&
+ sizeof...(V) == 0)>>
+ Result(U&& result, V&&... results)
+ : contents_(std::in_place_index_t<0>(), std::forward<U>(result),
+ std::forward<V>(results)...) {}
+
+ Result(Error&& error) : contents_(std::in_place_index_t<1>(), error.str(), error.get_errno()) {}
+ Result(const ResultError& result_error)
+ : contents_(std::in_place_index_t<1>(), result_error.error_string,
+ result_error.error_errno) {}
+ Result(ResultError&& result_error)
+ : contents_(std::in_place_index_t<1>(), std::move(result_error.error_string),
+ result_error.error_errno) {}
+
+ bool has_value() const { return contents_.index() == 0; }
+
+ T& value() & { return std::get<0>(contents_); }
+ const T& value() const & { return std::get<0>(contents_); }
+ T&& value() && { return std::get<0>(std::move(contents_)); }
+ const T&& value() const && { return std::get<0>(std::move(contents_)); }
+
+ const ResultError& error() const & { return std::get<1>(contents_); }
+ ResultError&& error() && { return std::get<1>(std::move(contents_)); }
+ const ResultError&& error() const && { return std::get<1>(std::move(contents_)); }
+
+ const std::string& error_string() const & { return std::get<1>(contents_).error_string; }
+ std::string&& error_string() && { return std::get<1>(std::move(contents_)).error_string; }
+ const std::string&& error_string() const && {
+ return std::get<1>(std::move(contents_)).error_string;
+ }
+
+ int error_errno() const { return std::get<1>(contents_).error_errno; }
+
+ explicit operator bool() const { return has_value(); }
+
+ T& operator*() & { return value(); }
+ const T& operator*() const & { return value(); }
+ T&& operator*() && { return std::move(value()); }
+ const T&& operator*() const && { return std::move(value()); }
+
+ T* operator->() { return &value(); }
+ const T* operator->() const { return &value(); }
+
+ private:
+ std::variant<T, ResultError> contents_;
+};
+
+using Success = std::monostate;
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/result_test.cpp b/init/result_test.cpp
new file mode 100644
index 0000000..327b444
--- /dev/null
+++ b/init/result_test.cpp
@@ -0,0 +1,333 @@
+/*
+ * 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 "result.h"
+
+#include "errno.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+using namespace std::string_literals;
+
+namespace android {
+namespace init {
+
+TEST(result, result_accessors) {
+ Result<std::string> result = "success";
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ("success", *result);
+ EXPECT_EQ("success", result.value());
+
+ EXPECT_EQ('s', result->data()[0]);
+}
+
+TEST(result, result_accessors_rvalue) {
+ ASSERT_TRUE(Result<std::string>("success"));
+ ASSERT_TRUE(Result<std::string>("success").has_value());
+
+ EXPECT_EQ("success", *Result<std::string>("success"));
+ EXPECT_EQ("success", Result<std::string>("success").value());
+
+ EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
+}
+
+TEST(result, result_success) {
+ Result<Success> result = Success();
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ(Success(), *result);
+ EXPECT_EQ(Success(), result.value());
+}
+
+TEST(result, result_success_rvalue) {
+ // Success() doesn't actually create a Result<Success> object, but rather an object that can be
+ // implicitly constructed into a Result<Success> object.
+
+ auto MakeRvalueSuccessResult = []() -> Result<Success> { return Success(); };
+ ASSERT_TRUE(MakeRvalueSuccessResult());
+ ASSERT_TRUE(MakeRvalueSuccessResult().has_value());
+
+ EXPECT_EQ(Success(), *MakeRvalueSuccessResult());
+ EXPECT_EQ(Success(), MakeRvalueSuccessResult().value());
+}
+
+TEST(result, result_error) {
+ Result<Success> result = Error() << "failure" << 1;
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(0, result.error_errno());
+ EXPECT_EQ("failure1", result.error_string());
+}
+
+TEST(result, result_error_empty) {
+ Result<Success> result = Error();
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(0, result.error_errno());
+ EXPECT_EQ("", result.error_string());
+}
+
+TEST(result, result_error_rvalue) {
+ // Error() and ErrnoError() aren't actually used to create a Result<T> object.
+ // Under the hood, they are an intermediate class that can be implicitly constructed into a
+ // Result<T>. This is needed both to create the ostream and because Error() itself, by
+ // definition will not know what the type, T, of the underlying Result<T> object that it would
+ // create is.
+
+ auto MakeRvalueErrorResult = []() -> Result<Success> { return Error() << "failure" << 1; };
+ ASSERT_FALSE(MakeRvalueErrorResult());
+ ASSERT_FALSE(MakeRvalueErrorResult().has_value());
+
+ EXPECT_EQ(0, MakeRvalueErrorResult().error_errno());
+ EXPECT_EQ("failure1", MakeRvalueErrorResult().error_string());
+}
+
+TEST(result, result_errno_error) {
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ Result<Success> result = ErrnoError() << "failure" << 1;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(test_errno, result.error_errno());
+ EXPECT_EQ("failure1: "s + strerror(test_errno), result.error_string());
+}
+
+TEST(result, result_errno_error_no_text) {
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ Result<Success> result = ErrnoError();
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(test_errno, result.error_errno());
+ EXPECT_EQ(strerror(test_errno), result.error_string());
+}
+
+TEST(result, result_error_from_other_result) {
+ auto error_text = "test error"s;
+ Result<Success> result = Error() << error_text;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = result.error();
+
+ ASSERT_FALSE(result2);
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(0, result.error_errno());
+ EXPECT_EQ(error_text, result.error_string());
+}
+
+TEST(result, result_error_through_ostream) {
+ auto error_text = "test error"s;
+ Result<Success> result = Error() << error_text;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = Error() << result.error();
+
+ ASSERT_FALSE(result2);
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(0, result.error_errno());
+ EXPECT_EQ(error_text, result.error_string());
+}
+
+TEST(result, result_errno_error_through_ostream) {
+ auto error_text = "test error"s;
+ constexpr int test_errno = 6;
+ errno = 6;
+ Result<Success> result = ErrnoError() << error_text;
+
+ errno = 0;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = Error() << result.error();
+
+ ASSERT_FALSE(result2);
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(test_errno, result.error_errno());
+ EXPECT_EQ(error_text + ": " + strerror(test_errno), result.error_string());
+}
+
+TEST(result, constructor_forwarding) {
+ auto result = Result<std::string>(5, 'a');
+
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ("aaaaa", *result);
+}
+
+struct ConstructorTracker {
+ static size_t constructor_called;
+ static size_t copy_constructor_called;
+ static size_t move_constructor_called;
+ static size_t copy_assignment_called;
+ static size_t move_assignment_called;
+
+ template <typename T>
+ ConstructorTracker(T&& string) : string(string) {
+ ++constructor_called;
+ }
+
+ ConstructorTracker(const ConstructorTracker& ct) {
+ ++copy_constructor_called;
+ string = ct.string;
+ }
+ ConstructorTracker(ConstructorTracker&& ct) noexcept {
+ ++move_constructor_called;
+ string = std::move(ct.string);
+ }
+ ConstructorTracker& operator=(const ConstructorTracker& ct) {
+ ++copy_assignment_called;
+ string = ct.string;
+ return *this;
+ }
+ ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
+ ++move_assignment_called;
+ string = std::move(ct.string);
+ return *this;
+ }
+
+ std::string string;
+};
+
+size_t ConstructorTracker::constructor_called = 0;
+size_t ConstructorTracker::copy_constructor_called = 0;
+size_t ConstructorTracker::move_constructor_called = 0;
+size_t ConstructorTracker::copy_assignment_called = 0;
+size_t ConstructorTracker::move_assignment_called = 0;
+
+Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
+ if (in.empty()) {
+ return "literal string";
+ }
+ if (in == "test2") {
+ return ConstructorTracker(in + in + "2");
+ }
+ ConstructorTracker result(in + " " + in);
+ return result;
+};
+
+TEST(result, no_copy_on_return) {
+ // If returning parameters that may be used to implicitly construct the type T of Result<T>,
+ // then those parameters are forwarded to the construction of Result<T>.
+
+ // If returning an prvalue or xvalue, it will be move constructed during the construction of
+ // Result<T>.
+
+ // This check ensures that that is the case, and particularly that no copy constructors
+ // are called.
+
+ auto result1 = ReturnConstructorTracker("");
+ ASSERT_TRUE(result1);
+ EXPECT_EQ("literal string", result1->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ auto result2 = ReturnConstructorTracker("test2");
+ ASSERT_TRUE(result2);
+ EXPECT_EQ("test2test22", result2->string);
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ auto result3 = ReturnConstructorTracker("test3");
+ ASSERT_TRUE(result3);
+ EXPECT_EQ("test3 test3", result3->string);
+ EXPECT_EQ(3U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+}
+
+// Below two tests require that we do not hide the move constructor with our forwarding reference
+// constructor. This is done with by disabling the forwarding reference constructor if its first
+// and only type is Result<T>.
+TEST(result, result_result_with_success) {
+ auto return_result_result_with_success = []() -> Result<Result<Success>> {
+ return Result<Success>();
+ };
+ auto result = return_result_result_with_success();
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(*result);
+
+ auto inner_result = result.value();
+ ASSERT_TRUE(inner_result);
+}
+
+TEST(result, result_result_with_failure) {
+ auto return_result_result_with_error = []() -> Result<Result<Success>> {
+ return Result<Success>(ResultError("failure string", 6));
+ };
+ auto result = return_result_result_with_error();
+ ASSERT_TRUE(result);
+ ASSERT_FALSE(*result);
+ EXPECT_EQ("failure string", result->error_string());
+ EXPECT_EQ(6, result->error_errno());
+}
+
+// This test requires that we disable the forwarding reference constructor if Result<T> is the
+// *only* type that we are forwarding. In otherwords, if we are forwarding Result<T>, int to
+// construct a Result<T>, then we still need the constructor.
+TEST(result, result_two_parameter_constructor_same_type) {
+ struct TestStruct {
+ TestStruct(int value) : value_(value) {}
+ TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
+ int value_;
+ };
+
+ auto return_test_struct = []() -> Result<TestStruct> { return {Result<TestStruct>(6), 6}; };
+
+ auto result = return_test_struct();
+ ASSERT_TRUE(result);
+ EXPECT_EQ(36, result->value_);
+}
+
+TEST(result, die_on_access_failed_result) {
+ Result<std::string> result = Error();
+ ASSERT_DEATH(*result, "");
+}
+
+TEST(result, die_on_get_error_succesful_result) {
+ Result<std::string> result = "success";
+ ASSERT_DEATH(result.error_string(), "");
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/rlimit_parser.cpp b/init/rlimit_parser.cpp
new file mode 100644
index 0000000..fe1d6a7
--- /dev/null
+++ b/init/rlimit_parser.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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 "rlimit_parser.h"
+
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+using android::base::EqualsIgnoreCase;
+using android::base::ParseInt;
+using android::base::ParseUint;
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+// Builtins and service definitions both have their arguments start at 1 and finish at 3.
+Result<std::pair<int, rlimit>> ParseRlimit(const std::vector<std::string>& args) {
+ static const std::vector<std::pair<const char*, int>> text_to_resources = {
+ {"cpu", 0}, {"fsize", 1}, {"data", 2}, {"stack", 3},
+ {"core", 4}, {"rss", 5}, {"nproc", 6}, {"nofile", 7},
+ {"memlock", 8}, {"as", 9}, {"locks", 10}, {"sigpending", 11},
+ {"msgqueue", 12}, {"nice", 13}, {"rtprio", 14}, {"rttime", 15},
+ };
+
+ int resource;
+
+ if (ParseInt(args[1], &resource)) {
+ if (resource >= RLIM_NLIMITS) {
+ return Error() << "Resource '" << args[1] << "' over the maximum resource value '"
+ << RLIM_NLIMITS << "'";
+ } else if (resource < 0) {
+ return Error() << "Resource '" << args[1] << "' below the minimum resource value '0'";
+ }
+ } else {
+ std::string resource_string;
+ if (StartsWith(args[1], "RLIM_")) {
+ resource_string = args[1].substr(5);
+ } else {
+ resource_string = args[1];
+ }
+
+ auto it = std::find_if(text_to_resources.begin(), text_to_resources.end(),
+ [&resource_string](const auto& entry) {
+ return EqualsIgnoreCase(resource_string, entry.first);
+ });
+ if (it == text_to_resources.end()) {
+ return Error() << "Could not parse resource '" << args[1] << "'";
+ }
+
+ resource = it->second;
+ }
+
+ rlimit limit;
+ if (!ParseUint(args[2], &limit.rlim_cur)) {
+ return Error() << "Could not parse soft limit '" << args[2] << "'";
+ }
+ if (!ParseUint(args[3], &limit.rlim_max)) {
+ return Error() << "Could not parse hard limit '" << args[3] << "'";
+ }
+ return {resource, limit};
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/signal_handler.h b/init/rlimit_parser.h
similarity index 61%
copy from init/signal_handler.h
copy to init/rlimit_parser.h
index 449b4af..0396463 100644
--- a/init/signal_handler.h
+++ b/init/rlimit_parser.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * 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.
@@ -14,9 +14,22 @@
* limitations under the License.
*/
-#ifndef _INIT_SIGNAL_HANDLER_H_
-#define _INIT_SIGNAL_HANDLER_H_
+#ifndef _INIT_RLIMIT_PARSER_H
+#define _INIT_RLIMIT_PARSER_H
-void signal_handler_init(void);
+#include <sys/resource.h>
+
+#include <string>
+#include <vector>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<std::pair<int, rlimit>> ParseRlimit(const std::vector<std::string>& args);
+
+} // namespace init
+} // namespace android
#endif
diff --git a/init/rlimit_parser_test.cpp b/init/rlimit_parser_test.cpp
new file mode 100644
index 0000000..f3f9eb4
--- /dev/null
+++ b/init/rlimit_parser_test.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 "rlimit_parser.h"
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace init {
+
+void TestRlimitSuccess(std::vector<std::string> input,
+ const std::pair<int, rlimit>& expected_result) {
+ input.emplace(input.begin(), "");
+ ASSERT_EQ(4U, input.size());
+ auto result = ParseRlimit(input);
+
+ ASSERT_TRUE(result) << "input: " << input[1];
+ const auto& [resource, rlimit] = *result;
+ const auto& [expected_resource, expected_rlimit] = expected_result;
+ EXPECT_EQ(expected_resource, resource);
+ EXPECT_EQ(expected_rlimit.rlim_cur, rlimit.rlim_cur);
+ EXPECT_EQ(expected_rlimit.rlim_max, rlimit.rlim_max);
+}
+
+void TestRlimitFailure(std::vector<std::string> input, const std::string& expected_result) {
+ input.emplace(input.begin(), "");
+ ASSERT_EQ(4U, input.size());
+ auto result = ParseRlimit(input);
+
+ ASSERT_FALSE(result) << "input: " << input[1];
+ EXPECT_EQ(expected_result, result.error_string());
+ EXPECT_EQ(0, result.error_errno());
+}
+
+TEST(rlimit, RlimitSuccess) {
+ const std::vector<std::pair<std::vector<std::string>, std::pair<int, rlimit>>>
+ inputs_and_results = {
+ {{"cpu", "10", "10"}, {0, {10, 10}}},
+ {{"fsize", "10", "10"}, {1, {10, 10}}},
+ {{"data", "10", "10"}, {2, {10, 10}}},
+ {{"stack", "10", "10"}, {3, {10, 10}}},
+ {{"core", "10", "10"}, {4, {10, 10}}},
+ {{"rss", "10", "10"}, {5, {10, 10}}},
+ {{"nproc", "10", "10"}, {6, {10, 10}}},
+ {{"nofile", "10", "10"}, {7, {10, 10}}},
+ {{"memlock", "10", "10"}, {8, {10, 10}}},
+ {{"as", "10", "10"}, {9, {10, 10}}},
+ {{"locks", "10", "10"}, {10, {10, 10}}},
+ {{"sigpending", "10", "10"}, {11, {10, 10}}},
+ {{"msgqueue", "10", "10"}, {12, {10, 10}}},
+ {{"nice", "10", "10"}, {13, {10, 10}}},
+ {{"rtprio", "10", "10"}, {14, {10, 10}}},
+ {{"rttime", "10", "10"}, {15, {10, 10}}},
+
+ {{"RLIM_CPU", "10", "10"}, {0, {10, 10}}},
+ {{"RLIM_FSIZE", "10", "10"}, {1, {10, 10}}},
+ {{"RLIM_DATA", "10", "10"}, {2, {10, 10}}},
+ {{"RLIM_STACK", "10", "10"}, {3, {10, 10}}},
+ {{"RLIM_CORE", "10", "10"}, {4, {10, 10}}},
+ {{"RLIM_RSS", "10", "10"}, {5, {10, 10}}},
+ {{"RLIM_NPROC", "10", "10"}, {6, {10, 10}}},
+ {{"RLIM_NOFILE", "10", "10"}, {7, {10, 10}}},
+ {{"RLIM_MEMLOCK", "10", "10"}, {8, {10, 10}}},
+ {{"RLIM_AS", "10", "10"}, {9, {10, 10}}},
+ {{"RLIM_LOCKS", "10", "10"}, {10, {10, 10}}},
+ {{"RLIM_SIGPENDING", "10", "10"}, {11, {10, 10}}},
+ {{"RLIM_MSGQUEUE", "10", "10"}, {12, {10, 10}}},
+ {{"RLIM_NICE", "10", "10"}, {13, {10, 10}}},
+ {{"RLIM_RTPRIO", "10", "10"}, {14, {10, 10}}},
+ {{"RLIM_RTTIME", "10", "10"}, {15, {10, 10}}},
+
+ {{"0", "10", "10"}, {0, {10, 10}}},
+ {{"1", "10", "10"}, {1, {10, 10}}},
+ {{"2", "10", "10"}, {2, {10, 10}}},
+ {{"3", "10", "10"}, {3, {10, 10}}},
+ {{"4", "10", "10"}, {4, {10, 10}}},
+ {{"5", "10", "10"}, {5, {10, 10}}},
+ {{"6", "10", "10"}, {6, {10, 10}}},
+ {{"7", "10", "10"}, {7, {10, 10}}},
+ {{"8", "10", "10"}, {8, {10, 10}}},
+ {{"9", "10", "10"}, {9, {10, 10}}},
+ {{"10", "10", "10"}, {10, {10, 10}}},
+ {{"11", "10", "10"}, {11, {10, 10}}},
+ {{"12", "10", "10"}, {12, {10, 10}}},
+ {{"13", "10", "10"}, {13, {10, 10}}},
+ {{"14", "10", "10"}, {14, {10, 10}}},
+ {{"15", "10", "10"}, {15, {10, 10}}},
+ };
+
+ for (const auto& [input, expected_result] : inputs_and_results) {
+ TestRlimitSuccess(input, expected_result);
+ }
+}
+
+TEST(rlimit, RlimitFailure) {
+ const std::vector<std::pair<std::vector<std::string>, std::string>> inputs_and_results = {
+ {{"-4", "10", "10"}, "Resource '-4' below the minimum resource value '0'"},
+ {{"100", "10", "10"}, "Resource '100' over the maximum resource value '16'"},
+ {{"bad_string", "10", "10"}, "Could not parse resource 'bad_string'"},
+ {{"RLIM_", "10", "10"}, "Could not parse resource 'RLIM_'"},
+ {{"cpu", "abc", "10"}, "Could not parse soft limit 'abc'"},
+ {{"cpu", "10", "abc"}, "Could not parse hard limit 'abc'"},
+ };
+
+ for (const auto& [input, expected_result] : inputs_and_results) {
+ TestRlimitFailure(input, expected_result);
+ }
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/security.cpp b/init/security.cpp
new file mode 100644
index 0000000..aac8f2e
--- /dev/null
+++ b/init/security.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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 "security.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <fstream>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+// Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
+// by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
+// Does nothing if Hardware RNG is not present.
+//
+// Since we don't yet trust the quality of Hardware RNG, these bytes are not
+// mixed into the primary pool of Linux RNG and the entropy estimate is left
+// unmodified.
+//
+// If the HW RNG device /dev/hw_random is present, we require that at least
+// 512 bytes read from it are written into Linux RNG. QA is expected to catch
+// devices/configurations where these I/O operations are blocking for a long
+// time. We do not reboot or halt on failures, as this is a best-effort
+// attempt.
+Result<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args) {
+ unique_fd hwrandom_fd(
+ TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+ if (hwrandom_fd == -1) {
+ if (errno == ENOENT) {
+ LOG(INFO) << "/dev/hw_random not found";
+ // It's not an error to not have a Hardware RNG.
+ return Success();
+ }
+ return ErrnoError() << "Failed to open /dev/hw_random";
+ }
+
+ unique_fd urandom_fd(
+ TEMP_FAILURE_RETRY(open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC)));
+ if (urandom_fd == -1) {
+ return ErrnoError() << "Failed to open /dev/urandom";
+ }
+
+ char buf[512];
+ size_t total_bytes_written = 0;
+ while (total_bytes_written < sizeof(buf)) {
+ ssize_t chunk_size =
+ TEMP_FAILURE_RETRY(read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
+ if (chunk_size == -1) {
+ return ErrnoError() << "Failed to read from /dev/hw_random";
+ } else if (chunk_size == 0) {
+ return Error() << "Failed to read from /dev/hw_random: EOF";
+ }
+
+ chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
+ if (chunk_size == -1) {
+ return ErrnoError() << "Failed to write to /dev/urandom";
+ }
+ total_bytes_written += chunk_size;
+ }
+
+ LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
+ return Success();
+}
+
+static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
+ std::ifstream inf(path, std::fstream::in);
+ if (!inf) {
+ LOG(ERROR) << "Cannot open for reading: " << path;
+ return false;
+ }
+
+ int current = max;
+ while (current >= min) {
+ // try to write out new value
+ std::string str_val = std::to_string(current);
+ std::ofstream of(path, std::fstream::out);
+ if (!of) {
+ LOG(ERROR) << "Cannot open for writing: " << path;
+ return false;
+ }
+ of << str_val << std::endl;
+ of.close();
+
+ // check to make sure it was recorded
+ inf.seekg(0);
+ std::string str_rec;
+ inf >> str_rec;
+ if (str_val.compare(str_rec) == 0) {
+ break;
+ }
+ current--;
+ }
+ inf.close();
+
+ if (current < min) {
+ LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
+ return false;
+ }
+ return true;
+}
+
+#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
+#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
+
+// __attribute__((unused)) due to lack of mips support: see mips block in SetMmapRndBitsAction
+static bool __attribute__((unused)) SetMmapRndBitsMin(int start, int min, bool compat) {
+ std::string path;
+ if (compat) {
+ path = MMAP_RND_COMPAT_PATH;
+ } else {
+ path = MMAP_RND_PATH;
+ }
+
+ return SetHighestAvailableOptionValue(path, min, start);
+}
+
+// Set /proc/sys/vm/mmap_rnd_bits and potentially
+// /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
+// Returns -1 if unable to set these to an acceptable value.
+//
+// To support this sysctl, the following upstream commits are needed:
+//
+// d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
+// e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
+// 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
+// 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
+// ec9ee4acd97c drivers: char: random: add get_random_long()
+// 5ef11c35ce86 mm: ASLR: use get_random_long()
+Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args) {
+// values are arch-dependent
+#if defined(USER_MODE_LINUX)
+ // uml does not support mmap_rnd_bits
+ return Success();
+#elif defined(__aarch64__)
+ // arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE
+ if (SetMmapRndBitsMin(33, 24, false) && SetMmapRndBitsMin(16, 16, true)) {
+ return Success();
+ }
+#elif defined(__x86_64__)
+ // x86_64 supports 28 - 32 bits
+ if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
+ return Success();
+ }
+#elif defined(__arm__) || defined(__i386__)
+ // check to see if we're running on 64-bit kernel
+ bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
+ // supported 32-bit architecture must have 16 bits set
+ if (SetMmapRndBitsMin(16, 16, h64)) {
+ return Success();
+ }
+#elif defined(__mips__) || defined(__mips64__)
+ // TODO: add mips support b/27788820
+ return Success();
+#else
+ LOG(ERROR) << "Unknown architecture";
+#endif
+
+ LOG(FATAL) << "Unable to set adequate mmap entropy value!";
+ return Error();
+}
+
+#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
+#define KPTR_RESTRICT_MINVALUE 2
+#define KPTR_RESTRICT_MAXVALUE 4
+
+// Set kptr_restrict to the highest available level.
+//
+// Aborts if unable to set this to an acceptable value.
+Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args) {
+ std::string path = KPTR_RESTRICT_PATH;
+
+ if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
+ LOG(FATAL) << "Unable to set adequate kptr_restrict value!";
+ return Error();
+ }
+ return Success();
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/security.h b/init/security.h
new file mode 100644
index 0000000..31e5790
--- /dev/null
+++ b/init/security.h
@@ -0,0 +1,35 @@
+/*
+ * 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 _INIT_SECURITY_H
+#define _INIT_SECURITY_H
+
+#include <string>
+#include <vector>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args);
+Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args);
+Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/selinux.cpp b/init/selinux.cpp
new file mode 100644
index 0000000..3f7ad13
--- /dev/null
+++ b/init/selinux.cpp
@@ -0,0 +1,499 @@
+/*
+ * 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.
+ */
+
+// This file contains the functions that initialize SELinux during boot as well as helper functions
+// for SELinux operation for init.
+
+// When the system boots, there is no SEPolicy present and init is running in the kernel domain.
+// Init loads the SEPolicy from the file system, restores the context of /init based on this
+// SEPolicy, and finally exec()'s itself to run in the proper domain.
+
+// The SEPolicy on Android comes in two variants: monolithic and split.
+
+// The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
+// file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
+
+// The split policy is for supporting treble devices. It splits the SEPolicy across files on
+// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'nonplat'
+// portion of the policy). This is necessary to allow the system image to be updated independently
+// of the vendor image, while maintaining contributions from both partitions in the SEPolicy. This
+// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
+// identical to the system image shipped on a vendor's device.
+
+// The split SEPolicy is loaded as described below:
+// 1) There is a precompiled SEPolicy located at /vendor/etc/selinux/precompiled_sepolicy.
+// Stored along with this file is the sha256 hash of the parts of the SEPolicy on /system that
+// were used to compile this precompiled policy. The system partition contains a similar sha256
+// of the parts of the SEPolicy that it currently contains. If these two hashes match, then the
+// system loads this precompiled_sepolicy directly.
+// 2) If these hashes do not match, then /system has been updated out of sync with /vendor and the
+// init needs to compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it
+// is used by the LoadSplitPolicy() function below to compile the SEPolicy to a temp directory
+// and load it. That function contains even more documentation with the specific implementation
+// details of how the SEPolicy is compiled if needed.
+
+#include "selinux.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <selinux/android.h>
+
+#include "log.h"
+#include "util.h"
+
+using android::base::Timer;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+namespace {
+
+selabel_handle* sehandle = nullptr;
+
+enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
+
+EnforcingStatus StatusFromCmdline() {
+ EnforcingStatus status = SELINUX_ENFORCING;
+
+ import_kernel_cmdline(false,
+ [&](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.selinux" && value == "permissive") {
+ status = SELINUX_PERMISSIVE;
+ }
+ });
+
+ return status;
+}
+
+bool IsEnforcing() {
+ if (ALLOW_PERMISSIVE_SELINUX) {
+ return StatusFromCmdline() == SELINUX_ENFORCING;
+ }
+ return true;
+}
+
+// Forks, executes the provided program in the child, and waits for the completion in the parent.
+// Child's stderr is captured and logged using LOG(ERROR).
+bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
+ // Create a pipe used for redirecting child process's output.
+ // * pipe_fds[0] is the FD the parent will use for reading.
+ // * pipe_fds[1] is the FD the child will use for writing.
+ int pipe_fds[2];
+ if (pipe(pipe_fds) == -1) {
+ PLOG(ERROR) << "Failed to create pipe";
+ return false;
+ }
+
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ PLOG(ERROR) << "Failed to fork for " << filename;
+ return false;
+ }
+
+ if (child_pid == 0) {
+ // fork succeeded -- this is executing in the child process
+
+ // Close the pipe FD not used by this process
+ TEMP_FAILURE_RETRY(close(pipe_fds[0]));
+
+ // Redirect stderr to the pipe FD provided by the parent
+ if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
+ PLOG(ERROR) << "Failed to redirect stderr of " << filename;
+ _exit(127);
+ return false;
+ }
+ TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+ if (execv(filename, argv) == -1) {
+ PLOG(ERROR) << "Failed to execve " << filename;
+ return false;
+ }
+ // Unreachable because execve will have succeeded and replaced this code
+ // with child process's code.
+ _exit(127);
+ return false;
+ } else {
+ // fork succeeded -- this is executing in the original/parent process
+
+ // Close the pipe FD not used by this process
+ TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+ // Log the redirected output of the child process.
+ // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
+ // As a result, we're buffering all output and logging it in one go at the end of the
+ // invocation, instead of logging it as it comes in.
+ const int child_out_fd = pipe_fds[0];
+ std::string child_output;
+ if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
+ PLOG(ERROR) << "Failed to capture full output of " << filename;
+ }
+ TEMP_FAILURE_RETRY(close(child_out_fd));
+ if (!child_output.empty()) {
+ // Log captured output, line by line, because LOG expects to be invoked for each line
+ std::istringstream in(child_output);
+ std::string line;
+ while (std::getline(in, line)) {
+ LOG(ERROR) << filename << ": " << line;
+ }
+ }
+
+ // Wait for child to terminate
+ int status;
+ if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
+ PLOG(ERROR) << "Failed to wait for " << filename;
+ return false;
+ }
+
+ if (WIFEXITED(status)) {
+ int status_code = WEXITSTATUS(status);
+ if (status_code == 0) {
+ return true;
+ } else {
+ LOG(ERROR) << filename << " exited with status " << status_code;
+ }
+ } else if (WIFSIGNALED(status)) {
+ LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
+ } else if (WIFSTOPPED(status)) {
+ LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
+ } else {
+ LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
+ }
+
+ return false;
+ }
+}
+
+bool ReadFirstLine(const char* file, std::string* line) {
+ line->clear();
+
+ std::string contents;
+ if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
+ return false;
+ }
+ std::istringstream in(contents);
+ std::getline(in, *line);
+ return true;
+}
+
+bool FindPrecompiledSplitPolicy(std::string* file) {
+ file->clear();
+ // If there is an odm partition, precompiled_sepolicy will be in
+ // odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.
+ static constexpr const char vendor_precompiled_sepolicy[] =
+ "/vendor/etc/selinux/precompiled_sepolicy";
+ static constexpr const char odm_precompiled_sepolicy[] =
+ "/odm/etc/selinux/precompiled_sepolicy";
+ if (access(odm_precompiled_sepolicy, R_OK) == 0) {
+ *file = odm_precompiled_sepolicy;
+ } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {
+ *file = vendor_precompiled_sepolicy;
+ } else {
+ PLOG(INFO) << "No precompiled sepolicy";
+ return false;
+ }
+ std::string actual_plat_id;
+ if (!ReadFirstLine("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256", &actual_plat_id)) {
+ PLOG(INFO) << "Failed to read "
+ "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
+ return false;
+ }
+
+ std::string precompiled_plat_id;
+ std::string precompiled_sha256 = *file + ".plat_and_mapping.sha256";
+ if (!ReadFirstLine(precompiled_sha256.c_str(), &precompiled_plat_id)) {
+ PLOG(INFO) << "Failed to read " << precompiled_sha256;
+ file->clear();
+ return false;
+ }
+ if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
+ file->clear();
+ return false;
+ }
+ return true;
+}
+
+bool GetVendorMappingVersion(std::string* plat_vers) {
+ if (!ReadFirstLine("/vendor/etc/selinux/plat_sepolicy_vers.txt", plat_vers)) {
+ PLOG(ERROR) << "Failed to read /vendor/etc/selinux/plat_sepolicy_vers.txt";
+ return false;
+ }
+ if (plat_vers->empty()) {
+ LOG(ERROR) << "No version present in plat_sepolicy_vers.txt";
+ return false;
+ }
+ return true;
+}
+
+constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
+
+bool IsSplitPolicyDevice() {
+ return access(plat_policy_cil_file, R_OK) != -1;
+}
+
+bool LoadSplitPolicy() {
+ // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
+ // * platform -- policy needed due to logic contained in the system image,
+ // * non-platform -- policy needed due to logic contained in the vendor image,
+ // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
+ // with newer versions of platform policy.
+ //
+ // secilc is invoked to compile the above three policy files into a single monolithic policy
+ // file. This file is then loaded into the kernel.
+
+ // Load precompiled policy from vendor image, if a matching policy is found there. The policy
+ // must match the platform policy on the system image.
+ std::string precompiled_sepolicy_file;
+ if (FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
+ unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
+ if (fd != -1) {
+ if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
+ LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
+ return false;
+ }
+ return true;
+ }
+ }
+ // No suitable precompiled policy could be loaded
+
+ LOG(INFO) << "Compiling SELinux policy";
+
+ // Determine the highest policy language version supported by the kernel
+ set_selinuxmnt("/sys/fs/selinux");
+ int max_policy_version = security_policyvers();
+ if (max_policy_version == -1) {
+ PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
+ return false;
+ }
+
+ // We store the output of the compilation on /dev because this is the most convenient tmpfs
+ // storage mount available this early in the boot sequence.
+ char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
+ unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
+ if (compiled_sepolicy_fd < 0) {
+ PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
+ return false;
+ }
+
+ // Determine which mapping file to include
+ std::string vend_plat_vers;
+ if (!GetVendorMappingVersion(&vend_plat_vers)) {
+ return false;
+ }
+ std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+
+ // vendor_sepolicy.cil and nonplat_declaration.cil are the new design to replace
+ // nonplat_sepolicy.cil.
+ std::string nonplat_declaration_cil_file("/vendor/etc/selinux/nonplat_declaration.cil");
+ std::string vendor_policy_cil_file("/vendor/etc/selinux/vendor_sepolicy.cil");
+
+ if (access(vendor_policy_cil_file.c_str(), F_OK) == -1) {
+ // For backward compatibility.
+ // TODO: remove this after no device is using nonplat_sepolicy.cil.
+ vendor_policy_cil_file = "/vendor/etc/selinux/nonplat_sepolicy.cil";
+ nonplat_declaration_cil_file.clear();
+ } else if (access(nonplat_declaration_cil_file.c_str(), F_OK) == -1) {
+ LOG(ERROR) << "Missing " << nonplat_declaration_cil_file;
+ return false;
+ }
+
+ // odm_sepolicy.cil is default but optional.
+ std::string odm_policy_cil_file("/odm/etc/selinux/odm_sepolicy.cil");
+ if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
+ odm_policy_cil_file.clear();
+ }
+ const std::string version_as_string = std::to_string(max_policy_version);
+
+ // clang-format off
+ std::vector<const char*> compile_args {
+ "/system/bin/secilc",
+ plat_policy_cil_file,
+ "-M", "true", "-G", "-N",
+ // Target the highest policy language version supported by the kernel
+ "-c", version_as_string.c_str(),
+ mapping_file.c_str(),
+ "-o", compiled_sepolicy,
+ // We don't care about file_contexts output by the compiler
+ "-f", "/sys/fs/selinux/null", // /dev/null is not yet available
+ };
+ // clang-format on
+
+ if (!nonplat_declaration_cil_file.empty()) {
+ compile_args.push_back(nonplat_declaration_cil_file.c_str());
+ }
+ if (!vendor_policy_cil_file.empty()) {
+ compile_args.push_back(vendor_policy_cil_file.c_str());
+ }
+ if (!odm_policy_cil_file.empty()) {
+ compile_args.push_back(odm_policy_cil_file.c_str());
+ }
+ compile_args.push_back(nullptr);
+
+ if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
+ unlink(compiled_sepolicy);
+ return false;
+ }
+ unlink(compiled_sepolicy);
+
+ LOG(INFO) << "Loading compiled SELinux policy";
+ if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
+ LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
+ return false;
+ }
+
+ return true;
+}
+
+bool LoadMonolithicPolicy() {
+ LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
+ if (selinux_android_load_policy() < 0) {
+ PLOG(ERROR) << "Failed to load monolithic SELinux policy";
+ return false;
+ }
+ return true;
+}
+
+bool LoadPolicy() {
+ return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
+}
+
+} // namespace
+
+void SelinuxInitialize() {
+ Timer t;
+
+ LOG(INFO) << "Loading SELinux policy";
+ if (!LoadPolicy()) {
+ LOG(FATAL) << "Unable to load SELinux policy";
+ }
+
+ bool kernel_enforcing = (security_getenforce() == 1);
+ bool is_enforcing = IsEnforcing();
+ if (kernel_enforcing != is_enforcing) {
+ if (security_setenforce(is_enforcing)) {
+ PLOG(FATAL) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
+ }
+ }
+
+ if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
+ LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
+ }
+
+ // init's first stage can't set properties, so pass the time to the second stage.
+ setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
+}
+
+// The files and directories that were created before initial sepolicy load or
+// files on ramdisk need to have their security context restored to the proper
+// value. This must happen before /dev is populated by ueventd.
+void SelinuxRestoreContext() {
+ LOG(INFO) << "Running restorecon...";
+ selinux_android_restorecon("/dev", 0);
+ selinux_android_restorecon("/dev/kmsg", 0);
+ if constexpr (WORLD_WRITABLE_KMSG) {
+ selinux_android_restorecon("/dev/kmsg_debug", 0);
+ }
+ selinux_android_restorecon("/dev/socket", 0);
+ selinux_android_restorecon("/dev/random", 0);
+ selinux_android_restorecon("/dev/urandom", 0);
+ selinux_android_restorecon("/dev/__properties__", 0);
+
+ selinux_android_restorecon("/file_contexts.bin", 0);
+ selinux_android_restorecon("/plat_file_contexts", 0);
+ selinux_android_restorecon("/nonplat_file_contexts", 0);
+ selinux_android_restorecon("/plat_property_contexts", 0);
+ selinux_android_restorecon("/nonplat_property_contexts", 0);
+ selinux_android_restorecon("/plat_seapp_contexts", 0);
+ selinux_android_restorecon("/nonplat_seapp_contexts", 0);
+ selinux_android_restorecon("/plat_service_contexts", 0);
+ selinux_android_restorecon("/nonplat_service_contexts", 0);
+ selinux_android_restorecon("/plat_hwservice_contexts", 0);
+ selinux_android_restorecon("/nonplat_hwservice_contexts", 0);
+ selinux_android_restorecon("/sepolicy", 0);
+ selinux_android_restorecon("/vndservice_contexts", 0);
+
+ selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+ selinux_android_restorecon("/dev/device-mapper", 0);
+
+ selinux_android_restorecon("/sbin/mke2fs_static", 0);
+ selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
+}
+
+// This function sets up SELinux logging to be written to kmsg, to match init's logging.
+void SelinuxSetupKernelLogging() {
+ selinux_callback cb;
+ cb.func_log = selinux_klog_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+}
+
+// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
+// its value. selinux_android_restorecon() also needs an sehandle for file context look up. It
+// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
+// one, thus eliminating an extra call to selinux_android_file_context_handle().
+void SelabelInitialize() {
+ sehandle = selinux_android_file_context_handle();
+ selinux_android_set_sehandle(sehandle);
+}
+
+// A C++ wrapper around selabel_lookup() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
+ result->clear();
+
+ if (!sehandle) return true;
+
+ char* context;
+ if (selabel_lookup(sehandle, &context, key.c_str(), type) != 0) {
+ return false;
+ }
+ *result = context;
+ free(context);
+ return true;
+}
+
+// A C++ wrapper around selabel_lookup_best_match() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+ const std::vector<std::string>& aliases, int type,
+ std::string* result) {
+ result->clear();
+
+ if (!sehandle) return true;
+
+ std::vector<const char*> c_aliases;
+ for (const auto& alias : aliases) {
+ c_aliases.emplace_back(alias.c_str());
+ }
+ c_aliases.emplace_back(nullptr);
+
+ char* context;
+ if (selabel_lookup_best_match(sehandle, &context, key.c_str(), &c_aliases[0], type) != 0) {
+ return false;
+ }
+ *result = context;
+ free(context);
+ return true;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/selinux.h b/init/selinux.h
new file mode 100644
index 0000000..7b880ec
--- /dev/null
+++ b/init/selinux.h
@@ -0,0 +1,40 @@
+/*
+ * 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 _INIT_SELINUX_H
+#define _INIT_SELINUX_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace init {
+
+void SelinuxInitialize();
+void SelinuxRestoreContext();
+
+void SelinuxSetupKernelLogging();
+
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+ const std::vector<std::string>& aliases, int type,
+ std::string* result);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/service.cpp b/init/service.cpp
index 9ba0272..86b910a 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -24,50 +24,54 @@
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
+#include <sys/system_properties.h>
#include <sys/time.h>
-#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
-#include <selinux/selinux.h>
-
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <processgroup/processgroup.h>
+#include <selinux/selinux.h>
#include <system/thread_defs.h>
-#include <processgroup/processgroup.h>
-
-#include "action.h"
#include "init.h"
-#include "init_parser.h"
-#include "log.h"
#include "property_service.h"
+#include "rlimit_parser.h"
#include "util.h"
+using android::base::boot_clock;
+using android::base::GetProperty;
+using android::base::Join;
+using android::base::make_scope_guard;
using android::base::ParseInt;
+using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
-static std::string ComputeContextFromExecutable(std::string& service_name,
- const std::string& service_path) {
+namespace android {
+namespace init {
+
+static Result<std::string> ComputeContextFromExecutable(std::string& service_name,
+ const std::string& service_path) {
std::string computed_context;
char* raw_con = nullptr;
char* raw_filecon = nullptr;
if (getcon(&raw_con) == -1) {
- LOG(ERROR) << "could not get context while starting '" << service_name << "'";
- return "";
+ return Error() << "Could not get security context";
}
std::unique_ptr<char> mycon(raw_con);
if (getfilecon(service_path.c_str(), &raw_filecon) == -1) {
- LOG(ERROR) << "could not get file context while starting '" << service_name << "'";
- return "";
+ return Error() << "Could not get file context";
}
std::unique_ptr<char> filecon(raw_filecon);
@@ -79,12 +83,14 @@
free(new_con);
}
if (rc == 0 && computed_context == mycon.get()) {
- LOG(ERROR) << "service " << service_name << " does not have a SELinux domain defined";
- return "";
+ return Error() << "File " << service_path << "(labeled \"" << filecon.get()
+ << "\") has incorrect label or no domain transition from " << mycon.get()
+ << " to another SELinux domain defined. Have you configured your "
+ "service correctly? https://source.android.com/security/selinux/"
+ "device-policy#label_new_services_and_address_denials";
}
if (rc < 0) {
- LOG(ERROR) << "could not get context while starting '" << service_name << "'";
- return "";
+ return Error() << "Could not get process context";
}
return computed_context;
}
@@ -129,45 +135,28 @@
}
}
-static void ExpandArgs(const std::vector<std::string>& args, std::vector<char*>* strs) {
+static bool ExpandArgsAndExecv(const std::vector<std::string>& args) {
std::vector<std::string> expanded_args;
+ std::vector<char*> c_strings;
+
expanded_args.resize(args.size());
- strs->push_back(const_cast<char*>(args[0].c_str()));
+ c_strings.push_back(const_cast<char*>(args[0].data()));
for (std::size_t i = 1; i < args.size(); ++i) {
if (!expand_props(args[i], &expanded_args[i])) {
LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
}
- strs->push_back(const_cast<char*>(expanded_args[i].c_str()));
+ c_strings.push_back(expanded_args[i].data());
}
- strs->push_back(nullptr);
+ c_strings.push_back(nullptr);
+
+ return execv(c_strings[0], c_strings.data()) == 0;
}
-ServiceEnvironmentInfo::ServiceEnvironmentInfo() {
-}
-
-ServiceEnvironmentInfo::ServiceEnvironmentInfo(const std::string& name,
- const std::string& value)
- : name(name), value(value) {
-}
+unsigned long Service::next_start_order_ = 1;
+bool Service::is_exec_service_running_ = false;
Service::Service(const std::string& name, const std::vector<std::string>& args)
- : name_(name),
- classnames_({"default"}),
- flags_(0),
- pid_(0),
- crash_count_(0),
- uid_(0),
- gid_(0),
- namespace_flags_(0),
- seclabel_(""),
- keychord_id_(0),
- ioprio_class_(IoSchedClass_NONE),
- ioprio_pri_(0),
- priority_(0),
- oom_score_adjust_(-1000),
- args_(args) {
- onrestart_.InitSingleTrigger("onrestart");
-}
+ : Service(name, 0, 0, 0, {}, 0, 0, "", args) {}
Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
@@ -184,11 +173,16 @@
capabilities_(capabilities),
namespace_flags_(namespace_flags),
seclabel_(seclabel),
+ onrestart_(false, "<Service '" + name + "' onrestart>", 0),
keychord_id_(0),
ioprio_class_(IoSchedClass_NONE),
ioprio_pri_(0),
priority_(0),
oom_score_adjust_(-1000),
+ swappiness_(-1),
+ soft_limit_in_bytes_(-1),
+ limit_in_bytes_(-1),
+ start_order_(0),
args_(args) {
onrestart_.InitSingleTrigger("onrestart");
}
@@ -199,39 +193,55 @@
return;
}
- std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
- property_set(prop_name.c_str(), new_state.c_str());
+ std::string prop_name = "init.svc." + name_;
+ property_set(prop_name, new_state);
if (new_state == "running") {
uint64_t start_ns = time_started_.time_since_epoch().count();
- property_set(StringPrintf("ro.boottime.%s", name_.c_str()).c_str(),
- StringPrintf("%" PRIu64, start_ns).c_str());
+ std::string boottime_property = "ro.boottime." + name_;
+ if (GetProperty(boottime_property, "").empty()) {
+ property_set(boottime_property, std::to_string(start_ns));
+ }
}
}
void Service::KillProcessGroup(int signal) {
- LOG(INFO) << "Sending signal " << signal
- << " to service '" << name_
- << "' (pid " << pid_ << ") process group...";
- int r;
- if (signal == SIGTERM) {
- r = killProcessGroupOnce(uid_, pid_, signal);
- } else {
- r = killProcessGroup(uid_, pid_, signal);
- }
- if (r == -1) {
- PLOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
- }
- if (kill(-pid_, signal) == -1) {
- PLOG(ERROR) << "kill(" << pid_ << ", " << signal << ") failed";
+ // If we've already seen a successful result from killProcessGroup*(), then we have removed
+ // the cgroup already and calling these functions a second time will simply result in an error.
+ // This is true regardless of which signal was sent.
+ // These functions handle their own logging, so no additional logging is needed.
+ if (!process_cgroup_empty_) {
+ LOG(INFO) << "Sending signal " << signal << " to service '" << name_ << "' (pid " << pid_
+ << ") process group...";
+ int r;
+ if (signal == SIGTERM) {
+ r = killProcessGroupOnce(uid_, pid_, signal);
+ } else {
+ r = killProcessGroup(uid_, pid_, signal);
+ }
+
+ if (r == 0) process_cgroup_empty_ = true;
}
}
void Service::SetProcessAttributes() {
+ for (const auto& rlimit : rlimits_) {
+ if (setrlimit(rlimit.first, &rlimit.second) == -1) {
+ LOG(FATAL) << StringPrintf("setrlimit(%d, {rlim_cur=%ld, rlim_max=%ld}) failed",
+ rlimit.first, rlimit.second.rlim_cur, rlimit.second.rlim_max);
+ }
+ }
// Keep capabilites on uid change.
if (capabilities_.any() && uid_) {
- if (prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS | SECBIT_KEEP_CAPS_LOCKED) != 0) {
- PLOG(FATAL) << "prtcl(PR_SET_KEEPCAPS) failed for " << name_;
+ // If Android is running in a container, some securebits might already
+ // be locked, so don't change those.
+ unsigned long securebits = prctl(PR_GET_SECUREBITS);
+ if (securebits == -1UL) {
+ PLOG(FATAL) << "prctl(PR_GET_SECUREBITS) failed for " << name_;
+ }
+ securebits |= SECBIT_KEEP_CAPS | SECBIT_KEEP_CAPS_LOCKED;
+ if (prctl(PR_SET_SECUREBITS, securebits) != 0) {
+ PLOG(FATAL) << "prctl(PR_SET_SECUREBITS) failed for " << name_;
}
}
@@ -277,12 +287,13 @@
std::for_each(descriptors_.begin(), descriptors_.end(),
std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
- if (flags_ & SVC_TEMPORARY) {
- return;
- }
+ if (flags_ & SVC_EXEC) UnSetExec();
+
+ if (flags_ & SVC_TEMPORARY) return;
pid_ = 0;
flags_ &= (~SVC_RUNNING);
+ start_order_ = 0;
// Oneshot processes go into the disabled state on exit,
// except when manually restarted.
@@ -301,8 +312,7 @@
if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
if (now < time_crashed_ + 4min) {
if (++crash_count_ > 4) {
- LOG(ERROR) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
- panic();
+ LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
}
} else {
time_crashed_ = now;
@@ -322,18 +332,18 @@
void Service::DumpState() const {
LOG(INFO) << "service " << name_;
- LOG(INFO) << " class '" << android::base::Join(classnames_, " ") << "'";
- LOG(INFO) << " exec "<< android::base::Join(args_, " ");
+ LOG(INFO) << " class '" << Join(classnames_, " ") << "'";
+ LOG(INFO) << " exec " << Join(args_, " ");
std::for_each(descriptors_.begin(), descriptors_.end(),
[] (const auto& info) { LOG(INFO) << *info; });
}
-bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseCapabilities(const std::vector<std::string>& args) {
capabilities_ = 0;
if (!CapAmbientSupported()) {
- *err = "capabilities requested but the kernel does not support ambient capabilities";
- return false;
+ return Error()
+ << "capabilities requested but the kernel does not support ambient capabilities";
}
unsigned int last_valid_cap = GetLastValidCap();
@@ -345,65 +355,71 @@
const std::string& arg = args[i];
int res = LookupCap(arg);
if (res < 0) {
- *err = StringPrintf("invalid capability '%s'", arg.c_str());
- return false;
+ return Error() << StringPrintf("invalid capability '%s'", arg.c_str());
}
unsigned int cap = static_cast<unsigned int>(res); // |res| is >= 0.
if (cap > last_valid_cap) {
- *err = StringPrintf("capability '%s' not supported by the kernel", arg.c_str());
- return false;
+ return Error() << StringPrintf("capability '%s' not supported by the kernel",
+ arg.c_str());
}
capabilities_[cap] = true;
}
- return true;
+ return Success();
}
-bool Service::ParseClass(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseClass(const std::vector<std::string>& args) {
classnames_ = std::set<std::string>(args.begin() + 1, args.end());
- return true;
+ return Success();
}
-bool Service::ParseConsole(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseConsole(const std::vector<std::string>& args) {
flags_ |= SVC_CONSOLE;
console_ = args.size() > 1 ? "/dev/" + args[1] : "";
- return true;
+ return Success();
}
-bool Service::ParseCritical(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseCritical(const std::vector<std::string>& args) {
flags_ |= SVC_CRITICAL;
- return true;
+ return Success();
}
-bool Service::ParseDisabled(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseDisabled(const std::vector<std::string>& args) {
flags_ |= SVC_DISABLED;
flags_ |= SVC_RC_DISABLED;
- return true;
+ return Success();
}
-bool Service::ParseGroup(const std::vector<std::string>& args, std::string* err) {
- gid_ = decode_uid(args[1].c_str());
- for (std::size_t n = 2; n < args.size(); n++) {
- supp_gids_.emplace_back(decode_uid(args[n].c_str()));
+Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
+ auto gid = DecodeUid(args[1]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
}
- return true;
+ gid_ = *gid;
+
+ for (std::size_t n = 2; n < args.size(); n++) {
+ gid = DecodeUid(args[n]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error();
+ }
+ supp_gids_.emplace_back(*gid);
+ }
+ return Success();
}
-bool Service::ParsePriority(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParsePriority(const std::vector<std::string>& args) {
priority_ = 0;
if (!ParseInt(args[1], &priority_,
static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
- *err = StringPrintf("process priority value must be range %d - %d",
- ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
- return false;
+ return Error() << StringPrintf("process priority value must be range %d - %d",
+ ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
}
- return true;
+ return Success();
}
-bool Service::ParseIoprio(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseIoprio(const std::vector<std::string>& args) {
if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) {
- *err = "priority value must be range 0 - 7";
- return false;
+ return Error() << "priority value must be range 0 - 7";
}
if (args[1] == "rt") {
@@ -413,14 +429,13 @@
} else if (args[1] == "idle") {
ioprio_class_ = IoSchedClass_IDLE;
} else {
- *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>";
- return false;
+ return Error() << "ioprio option usage: ioprio <rt|be|idle> <0-7>";
}
- return true;
+ return Success();
}
-bool Service::ParseKeycodes(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
for (std::size_t i = 1; i < args.size(); i++) {
int code;
if (ParseInt(args[i], &code)) {
@@ -429,21 +444,24 @@
LOG(WARNING) << "ignoring invalid keycode: " << args[i];
}
}
- return true;
+ return Success();
}
-bool Service::ParseOneshot(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOneshot(const std::vector<std::string>& args) {
flags_ |= SVC_ONESHOT;
- return true;
+ return Success();
}
-bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOnrestart(const std::vector<std::string>& args) {
std::vector<std::string> str_args(args.begin() + 1, args.end());
- onrestart_.AddCommand(str_args, "", 0, err);
- return true;
+ int line = onrestart_.NumCommands() + 1;
+ if (auto result = onrestart_.AddCommand(str_args, line); !result) {
+ return Error() << "cannot add Onrestart command: " << result.error();
+ }
+ return Success();
}
-bool Service::ParseNamespace(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseNamespace(const std::vector<std::string>& args) {
for (size_t i = 1; i < args.size(); i++) {
if (args[i] == "pid") {
namespace_flags_ |= CLONE_NEWPID;
@@ -452,94 +470,144 @@
} else if (args[i] == "mnt") {
namespace_flags_ |= CLONE_NEWNS;
} else {
- *err = "namespace must be 'pid' or 'mnt'";
- return false;
+ return Error() << "namespace must be 'pid' or 'mnt'";
}
}
- return true;
+ return Success();
}
-bool Service::ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOomScoreAdjust(const std::vector<std::string>& args) {
if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) {
- *err = "oom_score_adjust value must be in range -1000 - +1000";
- return false;
+ return Error() << "oom_score_adjust value must be in range -1000 - +1000";
}
- return true;
+ return Success();
}
-bool Service::ParseSeclabel(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseMemcgSwappiness(const std::vector<std::string>& args) {
+ if (!ParseInt(args[1], &swappiness_, 0)) {
+ return Error() << "swappiness value must be equal or greater than 0";
+ }
+ return Success();
+}
+
+Result<Success> Service::ParseMemcgLimitInBytes(const std::vector<std::string>& args) {
+ if (!ParseInt(args[1], &limit_in_bytes_, 0)) {
+ return Error() << "limit_in_bytes value must be equal or greater than 0";
+ }
+ return Success();
+}
+
+Result<Success> Service::ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args) {
+ if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
+ return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
+ }
+ return Success();
+}
+
+Result<Success> Service::ParseProcessRlimit(const std::vector<std::string>& args) {
+ auto rlimit = ParseRlimit(args);
+ if (!rlimit) return rlimit.error();
+
+ rlimits_.emplace_back(*rlimit);
+ return Success();
+}
+
+Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
seclabel_ = args[1];
- return true;
+ return Success();
}
-bool Service::ParseSetenv(const std::vector<std::string>& args, std::string* err) {
- envvars_.emplace_back(args[1], args[2]);
- return true;
+Result<Success> Service::ParseSetenv(const std::vector<std::string>& args) {
+ environment_vars_.emplace_back(args[1], args[2]);
+ return Success();
+}
+
+Result<Success> Service::ParseShutdown(const std::vector<std::string>& args) {
+ if (args[1] == "critical") {
+ flags_ |= SVC_SHUTDOWN_CRITICAL;
+ return Success();
+ }
+ return Error() << "Invalid shutdown option";
}
template <typename T>
-bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::AddDescriptor(const std::vector<std::string>& args) {
int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
- uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
- gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
+ Result<uid_t> uid = 0;
+ Result<gid_t> gid = 0;
std::string context = args.size() > 6 ? args[6] : "";
- auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context);
+ if (args.size() > 4) {
+ uid = DecodeUid(args[4]);
+ if (!uid) {
+ return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
+ }
+ }
+
+ if (args.size() > 5) {
+ gid = DecodeUid(args[5]);
+ if (!gid) {
+ return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
+ }
+ }
+
+ auto descriptor = std::make_unique<T>(args[1], args[2], *uid, *gid, perm, context);
auto old =
std::find_if(descriptors_.begin(), descriptors_.end(),
[&descriptor] (const auto& other) { return descriptor.get() == other.get(); });
if (old != descriptors_.end()) {
- *err = "duplicate descriptor " + args[1] + " " + args[2];
- return false;
+ return Error() << "duplicate descriptor " << args[1] << " " << args[2];
}
descriptors_.emplace_back(std::move(descriptor));
- return true;
+ return Success();
}
// name type perm [ uid gid context ]
-bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
- if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
- *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
- return false;
+Result<Success> Service::ParseSocket(const std::vector<std::string>& args) {
+ if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
+ !StartsWith(args[2], "seqpacket")) {
+ return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
}
- return AddDescriptor<SocketInfo>(args, err);
+ return AddDescriptor<SocketInfo>(args);
}
// name type perm [ uid gid context ]
-bool Service::ParseFile(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseFile(const std::vector<std::string>& args) {
if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
- *err = "file type must be 'r', 'w' or 'rw'";
- return false;
+ return Error() << "file type must be 'r', 'w' or 'rw'";
}
if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
- *err = "file name must not be relative";
- return false;
+ return Error() << "file name must not be relative";
}
- return AddDescriptor<FileInfo>(args, err);
+ return AddDescriptor<FileInfo>(args);
}
-bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
- uid_ = decode_uid(args[1].c_str());
- return true;
+Result<Success> Service::ParseUser(const std::vector<std::string>& args) {
+ auto uid = DecodeUid(args[1]);
+ if (!uid) {
+ return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
+ }
+ uid_ = *uid;
+ return Success();
}
-bool Service::ParseWritepid(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseWritepid(const std::vector<std::string>& args) {
writepid_files_.assign(args.begin() + 1, args.end());
- return true;
+ return Success();
}
class Service::OptionParserMap : public KeywordMap<OptionParser> {
-public:
- OptionParserMap() {
- }
-private:
- Map& map() const override;
+ public:
+ OptionParserMap() {}
+
+ private:
+ const Map& map() const override;
};
-Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
+const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map option_parsers = {
@@ -557,9 +625,17 @@
{"onrestart", {1, kMax, &Service::ParseOnrestart}},
{"oom_score_adjust",
{1, 1, &Service::ParseOomScoreAdjust}},
+ {"memcg.swappiness",
+ {1, 1, &Service::ParseMemcgSwappiness}},
+ {"memcg.soft_limit_in_bytes",
+ {1, 1, &Service::ParseMemcgSoftLimitInBytes}},
+ {"memcg.limit_in_bytes",
+ {1, 1, &Service::ParseMemcgLimitInBytes}},
{"namespace", {1, 2, &Service::ParseNamespace}},
+ {"rlimit", {3, 3, &Service::ParseProcessRlimit}},
{"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}},
+ {"shutdown", {1, 1, &Service::ParseShutdown}},
{"socket", {3, 6, &Service::ParseSocket}},
{"file", {2, 2, &Service::ParseFile}},
{"user", {1, 1, &Service::ParseUser}},
@@ -569,35 +645,33 @@
return option_parsers;
}
-bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
- if (args.empty()) {
- *err = "option needed, but not provided";
- return false;
- }
-
+Result<Success> Service::ParseLine(const std::vector<std::string>& args) {
static const OptionParserMap parser_map;
- auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);
+ auto parser = parser_map.FindFunction(args);
- if (!parser) {
- return false;
- }
+ if (!parser) return parser.error();
- return (this->*parser)(args, err);
+ return std::invoke(*parser, this, args);
}
-bool Service::ExecStart(std::unique_ptr<Timer>* exec_waiter) {
- flags_ |= SVC_EXEC | SVC_ONESHOT;
+Result<Success> Service::ExecStart() {
+ flags_ |= SVC_ONESHOT;
- exec_waiter->reset(new Timer);
-
- if (!Start()) {
- exec_waiter->reset();
- return false;
+ if (auto result = Start(); !result) {
+ return result;
}
- return true;
+
+ flags_ |= SVC_EXEC;
+ is_exec_service_running_ = true;
+
+ LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+"
+ << supp_gids_.size() << " context " << (!seclabel_.empty() ? seclabel_ : "default")
+ << ") started; waiting...";
+
+ return Success();
}
-bool Service::Start() {
+Result<Success> Service::Start() {
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
@@ -606,7 +680,8 @@
// process of exiting, we've ensured that they will immediately restart
// on exit, unless they are ONESHOT.
if (flags_ & SVC_RUNNING) {
- return false;
+ // It is not an error to try to start a service that is already running.
+ return Success();
}
bool needs_console = (flags_ & SVC_CONSOLE);
@@ -619,29 +694,27 @@
// properly registered for the device node
int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
if (console_fd < 0) {
- PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'";
flags_ |= SVC_DISABLED;
- return false;
+ return ErrnoError() << "Couldn't open console '" << console_ << "'";
}
close(console_fd);
}
struct stat sb;
if (stat(args_[0].c_str(), &sb) == -1) {
- PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
flags_ |= SVC_DISABLED;
- return false;
+ return ErrnoError() << "Cannot find '" << args_[0] << "'";
}
std::string scon;
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
- LOG(INFO) << "computing context for service '" << name_ << "'";
- scon = ComputeContextFromExecutable(name_, args_[0]);
- if (scon == "") {
- return false;
+ auto result = ComputeContextFromExecutable(name_, args_[0]);
+ if (!result) {
+ return result.error();
}
+ scon = *result;
}
LOG(INFO) << "starting service '" << name_ << "'...";
@@ -662,8 +735,8 @@
SetUpPidNamespace(name_);
}
- for (const auto& ei : envvars_) {
- add_environment(ei.name.c_str(), ei.value.c_str());
+ for (const auto& [key, value] : environment_vars_) {
+ setenv(key.c_str(), value.c_str(), 1);
}
std::for_each(descriptors_.begin(), descriptors_.end(),
@@ -671,13 +744,13 @@
// See if there were "writepid" instructions to write to files under /dev/cpuset/.
auto cpuset_predicate = [](const std::string& path) {
- return android::base::StartsWith(path, "/dev/cpuset/");
+ return StartsWith(path, "/dev/cpuset/");
};
auto iter = std::find_if(writepid_files_.begin(), writepid_files_.end(), cpuset_predicate);
if (iter == writepid_files_.end()) {
// There were no "writepid" instructions for cpusets, check if the system default
// cpuset is specified to be used for the process.
- std::string default_cpuset = android::base::GetProperty("ro.cpuset.default", "");
+ std::string default_cpuset = GetProperty("ro.cpuset.default", "");
if (!default_cpuset.empty()) {
// Make sure the cpuset name starts and ends with '/'.
// A single '/' means the 'root' cpuset.
@@ -691,7 +764,7 @@
StringPrintf("/dev/cpuset%stasks", default_cpuset.c_str()));
}
}
- std::string pid_str = StringPrintf("%d", getpid());
+ std::string pid_str = std::to_string(getpid());
for (const auto& file : writepid_files_) {
if (!WriteStringToFile(pid_str, file)) {
PLOG(ERROR) << "couldn't write " << pid_str << " to " << file;
@@ -716,23 +789,20 @@
// priority. Aborts on failure.
SetProcessAttributes();
- std::vector<char*> strs;
- ExpandArgs(args_, &strs);
- if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
- PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
+ if (!ExpandArgsAndExecv(args_)) {
+ PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
}
_exit(127);
}
if (pid < 0) {
- PLOG(ERROR) << "failed to fork for '" << name_ << "'";
pid_ = 0;
- return false;
+ return ErrnoError() << "Failed to fork";
}
if (oom_score_adjust_ != -1000) {
- std::string oom_str = StringPrintf("%d", oom_score_adjust_);
+ std::string oom_str = std::to_string(oom_score_adjust_);
std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
if (!WriteStringToFile(oom_str, oom_file)) {
PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno);
@@ -742,38 +812,52 @@
time_started_ = boot_clock::now();
pid_ = pid;
flags_ |= SVC_RUNNING;
+ start_order_ = next_start_order_++;
+ process_cgroup_empty_ = false;
errno = -createProcessGroup(uid_, pid_);
if (errno != 0) {
PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
<< name_ << "'";
- }
+ } else {
+ if (swappiness_ != -1) {
+ if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
+ PLOG(ERROR) << "setProcessGroupSwappiness failed";
+ }
+ }
- if ((flags_ & SVC_EXEC) != 0) {
- LOG(INFO) << android::base::StringPrintf(
- "SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...", pid_, uid_, gid_,
- supp_gids_.size(), !seclabel_.empty() ? seclabel_.c_str() : "default");
+ if (soft_limit_in_bytes_ != -1) {
+ if (!setProcessGroupSoftLimit(uid_, pid_, soft_limit_in_bytes_)) {
+ PLOG(ERROR) << "setProcessGroupSoftLimit failed";
+ }
+ }
+
+ if (limit_in_bytes_ != -1) {
+ if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) {
+ PLOG(ERROR) << "setProcessGroupLimit failed";
+ }
+ }
}
NotifyStateChange("running");
- return true;
+ return Success();
}
-bool Service::StartIfNotDisabled() {
+Result<Success> Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
- return true;
+ return Success();
}
-bool Service::Enable() {
+Result<Success> Service::Enable() {
flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED);
if (flags_ & SVC_DISABLED_START) {
return Start();
}
- return true;
+ return Success();
}
void Service::Reset() {
@@ -799,26 +883,12 @@
StopOrReset(SVC_RESTART);
} else if (!(flags_ & SVC_RESTARTING)) {
/* Just start the service since it's not running. */
- Start();
+ if (auto result = Start(); !result) {
+ LOG(ERROR) << "Could not restart '" << name_ << "': " << result.error();
+ }
} /* else: Service is restarting anyways. */
}
-void Service::RestartIfNeeded(time_t* process_needs_restart_at) {
- boot_clock::time_point now = boot_clock::now();
- boot_clock::time_point next_start = time_started_ + 5s;
- if (now > next_start) {
- flags_ &= (~SVC_RESTARTING);
- Start();
- return;
- }
-
- time_t next_start_time_t = time(nullptr) +
- time_t(std::chrono::duration_cast<std::chrono::seconds>(next_start - now).count());
- if (next_start_time_t < *process_needs_restart_at || *process_needs_restart_at == 0) {
- *process_needs_restart_at = next_start_time_t;
- }
-}
-
// The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART.
void Service::StopOrReset(int how) {
// The service is still SVC_RUNNING until its process exits, but if it has
@@ -864,55 +934,18 @@
close(fd);
}
-int ServiceManager::exec_count_ = 0;
+ServiceList::ServiceList() {}
-ServiceManager::ServiceManager() {
-}
-
-ServiceManager& ServiceManager::GetInstance() {
- static ServiceManager instance;
+ServiceList& ServiceList::GetInstance() {
+ static ServiceList instance;
return instance;
}
-void ServiceManager::AddService(std::unique_ptr<Service> service) {
- Service* old_service = FindServiceByName(service->name());
- if (old_service) {
- LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
- return;
- }
+void ServiceList::AddService(std::unique_ptr<Service> service) {
services_.emplace_back(std::move(service));
}
-bool ServiceManager::Exec(const std::vector<std::string>& args) {
- Service* svc = MakeExecOneshotService(args);
- if (!svc) {
- LOG(ERROR) << "Could not create exec service";
- return false;
- }
- if (!svc->ExecStart(&exec_waiter_)) {
- LOG(ERROR) << "Could not start exec service";
- ServiceManager::GetInstance().RemoveService(*svc);
- return false;
- }
- return true;
-}
-
-bool ServiceManager::ExecStart(const std::string& name) {
- Service* svc = FindServiceByName(name);
- if (!svc) {
- LOG(ERROR) << "ExecStart(" << name << "): Service not found";
- return false;
- }
- if (!svc->ExecStart(&exec_waiter_)) {
- LOG(ERROR) << "ExecStart(" << name << "): Could not start Service";
- return false;
- }
- return true;
-}
-
-bool ServiceManager::IsWaitingForExec() const { return exec_waiter_ != nullptr; }
-
-Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
+std::unique_ptr<Service> Service::MakeTemporaryOneshotService(const std::vector<std::string>& args) {
// Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
// SECLABEL can be a - to denote default
std::size_t command_arg = 1;
@@ -933,9 +966,11 @@
}
std::vector<std::string> str_args(args.begin() + command_arg, args.end());
- exec_count_++;
- std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
- unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY;
+ static size_t exec_count = 0;
+ exec_count++;
+ std::string name = "exec " + std::to_string(exec_count) + " (" + Join(str_args, " ") + ")";
+
+ unsigned flags = SVC_ONESHOT | SVC_TEMPORARY;
CapSet no_capabilities;
unsigned namespace_flags = 0;
@@ -943,87 +978,50 @@
if (command_arg > 2 && args[1] != "-") {
seclabel = args[1];
}
- uid_t uid = 0;
+ Result<uid_t> uid = 0;
if (command_arg > 3) {
- uid = decode_uid(args[2].c_str());
+ uid = DecodeUid(args[2]);
+ if (!uid) {
+ LOG(ERROR) << "Unable to decode UID for '" << args[2] << "': " << uid.error();
+ return nullptr;
+ }
}
- gid_t gid = 0;
+ Result<gid_t> gid = 0;
std::vector<gid_t> supp_gids;
if (command_arg > 4) {
- gid = decode_uid(args[3].c_str());
+ gid = DecodeUid(args[3]);
+ if (!gid) {
+ LOG(ERROR) << "Unable to decode GID for '" << args[3] << "': " << gid.error();
+ return nullptr;
+ }
std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
for (size_t i = 0; i < nr_supp_gids; ++i) {
- supp_gids.push_back(decode_uid(args[4 + i].c_str()));
+ auto supp_gid = DecodeUid(args[4 + i]);
+ if (!supp_gid) {
+ LOG(ERROR) << "Unable to decode GID for '" << args[4 + i]
+ << "': " << supp_gid.error();
+ return nullptr;
+ }
+ supp_gids.push_back(*supp_gid);
}
}
- auto svc_p = std::make_unique<Service>(name, flags, uid, gid, supp_gids, no_capabilities,
- namespace_flags, seclabel, str_args);
- Service* svc = svc_p.get();
- services_.emplace_back(std::move(svc_p));
-
- return svc;
+ return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, no_capabilities,
+ namespace_flags, seclabel, str_args);
}
-Service* ServiceManager::FindServiceByName(const std::string& name) const {
- auto svc = std::find_if(services_.begin(), services_.end(),
- [&name] (const std::unique_ptr<Service>& s) {
- return name == s->name();
- });
- if (svc != services_.end()) {
- return svc->get();
+// Shutdown services in the opposite order that they were started.
+const std::vector<Service*> ServiceList::services_in_shutdown_order() const {
+ std::vector<Service*> shutdown_services;
+ for (const auto& service : services_) {
+ if (service->start_order() > 0) shutdown_services.emplace_back(service.get());
}
- return nullptr;
+ std::sort(shutdown_services.begin(), shutdown_services.end(),
+ [](const auto& a, const auto& b) { return a->start_order() > b->start_order(); });
+ return shutdown_services;
}
-Service* ServiceManager::FindServiceByPid(pid_t pid) const {
- auto svc = std::find_if(services_.begin(), services_.end(),
- [&pid] (const std::unique_ptr<Service>& s) {
- return s->pid() == pid;
- });
- if (svc != services_.end()) {
- return svc->get();
- }
- return nullptr;
-}
-
-Service* ServiceManager::FindServiceByKeychord(int keychord_id) const {
- auto svc = std::find_if(services_.begin(), services_.end(),
- [&keychord_id] (const std::unique_ptr<Service>& s) {
- return s->keychord_id() == keychord_id;
- });
-
- if (svc != services_.end()) {
- return svc->get();
- }
- return nullptr;
-}
-
-void ServiceManager::ForEachService(const std::function<void(Service*)>& callback) const {
- for (const auto& s : services_) {
- callback(s.get());
- }
-}
-
-void ServiceManager::ForEachServiceInClass(const std::string& classname,
- void (*func)(Service* svc)) const {
- for (const auto& s : services_) {
- if (s->classnames().find(classname) != s->classnames().end()) {
- func(s.get());
- }
- }
-}
-
-void ServiceManager::ForEachServiceWithFlags(unsigned matchflags,
- void (*func)(Service* svc)) const {
- for (const auto& s : services_) {
- if (s->flags() & matchflags) {
- func(s.get());
- }
- }
-}
-
-void ServiceManager::RemoveService(const Service& svc) {
+void ServiceList::RemoveService(const Service& svc) {
auto svc_it = std::find_if(services_.begin(), services_.end(),
[&svc] (const std::unique_ptr<Service>& s) {
return svc.name() == s->name();
@@ -1035,95 +1033,40 @@
services_.erase(svc_it);
}
-void ServiceManager::DumpState() const {
+void ServiceList::DumpState() const {
for (const auto& s : services_) {
s->DumpState();
}
}
-bool ServiceManager::ReapOneProcess() {
- int status;
- pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
- if (pid == 0) {
- return false;
- } else if (pid == -1) {
- PLOG(ERROR) << "waitpid failed";
- return false;
- }
-
- Service* svc = FindServiceByPid(pid);
-
- std::string name;
- std::string wait_string;
- if (svc) {
- name = android::base::StringPrintf("Service '%s' (pid %d)",
- svc->name().c_str(), pid);
- if (svc->flags() & SVC_EXEC) {
- wait_string =
- android::base::StringPrintf(" waiting took %f seconds", exec_waiter_->duration_s());
- }
- } else {
- name = android::base::StringPrintf("Untracked pid %d", pid);
- }
-
- if (WIFEXITED(status)) {
- LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
- } else if (WIFSIGNALED(status)) {
- LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
- } else if (WIFSTOPPED(status)) {
- LOG(INFO) << name << " stopped by signal " << WSTOPSIG(status) << wait_string;
- } else {
- LOG(INFO) << name << " state changed" << wait_string;
- }
-
- if (!svc) {
- return true;
- }
-
- svc->Reap();
-
- if (svc->flags() & SVC_EXEC) {
- exec_waiter_.reset();
- }
- if (svc->flags() & SVC_TEMPORARY) {
- RemoveService(*svc);
- }
-
- return true;
-}
-
-void ServiceManager::ReapAnyOutstandingChildren() {
- while (ReapOneProcess()) {
- }
-}
-
-bool ServiceParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
if (args.size() < 3) {
- *err = "services must have a name and a program";
- return false;
+ return Error() << "services must have a name and a program";
}
const std::string& name = args[1];
if (!IsValidName(name)) {
- *err = StringPrintf("invalid service name '%s'", name.c_str());
- return false;
+ return Error() << "invalid service name '" << name << "'";
+ }
+
+ Service* old_service = service_list_->FindService(name);
+ if (old_service) {
+ return Error() << "ignored duplicate definition of service '" << name << "'";
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name, str_args);
- return true;
+ return Success();
}
-bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const {
- return service_ ? service_->ParseLine(args, err) : false;
+Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ return service_ ? service_->ParseLine(std::move(args)) : Success();
}
void ServiceParser::EndSection() {
if (service_) {
- ServiceManager::GetInstance().AddService(std::move(service_));
+ service_list_->AddService(std::move(service_));
}
}
@@ -1134,3 +1077,6 @@
// the service name to the "ctl.start" and "ctl.stop" properties.)
return is_legal_property_name("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
}
+
+} // namespace init
+} // namespace android
diff --git a/init/service.h b/init/service.h
index f93ac38..67542ca 100644
--- a/init/service.h
+++ b/init/service.h
@@ -17,21 +17,22 @@
#ifndef _INIT_SERVICE_H
#define _INIT_SERVICE_H
+#include <sys/resource.h>
#include <sys/types.h>
-#include <cutils/iosched_policy.h>
-
#include <memory>
#include <set>
#include <string>
#include <vector>
+#include <android-base/chrono_utils.h>
+#include <cutils/iosched_policy.h>
+
#include "action.h"
#include "capabilities.h"
#include "descriptors.h"
-#include "init_parser.h"
#include "keyword_map.h"
-#include "util.h"
+#include "parser.h"
#define SVC_DISABLED 0x001 // do not autostart with class
#define SVC_ONESHOT 0x002 // do not restart on exit
@@ -54,15 +55,8 @@
#define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups
-class Action;
-class ServiceManager;
-
-struct ServiceEnvironmentInfo {
- ServiceEnvironmentInfo();
- ServiceEnvironmentInfo(const std::string& name, const std::string& value);
- std::string name;
- std::string value;
-};
+namespace android {
+namespace init {
class Service {
public:
@@ -73,26 +67,34 @@
unsigned namespace_flags, const std::string& seclabel,
const std::vector<std::string>& args);
+ static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
+
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
- bool ParseLine(const std::vector<std::string>& args, std::string* err);
- bool ExecStart(std::unique_ptr<Timer>* exec_waiter);
- bool Start();
- bool StartIfNotDisabled();
- bool Enable();
+ Result<Success> ParseLine(const std::vector<std::string>& args);
+ Result<Success> ExecStart();
+ Result<Success> Start();
+ Result<Success> StartIfNotDisabled();
+ Result<Success> Enable();
void Reset();
void Stop();
void Terminate();
void Restart();
- void RestartIfNeeded(time_t* process_needs_restart_at);
void Reap();
void DumpState() const;
void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
+ void UnSetExec() {
+ is_exec_service_running_ = false;
+ flags_ &= ~SVC_EXEC;
+ }
+
+ static bool is_exec_service_running() { return is_exec_service_running_; }
const std::string& name() const { return name_; }
const std::set<std::string>& classnames() const { return classnames_; }
unsigned flags() const { return flags_; }
pid_t pid() const { return pid_; }
+ android::base::boot_clock::time_point time_started() const { return time_started_; }
int crash_count() const { return crash_count_; }
uid_t uid() const { return uid_; }
gid_t gid() const { return gid_; }
@@ -106,11 +108,12 @@
int ioprio_pri() const { return ioprio_pri_; }
int priority() const { return priority_; }
int oom_score_adjust() const { return oom_score_adjust_; }
+ bool process_cgroup_empty() const { return process_cgroup_empty_; }
+ unsigned long start_order() const { return start_order_; }
const std::vector<std::string>& args() const { return args_; }
private:
- using OptionParser = bool (Service::*) (const std::vector<std::string>& args,
- std::string* err);
+ using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
class OptionParserMap;
void NotifyStateChange(const std::string& new_state) const;
@@ -120,28 +123,36 @@
void KillProcessGroup(int signal);
void SetProcessAttributes();
- bool ParseCapabilities(const std::vector<std::string>& args, std::string *err);
- bool ParseClass(const std::vector<std::string>& args, std::string* err);
- bool ParseConsole(const std::vector<std::string>& args, std::string* err);
- bool ParseCritical(const std::vector<std::string>& args, std::string* err);
- bool ParseDisabled(const std::vector<std::string>& args, std::string* err);
- bool ParseGroup(const std::vector<std::string>& args, std::string* err);
- bool ParsePriority(const std::vector<std::string>& args, std::string* err);
- bool ParseIoprio(const std::vector<std::string>& args, std::string* err);
- bool ParseKeycodes(const std::vector<std::string>& args, std::string* err);
- bool ParseOneshot(const std::vector<std::string>& args, std::string* err);
- bool ParseOnrestart(const std::vector<std::string>& args, std::string* err);
- bool ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err);
- bool ParseNamespace(const std::vector<std::string>& args, std::string* err);
- bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
- bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
- bool ParseSocket(const std::vector<std::string>& args, std::string* err);
- bool ParseFile(const std::vector<std::string>& args, std::string* err);
- bool ParseUser(const std::vector<std::string>& args, std::string* err);
- bool ParseWritepid(const std::vector<std::string>& args, std::string* err);
+ Result<Success> ParseCapabilities(const std::vector<std::string>& args);
+ Result<Success> ParseClass(const std::vector<std::string>& args);
+ Result<Success> ParseConsole(const std::vector<std::string>& args);
+ Result<Success> ParseCritical(const std::vector<std::string>& args);
+ Result<Success> ParseDisabled(const std::vector<std::string>& args);
+ Result<Success> ParseGroup(const std::vector<std::string>& args);
+ Result<Success> ParsePriority(const std::vector<std::string>& args);
+ Result<Success> ParseIoprio(const std::vector<std::string>& args);
+ Result<Success> ParseKeycodes(const std::vector<std::string>& args);
+ Result<Success> ParseOneshot(const std::vector<std::string>& args);
+ Result<Success> ParseOnrestart(const std::vector<std::string>& args);
+ Result<Success> ParseOomScoreAdjust(const std::vector<std::string>& args);
+ Result<Success> ParseMemcgLimitInBytes(const std::vector<std::string>& args);
+ Result<Success> ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args);
+ Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
+ Result<Success> ParseNamespace(const std::vector<std::string>& args);
+ Result<Success> ParseProcessRlimit(const std::vector<std::string>& args);
+ Result<Success> ParseSeclabel(const std::vector<std::string>& args);
+ Result<Success> ParseSetenv(const std::vector<std::string>& args);
+ Result<Success> ParseShutdown(const std::vector<std::string>& args);
+ Result<Success> ParseSocket(const std::vector<std::string>& args);
+ Result<Success> ParseFile(const std::vector<std::string>& args);
+ Result<Success> ParseUser(const std::vector<std::string>& args);
+ Result<Success> ParseWritepid(const std::vector<std::string>& args);
template <typename T>
- bool AddDescriptor(const std::vector<std::string>& args, std::string* err);
+ Result<Success> AddDescriptor(const std::vector<std::string>& args);
+
+ static unsigned long next_start_order_;
+ static bool is_exec_service_running_;
std::string name_;
std::set<std::string> classnames_;
@@ -149,8 +160,8 @@
unsigned flags_;
pid_t pid_;
- boot_clock::time_point time_started_; // time of last start
- boot_clock::time_point time_crashed_; // first crash within inspection window
+ android::base::boot_clock::time_point time_started_; // time of last start
+ android::base::boot_clock::time_point time_crashed_; // first crash within inspection window
int crash_count_; // number of times crashed within window
uid_t uid_;
@@ -162,7 +173,7 @@
std::string seclabel_;
std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
- std::vector<ServiceEnvironmentInfo> envvars_;
+ std::vector<std::pair<std::string, std::string>> environment_vars_;
Action onrestart_; // Commands to execute on restart.
@@ -178,60 +189,68 @@
int oom_score_adjust_;
+ int swappiness_;
+ int soft_limit_in_bytes_;
+ int limit_in_bytes_;
+
+ bool process_cgroup_empty_ = false;
+
+ unsigned long start_order_;
+
+ std::vector<std::pair<int, rlimit>> rlimits_;
+
std::vector<std::string> args_;
};
-class ServiceManager {
-public:
- static ServiceManager& GetInstance();
+class ServiceList {
+ public:
+ static ServiceList& GetInstance();
// Exposed for testing
- ServiceManager();
+ ServiceList();
void AddService(std::unique_ptr<Service> service);
- Service* MakeExecOneshotService(const std::vector<std::string>& args);
- bool Exec(const std::vector<std::string>& args);
- bool ExecStart(const std::string& name);
- bool IsWaitingForExec() const;
- Service* FindServiceByName(const std::string& name) const;
- Service* FindServiceByPid(pid_t pid) const;
- Service* FindServiceByKeychord(int keychord_id) const;
- void ForEachService(const std::function<void(Service*)>& callback) const;
- void ForEachServiceInClass(const std::string& classname,
- void (*func)(Service* svc)) const;
- void ForEachServiceWithFlags(unsigned matchflags,
- void (*func)(Service* svc)) const;
- void ReapAnyOutstandingChildren();
void RemoveService(const Service& svc);
+
+ template <typename T, typename F = decltype(&Service::name)>
+ Service* FindService(T value, F function = &Service::name) const {
+ auto svc = std::find_if(services_.begin(), services_.end(),
+ [&function, &value](const std::unique_ptr<Service>& s) {
+ return std::invoke(function, s) == value;
+ });
+ if (svc != services_.end()) {
+ return svc->get();
+ }
+ return nullptr;
+ }
+
void DumpState() const;
-private:
- // Cleans up a child process that exited.
- // Returns true iff a children was cleaned up.
- bool ReapOneProcess();
+ auto begin() const { return services_.begin(); }
+ auto end() const { return services_.end(); }
+ const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
+ const std::vector<Service*> services_in_shutdown_order() const;
- static int exec_count_; // Every service needs a unique name.
- std::unique_ptr<Timer> exec_waiter_;
-
+ private:
std::vector<std::unique_ptr<Service>> services_;
};
class ServiceParser : public SectionParser {
-public:
- ServiceParser() : service_(nullptr) {
- }
- bool ParseSection(const std::vector<std::string>& args,
- std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override;
+ public:
+ ServiceParser(ServiceList* service_list) : service_list_(service_list), service_(nullptr) {}
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
void EndSection() override;
- void EndFile(const std::string&) override {
- }
-private:
+
+ private:
bool IsValidName(const std::string& name) const;
+ ServiceList* service_list_;
std::unique_ptr<Service> service_;
};
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 4493f25..98d876f 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -23,6 +23,11 @@
#include <gtest/gtest.h>
+#include "util.h"
+
+namespace android {
+namespace init {
+
TEST(service, pod_initialized) {
constexpr auto memory_size = sizeof(Service);
alignas(alignof(Service)) char old_memory[memory_size];
@@ -45,6 +50,7 @@
EXPECT_EQ(0, service_in_old_memory->ioprio_pri());
EXPECT_EQ(0, service_in_old_memory->priority());
EXPECT_EQ(-1000, service_in_old_memory->oom_score_adjust());
+ EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
for (std::size_t i = 0; i < memory_size; ++i) {
old_memory[i] = 0xFF;
@@ -64,4 +70,123 @@
EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());
EXPECT_EQ(0, service_in_old_memory2->priority());
EXPECT_EQ(-1000, service_in_old_memory2->oom_score_adjust());
+ EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
}
+
+TEST(service, make_temporary_oneshot_service_invalid_syntax) {
+ std::vector<std::string> args;
+ // Nothing.
+ ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+
+ // No arguments to 'exec'.
+ args.push_back("exec");
+ ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+
+ // No command in "exec --".
+ args.push_back("--");
+ ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+}
+
+TEST(service, make_temporary_oneshot_service_too_many_supplementary_gids) {
+ std::vector<std::string> args;
+ args.push_back("exec");
+ args.push_back("seclabel");
+ args.push_back("root"); // uid.
+ args.push_back("root"); // gid.
+ for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
+ args.push_back("root"); // Supplementary gid.
+ }
+ args.push_back("--");
+ args.push_back("/system/bin/id");
+ ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+}
+
+static void Test_make_temporary_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,
+ bool supplementary_gids) {
+ std::vector<std::string> args;
+ args.push_back("exec");
+ if (seclabel) {
+ args.push_back("u:r:su:s0"); // seclabel
+ if (uid) {
+ args.push_back("log"); // uid
+ if (gid) {
+ args.push_back("shell"); // gid
+ if (supplementary_gids) {
+ args.push_back("system"); // supplementary gid 0
+ args.push_back("adb"); // supplementary gid 1
+ }
+ }
+ }
+ }
+ if (dash_dash) {
+ args.push_back("--");
+ }
+ args.push_back("/system/bin/toybox");
+ args.push_back("id");
+ auto svc = Service::MakeTemporaryOneshotService(args);
+ ASSERT_NE(nullptr, svc);
+
+ if (seclabel) {
+ ASSERT_EQ("u:r:su:s0", svc->seclabel());
+ } else {
+ ASSERT_EQ("", svc->seclabel());
+ }
+ if (uid) {
+ auto decoded_uid = DecodeUid("log");
+ ASSERT_TRUE(decoded_uid);
+ ASSERT_EQ(*decoded_uid, svc->uid());
+ } else {
+ ASSERT_EQ(0U, svc->uid());
+ }
+ if (gid) {
+ auto decoded_uid = DecodeUid("shell");
+ ASSERT_TRUE(decoded_uid);
+ ASSERT_EQ(*decoded_uid, svc->gid());
+ } else {
+ ASSERT_EQ(0U, svc->gid());
+ }
+ if (supplementary_gids) {
+ ASSERT_EQ(2U, svc->supp_gids().size());
+
+ auto decoded_uid = DecodeUid("system");
+ ASSERT_TRUE(decoded_uid);
+ ASSERT_EQ(*decoded_uid, svc->supp_gids()[0]);
+
+ decoded_uid = DecodeUid("adb");
+ ASSERT_TRUE(decoded_uid);
+ ASSERT_EQ(*decoded_uid, svc->supp_gids()[1]);
+ } else {
+ ASSERT_EQ(0U, svc->supp_gids().size());
+ }
+
+ ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
+ ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
+ ASSERT_EQ("id", svc->args()[1]);
+}
+
+TEST(service, make_temporary_oneshot_service_with_everything) {
+ Test_make_temporary_oneshot_service(true, true, true, true, true);
+}
+
+TEST(service, make_temporary_oneshot_service_with_seclabel_uid_gid) {
+ Test_make_temporary_oneshot_service(true, true, true, true, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_seclabel_uid) {
+ Test_make_temporary_oneshot_service(true, true, true, false, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_seclabel) {
+ Test_make_temporary_oneshot_service(true, true, false, false, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_just_command) {
+ Test_make_temporary_oneshot_service(true, false, false, false, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_just_command_no_dash) {
+ Test_make_temporary_oneshot_service(false, false, false, false, false);
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
new file mode 100644
index 0000000..8fc9956
--- /dev/null
+++ b/init/sigchld_handler.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 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 "sigchld_handler.h"
+
+#include <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
+
+#include "init.h"
+#include "property_service.h"
+#include "service.h"
+
+using android::base::StringPrintf;
+using android::base::boot_clock;
+using android::base::make_scope_guard;
+
+namespace android {
+namespace init {
+
+static int signal_write_fd = -1;
+static int signal_read_fd = -1;
+
+static bool ReapOneProcess() {
+ siginfo_t siginfo = {};
+ // This returns a zombie pid or informs us that there are no zombies left to be reaped.
+ // It does NOT reap the pid; that is done below.
+ if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
+ PLOG(ERROR) << "waitid failed";
+ return false;
+ }
+
+ auto pid = siginfo.si_pid;
+ if (pid == 0) return false;
+
+ // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
+ // whenever the function returns from this point forward.
+ // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
+ // want the pid to remain valid throughout that (and potentially future) usages.
+ auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
+
+ if (PropertyChildReap(pid)) return true;
+
+ Service* service = ServiceList::GetInstance().FindService(pid, &Service::pid);
+
+ std::string name;
+ std::string wait_string;
+ if (service) {
+ name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
+ if (service->flags() & SVC_EXEC) {
+ auto exec_duration = boot_clock::now() - service->time_started();
+ auto exec_duration_ms =
+ std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
+ wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
+ }
+ } else {
+ name = StringPrintf("Untracked pid %d", pid);
+ }
+
+ auto status = siginfo.si_status;
+ if (WIFEXITED(status)) {
+ LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
+ } else if (WIFSIGNALED(status)) {
+ LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
+ }
+
+ if (!service) return true;
+
+ service->Reap();
+
+ if (service->flags() & SVC_TEMPORARY) {
+ ServiceList::GetInstance().RemoveService(*service);
+ }
+
+ return true;
+}
+
+static void handle_signal() {
+ // Clear outstanding requests.
+ char buf[32];
+ read(signal_read_fd, buf, sizeof(buf));
+
+ ReapAnyOutstandingChildren();
+}
+
+static void SIGCHLD_handler(int) {
+ if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
+ PLOG(ERROR) << "write(signal_write_fd) failed";
+ }
+}
+
+void ReapAnyOutstandingChildren() {
+ while (ReapOneProcess()) {
+ }
+}
+
+void sigchld_handler_init() {
+ // Create a signalling mechanism for SIGCHLD.
+ int s[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
+ PLOG(ERROR) << "socketpair failed";
+ exit(1);
+ }
+
+ signal_write_fd = s[0];
+ signal_read_fd = s[1];
+
+ // Write to signal_write_fd if we catch SIGCHLD.
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIGCHLD_handler;
+ act.sa_flags = SA_NOCLDSTOP;
+ sigaction(SIGCHLD, &act, 0);
+
+ ReapAnyOutstandingChildren();
+
+ register_epoll_handler(signal_read_fd, handle_signal);
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/signal_handler.h b/init/sigchld_handler.h
similarity index 74%
rename from init/signal_handler.h
rename to init/sigchld_handler.h
index 449b4af..c86dc8d 100644
--- a/init/signal_handler.h
+++ b/init/sigchld_handler.h
@@ -14,9 +14,17 @@
* limitations under the License.
*/
-#ifndef _INIT_SIGNAL_HANDLER_H_
-#define _INIT_SIGNAL_HANDLER_H_
+#ifndef _INIT_SIGCHLD_HANDLER_H_
+#define _INIT_SIGCHLD_HANDLER_H_
-void signal_handler_init(void);
+namespace android {
+namespace init {
+
+void ReapAnyOutstandingChildren();
+
+void sigchld_handler_init(void);
+
+} // namespace init
+} // namespace android
#endif
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
deleted file mode 100644
index 5e3acac..0000000
--- a/init/signal_handler.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2010 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 <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <stdio.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <android-base/stringprintf.h>
-
-#include "action.h"
-#include "init.h"
-#include "log.h"
-#include "service.h"
-#include "util.h"
-
-static int signal_write_fd = -1;
-static int signal_read_fd = -1;
-
-static void handle_signal() {
- // Clear outstanding requests.
- char buf[32];
- read(signal_read_fd, buf, sizeof(buf));
-
- ServiceManager::GetInstance().ReapAnyOutstandingChildren();
-}
-
-static void SIGCHLD_handler(int) {
- if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
- PLOG(ERROR) << "write(signal_write_fd) failed";
- }
-}
-
-void signal_handler_init() {
- // Create a signalling mechanism for SIGCHLD.
- int s[2];
- if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
- PLOG(ERROR) << "socketpair failed";
- exit(1);
- }
-
- signal_write_fd = s[0];
- signal_read_fd = s[1];
-
- // Write to signal_write_fd if we catch SIGCHLD.
- struct sigaction act;
- memset(&act, 0, sizeof(act));
- act.sa_handler = SIGCHLD_handler;
- act.sa_flags = SA_NOCLDSTOP;
- sigaction(SIGCHLD, &act, 0);
-
- ServiceManager::GetInstance().ReapAnyOutstandingChildren();
-
- register_epoll_handler(signal_read_fd, handle_signal);
-}
diff --git a/init/test_service/Android.bp b/init/test_service/Android.bp
new file mode 100644
index 0000000..9bd6f27
--- /dev/null
+++ b/init/test_service/Android.bp
@@ -0,0 +1,22 @@
+//
+// 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.
+//
+
+cc_binary {
+ name: "test_service",
+ srcs: ["test_service.cpp"],
+ shared_libs: ["libbase"],
+ init_rc: ["test_service.rc"],
+}
diff --git a/init/test_service/Android.mk b/init/test_service/Android.mk
deleted file mode 100644
index 30c9e9d..0000000
--- a/init/test_service/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2016 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.
-
-LOCAL_PATH := $(call my-dir)
-
-# Sample service for testing.
-# =========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := test_service
-LOCAL_SRC_FILES := test_service.cpp
-
-LOCAL_SHARED_LIBRARIES += libbase
-
-LOCAL_INIT_RC := test_service.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/init/tokenizer.cpp b/init/tokenizer.cpp
new file mode 100644
index 0000000..f8d9b6b
--- /dev/null
+++ b/init/tokenizer.cpp
@@ -0,0 +1,124 @@
+#include "tokenizer.h"
+
+namespace android {
+namespace init {
+
+int next_token(struct parse_state *state)
+{
+ char *x = state->ptr;
+ char *s;
+
+ if (state->nexttoken) {
+ int t = state->nexttoken;
+ state->nexttoken = 0;
+ return t;
+ }
+
+ for (;;) {
+ switch (*x) {
+ case 0:
+ state->ptr = x;
+ return T_EOF;
+ case '\n':
+ x++;
+ state->ptr = x;
+ return T_NEWLINE;
+ case ' ':
+ case '\t':
+ case '\r':
+ x++;
+ continue;
+ case '#':
+ while (*x && (*x != '\n')) x++;
+ if (*x == '\n') {
+ state->ptr = x+1;
+ return T_NEWLINE;
+ } else {
+ state->ptr = x;
+ return T_EOF;
+ }
+ default:
+ goto text;
+ }
+ }
+
+textdone:
+ state->ptr = x;
+ *s = 0;
+ return T_TEXT;
+text:
+ state->text = s = x;
+textresume:
+ for (;;) {
+ switch (*x) {
+ case 0:
+ goto textdone;
+ case ' ':
+ case '\t':
+ case '\r':
+ x++;
+ goto textdone;
+ case '\n':
+ state->nexttoken = T_NEWLINE;
+ x++;
+ goto textdone;
+ case '"':
+ x++;
+ for (;;) {
+ switch (*x) {
+ case 0:
+ /* unterminated quoted thing */
+ state->ptr = x;
+ return T_EOF;
+ case '"':
+ x++;
+ goto textresume;
+ default:
+ *s++ = *x++;
+ }
+ }
+ break;
+ case '\\':
+ x++;
+ switch (*x) {
+ case 0:
+ goto textdone;
+ case 'n':
+ *s++ = '\n';
+ break;
+ case 'r':
+ *s++ = '\r';
+ break;
+ case 't':
+ *s++ = '\t';
+ break;
+ case '\\':
+ *s++ = '\\';
+ break;
+ case '\r':
+ /* \ <cr> <lf> -> line continuation */
+ if (x[1] != '\n') {
+ x++;
+ continue;
+ }
+ case '\n':
+ /* \ <lf> -> line continuation */
+ state->line++;
+ x++;
+ /* eat any extra whitespace */
+ while((*x == ' ') || (*x == '\t')) x++;
+ continue;
+ default:
+ /* unknown escape -- just copy */
+ *s++ = *x++;
+ }
+ continue;
+ default:
+ *s++ = *x++;
+ }
+ }
+ return T_EOF;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/signal_handler.h b/init/tokenizer.h
similarity index 65%
copy from init/signal_handler.h
copy to init/tokenizer.h
index 449b4af..72c08ef 100644
--- a/init/signal_handler.h
+++ b/init/tokenizer.h
@@ -14,9 +14,27 @@
* limitations under the License.
*/
-#ifndef _INIT_SIGNAL_HANDLER_H_
-#define _INIT_SIGNAL_HANDLER_H_
+#ifndef _INIT_TOKENIZER_H_
+#define _INIT_TOKENIZER_H_
-void signal_handler_init(void);
+#define T_EOF 0
+#define T_TEXT 1
+#define T_NEWLINE 2
+
+namespace android {
+namespace init {
+
+struct parse_state
+{
+ char *ptr;
+ char *text;
+ int line;
+ int nexttoken;
+};
+
+int next_token(struct parse_state *state);
+
+} // namespace init
+} // namespace android
#endif
diff --git a/init/uevent.h b/init/uevent.h
new file mode 100644
index 0000000..c4fd945
--- /dev/null
+++ b/init/uevent.h
@@ -0,0 +1,40 @@
+/*
+ * 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 _INIT_UEVENT_H
+#define _INIT_UEVENT_H
+
+#include <string>
+
+namespace android {
+namespace init {
+
+struct Uevent {
+ std::string action;
+ std::string path;
+ std::string subsystem;
+ std::string firmware;
+ std::string partition_name;
+ std::string device_name;
+ int partition_num;
+ int major;
+ int minor;
+};
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
new file mode 100644
index 0000000..ac1d7c7
--- /dev/null
+++ b/init/uevent_listener.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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 "uevent_listener.h"
+
+#include <fcntl.h>
+#include <poll.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+#include <cutils/uevent.h>
+
+namespace android {
+namespace init {
+
+static void ParseEvent(const char* msg, Uevent* uevent) {
+ uevent->partition_num = -1;
+ uevent->major = -1;
+ uevent->minor = -1;
+ uevent->action.clear();
+ uevent->path.clear();
+ uevent->subsystem.clear();
+ uevent->firmware.clear();
+ uevent->partition_name.clear();
+ uevent->device_name.clear();
+ // currently ignoring SEQNUM
+ while (*msg) {
+ if (!strncmp(msg, "ACTION=", 7)) {
+ msg += 7;
+ uevent->action = msg;
+ } else if (!strncmp(msg, "DEVPATH=", 8)) {
+ msg += 8;
+ uevent->path = msg;
+ } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
+ msg += 10;
+ uevent->subsystem = msg;
+ } else if (!strncmp(msg, "FIRMWARE=", 9)) {
+ msg += 9;
+ uevent->firmware = msg;
+ } else if (!strncmp(msg, "MAJOR=", 6)) {
+ msg += 6;
+ uevent->major = atoi(msg);
+ } else if (!strncmp(msg, "MINOR=", 6)) {
+ msg += 6;
+ uevent->minor = atoi(msg);
+ } else if (!strncmp(msg, "PARTN=", 6)) {
+ msg += 6;
+ uevent->partition_num = atoi(msg);
+ } else if (!strncmp(msg, "PARTNAME=", 9)) {
+ msg += 9;
+ uevent->partition_name = msg;
+ } else if (!strncmp(msg, "DEVNAME=", 8)) {
+ msg += 8;
+ uevent->device_name = msg;
+ }
+
+ // advance to after the next \0
+ while (*msg++)
+ ;
+ }
+
+ if (LOG_UEVENTS) {
+ LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
+ << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
+ << ", " << uevent->minor << " }";
+ }
+}
+
+UeventListener::UeventListener() {
+ // is 256K enough? udev uses 16MB!
+ device_fd_.reset(uevent_open_socket(256 * 1024, true));
+ if (device_fd_ == -1) {
+ LOG(FATAL) << "Could not open uevent socket";
+ }
+
+ fcntl(device_fd_, F_SETFL, O_NONBLOCK);
+}
+
+bool UeventListener::ReadUevent(Uevent* uevent) const {
+ char msg[UEVENT_MSG_LEN + 2];
+ int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);
+ if (n <= 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ LOG(ERROR) << "Error reading from Uevent Fd";
+ }
+ return false;
+ }
+ if (n >= UEVENT_MSG_LEN) {
+ LOG(ERROR) << "Uevent overflowed buffer, discarding";
+ // Return true here even if we discard as we may have more uevents pending and we
+ // want to keep processing them.
+ return true;
+ }
+
+ msg[n] = '\0';
+ msg[n + 1] = '\0';
+
+ ParseEvent(msg, uevent);
+
+ return true;
+}
+
+// RegenerateUevents*() walks parts of the /sys tree and pokes the uevent files to cause the kernel
+// to regenerate device add uevents that have already happened. This is particularly useful when
+// starting ueventd, to regenerate all of the uevents that it had previously missed.
+//
+// We drain any pending events from the netlink socket every time we poke another uevent file to
+// make sure we don't overrun the socket's buffer.
+//
+
+ListenerAction UeventListener::RegenerateUeventsForDir(DIR* d,
+ const ListenerCallback& callback) const {
+ int dfd = dirfd(d);
+
+ int fd = openat(dfd, "uevent", O_WRONLY);
+ if (fd >= 0) {
+ write(fd, "add\n", 4);
+ close(fd);
+
+ Uevent uevent;
+ while (ReadUevent(&uevent)) {
+ if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;
+ }
+ }
+
+ dirent* de;
+ while ((de = readdir(d)) != nullptr) {
+ if (de->d_type != DT_DIR || de->d_name[0] == '.') continue;
+
+ fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+ if (fd < 0) continue;
+
+ std::unique_ptr<DIR, decltype(&closedir)> d2(fdopendir(fd), closedir);
+ if (d2 == 0) {
+ close(fd);
+ } else {
+ if (RegenerateUeventsForDir(d2.get(), callback) == ListenerAction::kStop) {
+ return ListenerAction::kStop;
+ }
+ }
+ }
+
+ // default is always to continue looking for uevents
+ return ListenerAction::kContinue;
+}
+
+ListenerAction UeventListener::RegenerateUeventsForPath(const std::string& path,
+ const ListenerCallback& callback) const {
+ std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
+ if (!d) return ListenerAction::kContinue;
+
+ return RegenerateUeventsForDir(d.get(), callback);
+}
+
+static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
+
+void UeventListener::RegenerateUevents(const ListenerCallback& callback) const {
+ for (const auto path : kRegenerationPaths) {
+ if (RegenerateUeventsForPath(path, callback) == ListenerAction::kStop) return;
+ }
+}
+
+void UeventListener::Poll(const ListenerCallback& callback,
+ const std::optional<std::chrono::milliseconds> relative_timeout) const {
+ using namespace std::chrono;
+
+ pollfd ufd;
+ ufd.events = POLLIN;
+ ufd.fd = device_fd_;
+
+ auto start_time = steady_clock::now();
+
+ while (true) {
+ ufd.revents = 0;
+
+ int timeout_ms = -1;
+ if (relative_timeout) {
+ auto now = steady_clock::now();
+ auto time_elapsed = duration_cast<milliseconds>(now - start_time);
+ if (time_elapsed > *relative_timeout) return;
+
+ auto remaining_timeout = *relative_timeout - time_elapsed;
+ timeout_ms = remaining_timeout.count();
+ }
+
+ int nr = poll(&ufd, 1, timeout_ms);
+ if (nr == 0) return;
+ if (nr < 0) {
+ PLOG(ERROR) << "poll() of uevent socket failed, continuing";
+ continue;
+ }
+ if (ufd.revents & POLLIN) {
+ // We're non-blocking, so if we receive a poll event keep processing until
+ // we have exhausted all uevent messages.
+ Uevent uevent;
+ while (ReadUevent(&uevent)) {
+ if (callback(uevent) == ListenerAction::kStop) return;
+ }
+ }
+ }
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
new file mode 100644
index 0000000..5b453fe
--- /dev/null
+++ b/init/uevent_listener.h
@@ -0,0 +1,62 @@
+/*
+ * 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 _INIT_UEVENT_LISTENER_H
+#define _INIT_UEVENT_LISTENER_H
+
+#include <dirent.h>
+
+#include <chrono>
+#include <functional>
+#include <optional>
+
+#include <android-base/unique_fd.h>
+
+#include "uevent.h"
+
+#define UEVENT_MSG_LEN 2048
+
+namespace android {
+namespace init {
+
+enum class ListenerAction {
+ kStop = 0, // Stop regenerating uevents as we've handled the one(s) we're interested in.
+ kContinue, // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
+};
+
+using ListenerCallback = std::function<ListenerAction(const Uevent&)>;
+
+class UeventListener {
+ public:
+ UeventListener();
+
+ void RegenerateUevents(const ListenerCallback& callback) const;
+ ListenerAction RegenerateUeventsForPath(const std::string& path,
+ const ListenerCallback& callback) const;
+ void Poll(const ListenerCallback& callback,
+ const std::optional<std::chrono::milliseconds> relative_timeout = {}) const;
+
+ private:
+ bool ReadUevent(Uevent* uevent) const;
+ ListenerAction RegenerateUeventsForDir(DIR* d, const ListenerCallback& callback) const;
+
+ android::base::unique_fd device_fd_;
+};
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index f27be64..1435d82 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -14,55 +14,223 @@
* limitations under the License.
*/
+#include "ueventd.h"
+
#include <ctype.h>
#include <fcntl.h>
-#include <grp.h>
-#include <poll.h>
-#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/wait.h>
-#include <sys/types.h>
+#include <set>
+#include <thread>
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
+#include <selinux/android.h>
#include <selinux/selinux.h>
-#include "ueventd.h"
-#include "log.h"
-#include "util.h"
#include "devices.h"
+#include "firmware_handler.h"
+#include "log.h"
+#include "selinux.h"
+#include "uevent_listener.h"
#include "ueventd_parser.h"
+#include "util.h"
-int ueventd_main(int argc, char **argv)
-{
- /*
- * init sets the umask to 077 for forked processes. We need to
- * create files with exact permissions, without modification by
- * the umask.
- */
- umask(000);
+// At a high level, ueventd listens for uevent messages generated by the kernel through a netlink
+// socket. When ueventd receives such a message it handles it by taking appropriate actions,
+// which can typically be creating a device node in /dev, setting file permissions, setting selinux
+// labels, etc.
+// Ueventd also handles loading of firmware that the kernel requests, and creates symlinks for block
+// and character devices.
- /* Prevent fire-and-forget children from becoming zombies.
- * If we should need to wait() for some children in the future
- * (as opposed to none right now), double-forking here instead
- * of ignoring SIGCHLD may be the better solution.
- */
- signal(SIGCHLD, SIG_IGN);
+// When ueventd starts, it regenerates uevents for all currently registered devices by traversing
+// /sys and writing 'add' to each 'uevent' file that it finds. This causes the kernel to generate
+// and resend uevent messages for all of the currently registered devices. This is done, because
+// ueventd would not have been running when these devices were registered and therefore was unable
+// to receive their uevent messages and handle them appropriately. This process is known as
+// 'cold boot'.
- InitKernelLogging(argv);
+// 'init' currently waits synchronously on the cold boot process of ueventd before it continues
+// its boot process. For this reason, cold boot should be as quick as possible. One way to achieve
+// a speed up here is to parallelize the handling of ueventd messages, which consume the bulk of the
+// time during cold boot.
- LOG(INFO) << "ueventd started!";
+// Handling of uevent messages has two unique properties:
+// 1) It can be done in isolation; it doesn't need to read or write any status once it is started.
+// 2) It uses setegid() and setfscreatecon() so either care (aka locking) must be taken to ensure
+// that no file system operations are done while the uevent process has an abnormal egid or
+// fscreatecon or this handling must happen in a separate process.
+// Given the above two properties, it is best to fork() subprocesses to handle the uevents. This
+// reduces the overhead and complexity that would be required in a solution with threads and locks.
+// In testing, a racy multithreaded solution has the same performance as the fork() solution, so
+// there is no reason to deal with the complexity of the former.
- selinux_callback cb;
- cb.func_log = selinux_klog_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
+// One other important caveat during the boot process is the handling of SELinux restorecon.
+// Since many devices have child devices, calling selinux_android_restorecon() recursively for each
+// device when its uevent is handled, results in multiple restorecon operations being done on a
+// given file. It is more efficient to simply do restorecon recursively on /sys during cold boot,
+// than to do restorecon on each device as its uevent is handled. This only applies to cold boot;
+// once that has completed, restorecon is done for each device as its uevent is handled.
- ueventd_parse_config_file("/ueventd.rc");
- ueventd_parse_config_file("/vendor/ueventd.rc");
- ueventd_parse_config_file("/odm/ueventd.rc");
+// With all of the above considered, the cold boot process has the below steps:
+// 1) ueventd regenerates uevents by doing the /sys traversal and listens to the netlink socket for
+// the generated uevents. It writes these uevents into a queue represented by a vector.
+//
+// 2) ueventd forks 'n' separate uevent handler subprocesses and has each of them to handle the
+// uevents in the queue based on a starting offset (their process number) and a stride (the total
+// number of processes). Note that no IPC happens at this point and only const functions from
+// DeviceHandler should be called from this context.
+//
+// 3) In parallel to the subprocesses handling the uevents, the main thread of ueventd calls
+// selinux_android_restorecon() recursively on /sys/class, /sys/block, and /sys/devices.
+//
+// 4) Once the restorecon operation finishes, the main thread calls waitpid() to wait for all
+// subprocess handlers to complete and exit. Once this happens, it marks coldboot as having
+// completed.
+//
+// At this point, ueventd is single threaded, poll()'s and then handles any future uevents.
+
+// Lastly, it should be noted that uevents that occur during the coldboot process are handled
+// without issue after the coldboot process completes. This is because the uevent listener is
+// paused while the uevent handler and restorecon actions take place. Once coldboot completes,
+// the uevent listener resumes in polling mode and will handle the uevents that occurred during
+// coldboot.
+
+namespace android {
+namespace init {
+
+class ColdBoot {
+ public:
+ ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler)
+ : uevent_listener_(uevent_listener),
+ device_handler_(device_handler),
+ num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
+
+ void Run();
+
+ private:
+ void UeventHandlerMain(unsigned int process_num, unsigned int total_processes);
+ void RegenerateUevents();
+ void ForkSubProcesses();
+ void DoRestoreCon();
+ void WaitForSubProcesses();
+
+ UeventListener& uevent_listener_;
+ DeviceHandler& device_handler_;
+
+ unsigned int num_handler_subprocesses_;
+ std::vector<Uevent> uevent_queue_;
+
+ std::set<pid_t> subprocess_pids_;
+};
+
+void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
+ for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {
+ auto& uevent = uevent_queue_[i];
+ device_handler_.HandleDeviceEvent(uevent);
+ }
+ _exit(EXIT_SUCCESS);
+}
+
+void ColdBoot::RegenerateUevents() {
+ uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
+ HandleFirmwareEvent(uevent);
+
+ uevent_queue_.emplace_back(std::move(uevent));
+ return ListenerAction::kContinue;
+ });
+}
+
+void ColdBoot::ForkSubProcesses() {
+ for (unsigned int i = 0; i < num_handler_subprocesses_; ++i) {
+ auto pid = fork();
+ if (pid < 0) {
+ PLOG(FATAL) << "fork() failed!";
+ }
+
+ if (pid == 0) {
+ UeventHandlerMain(i, num_handler_subprocesses_);
+ }
+
+ subprocess_pids_.emplace(pid);
+ }
+}
+
+void ColdBoot::DoRestoreCon() {
+ selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
+ device_handler_.set_skip_restorecon(false);
+}
+
+void ColdBoot::WaitForSubProcesses() {
+ // Treat subprocesses that crash or get stuck the same as if ueventd itself has crashed or gets
+ // stuck.
+ //
+ // When a subprocess crashes, we fatally abort from ueventd. init will restart ueventd when
+ // init reaps it, and the cold boot process will start again. If this continues to fail, then
+ // since ueventd is marked as a critical service, init will reboot to recovery.
+ //
+ // When a subprocess gets stuck, keep ueventd spinning waiting for it. init has a timeout for
+ // cold boot and will reboot to the bootloader if ueventd does not complete in time.
+ while (!subprocess_pids_.empty()) {
+ int status;
+ pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, 0));
+ if (pid == -1) {
+ PLOG(ERROR) << "waitpid() failed";
+ continue;
+ }
+
+ auto it = std::find(subprocess_pids_.begin(), subprocess_pids_.end(), pid);
+ if (it == subprocess_pids_.end()) continue;
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) == EXIT_SUCCESS) {
+ subprocess_pids_.erase(it);
+ } else {
+ LOG(FATAL) << "subprocess exited with status " << WEXITSTATUS(status);
+ }
+ } else if (WIFSIGNALED(status)) {
+ LOG(FATAL) << "subprocess killed by signal " << WTERMSIG(status);
+ }
+ }
+}
+
+void ColdBoot::Run() {
+ android::base::Timer cold_boot_timer;
+
+ RegenerateUevents();
+
+ ForkSubProcesses();
+
+ DoRestoreCon();
+
+ WaitForSubProcesses();
+
+ close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+ LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
+}
+
+DeviceHandler CreateDeviceHandler() {
+ Parser parser;
+
+ std::vector<Subsystem> subsystems;
+ parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>(&subsystems));
+
+ using namespace std::placeholders;
+ std::vector<SysfsPermissions> sysfs_permissions;
+ std::vector<Permissions> dev_permissions;
+ parser.AddSingleLineParser("/sys/",
+ std::bind(ParsePermissionsLine, _1, &sysfs_permissions, nullptr));
+ parser.AddSingleLineParser("/dev/",
+ std::bind(ParsePermissionsLine, _1, nullptr, &dev_permissions));
+
+ parser.ParseConfig("/ueventd.rc");
+ parser.ParseConfig("/vendor/ueventd.rc");
+ parser.ParseConfig("/odm/ueventd.rc");
/*
* keep the current product name base configuration so
@@ -72,95 +240,50 @@
* device node entries (b/34968103)
*/
std::string hardware = android::base::GetProperty("ro.hardware", "");
- ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
+ parser.ParseConfig("/ueventd." + hardware + ".rc");
- device_init();
+ return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
+ std::move(subsystems), true);
+}
- pollfd ufd;
- ufd.events = POLLIN;
- ufd.fd = get_device_fd();
+int ueventd_main(int argc, char** argv) {
+ /*
+ * init sets the umask to 077 for forked processes. We need to
+ * create files with exact permissions, without modification by
+ * the umask.
+ */
+ umask(000);
- while (true) {
- ufd.revents = 0;
- int nr = poll(&ufd, 1, -1);
- if (nr <= 0) {
- continue;
- }
- if (ufd.revents & POLLIN) {
- handle_device_fd();
- }
+ InitKernelLogging(argv);
+
+ LOG(INFO) << "ueventd started!";
+
+ SelinuxSetupKernelLogging();
+ SelabelInitialize();
+
+ DeviceHandler device_handler = CreateDeviceHandler();
+ UeventListener uevent_listener;
+
+ if (access(COLDBOOT_DONE, F_OK) != 0) {
+ ColdBoot cold_boot(uevent_listener, device_handler);
+ cold_boot.Run();
}
+ // We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now.
+ signal(SIGCHLD, SIG_IGN);
+ // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN
+ // for SIGCHLD above.
+ while (waitpid(-1, nullptr, WNOHANG) > 0) {
+ }
+
+ uevent_listener.Poll([&device_handler](const Uevent& uevent) {
+ HandleFirmwareEvent(uevent);
+ device_handler.HandleDeviceEvent(uevent);
+ return ListenerAction::kContinue;
+ });
+
return 0;
}
-void set_device_permission(int nargs, char **args)
-{
- char *name;
- char *attr = 0;
- mode_t perm;
- uid_t uid;
- gid_t gid;
- int prefix = 0;
- int wildcard = 0;
- char *endptr;
-
- if (nargs == 0)
- return;
-
- if (args[0][0] == '#')
- return;
-
- name = args[0];
-
- if (!strncmp(name,"/sys/", 5) && (nargs == 5)) {
- LOG(INFO) << "/sys/ rule " << args[0] << " " << args[1];
- attr = args[1];
- args++;
- nargs--;
- }
-
- if (nargs != 4) {
- LOG(ERROR) << "invalid line ueventd.rc line for '" << args[0] << "'";
- return;
- }
-
- int len = strlen(name);
- char *wildcard_chr = strchr(name, '*');
- if ((name[len - 1] == '*') && (wildcard_chr == (name + len - 1))) {
- prefix = 1;
- name[len - 1] = '\0';
- } else if (wildcard_chr) {
- wildcard = 1;
- }
-
- perm = strtol(args[1], &endptr, 8);
- if (!endptr || *endptr != '\0') {
- LOG(ERROR) << "invalid mode '" << args[1] << "'";
- return;
- }
-
- struct passwd* pwd = getpwnam(args[2]);
- if (!pwd) {
- LOG(ERROR) << "invalid uid '" << args[2] << "'";
- return;
- }
- uid = pwd->pw_uid;
-
- struct group* grp = getgrnam(args[3]);
- if (!grp) {
- LOG(ERROR) << "invalid gid '" << args[3] << "'";
- return;
- }
- gid = grp->gr_gid;
-
- if (add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard) != 0) {
- PLOG(ERROR) << "add_dev_perms(name=" << name <<
- ", attr=" << attr <<
- ", perm=" << std::oct << perm << std::dec <<
- ", uid=" << uid << ", gid=" << gid <<
- ", prefix=" << prefix << ", wildcard=" << wildcard <<
- ")";
- return;
- }
-}
+} // namespace init
+} // namespace android
diff --git a/init/ueventd.h b/init/ueventd.h
index d12d7fe..51775ec 100644
--- a/init/ueventd.h
+++ b/init/ueventd.h
@@ -17,23 +17,12 @@
#ifndef _INIT_UEVENTD_H_
#define _INIT_UEVENTD_H_
-#include <cutils/list.h>
-#include <sys/types.h>
+namespace android {
+namespace init {
-enum devname_src_t {
- DEVNAME_UNKNOWN = 0,
- DEVNAME_UEVENT_DEVNAME,
- DEVNAME_UEVENT_DEVPATH,
-};
+int ueventd_main(int argc, char** argv);
-struct ueventd_subsystem {
- struct listnode slist;
-
- const char *name;
- const char *dirname;
- devname_src_t devname_src;
-};
-
-int ueventd_main(int argc, char **argv);
+} // namespace init
+} // namespace android
#endif
diff --git a/init/ueventd_keywords.h b/init/ueventd_keywords.h
deleted file mode 100644
index 88e8f01..0000000
--- a/init/ueventd_keywords.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef KEYWORD
-#define __MAKE_KEYWORD_ENUM__
-#define KEYWORD(symbol, flags, nargs) K_##symbol,
-enum {
- K_UNKNOWN,
-#endif
- KEYWORD(subsystem, SECTION, 1)
- KEYWORD(devname, OPTION, 1)
- KEYWORD(dirname, OPTION, 1)
-#ifdef __MAKE_KEYWORD_ENUM__
- KEYWORD_COUNT,
-};
-#undef __MAKE_KEYWORD_ENUM__
-#undef KEYWORD
-#endif
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index baff58c..cd7adb4 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * 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.
@@ -14,228 +14,127 @@
* limitations under the License.
*/
-#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "ueventd.h"
#include "ueventd_parser.h"
-#include "parser.h"
-#include "log.h"
-#include "util.h"
-static list_declare(subsystem_list);
+#include <grp.h>
+#include <pwd.h>
-static void parse_line_device(struct parse_state *state, int nargs, char **args);
+#include "keyword_map.h"
-#define SECTION 0x01
-#define OPTION 0x02
+namespace android {
+namespace init {
-#include "ueventd_keywords.h"
-
-#define KEYWORD(symbol, flags, nargs) \
- [ K_##symbol ] = { #symbol, (nargs) + 1, flags, },
-
-static struct {
- const char *name;
- unsigned char nargs;
- unsigned char flags;
-} keyword_info[KEYWORD_COUNT] = {
- [ K_UNKNOWN ] = { "unknown", 0, 0 },
-#include "ueventd_keywords.h"
-};
-#undef KEYWORD
-
-#define kw_is(kw, type) (keyword_info[kw].flags & (type))
-#define kw_nargs(kw) (keyword_info[kw].nargs)
-
-static int lookup_keyword(const char *s)
-{
- switch (*s++) {
- case 'd':
- if (!strcmp(s, "evname")) return K_devname;
- if (!strcmp(s, "irname")) return K_dirname;
- break;
- case 's':
- if (!strcmp(s, "ubsystem")) return K_subsystem;
- break;
- }
- return K_UNKNOWN;
-}
-
-static void parse_line_no_op(struct parse_state*, int, char**) {
-}
-
-static int valid_name(const char *name)
-{
- while (*name) {
- if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
- return 0;
- }
- name++;
- }
- return 1;
-}
-
-struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name)
-{
- struct listnode *node;
- struct ueventd_subsystem *s;
-
- list_for_each(node, &subsystem_list) {
- s = node_to_item(node, struct ueventd_subsystem, slist);
- if (!strcmp(s->name, name)) {
- return s;
- }
- }
- return 0;
-}
-
-static void *parse_subsystem(parse_state* state, int /*nargs*/, char** args) {
- if (!valid_name(args[1])) {
- parse_error(state, "invalid subsystem name '%s'\n", args[1]);
- return 0;
+Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
+ std::vector<SysfsPermissions>* out_sysfs_permissions,
+ std::vector<Permissions>* out_dev_permissions) {
+ bool is_sysfs = out_sysfs_permissions != nullptr;
+ if (is_sysfs && args.size() != 5) {
+ return Error() << "/sys/ lines must have 5 entries";
}
- ueventd_subsystem* s = ueventd_subsystem_find_by_name(args[1]);
- if (s) {
- parse_error(state, "ignored duplicate definition of subsystem '%s'\n",
- args[1]);
- return 0;
+ if (!is_sysfs && args.size() != 4) {
+ return Error() << "/dev/ lines must have 4 entries";
}
- s = (ueventd_subsystem*) calloc(1, sizeof(*s));
- if (!s) {
- parse_error(state, "out of memory\n");
- return 0;
- }
- s->name = args[1];
- s->dirname = "/dev";
- list_add_tail(&subsystem_list, &s->slist);
- return s;
-}
+ auto it = args.begin();
+ const std::string& name = *it++;
-static void parse_line_subsystem(struct parse_state *state, int nargs,
- char **args)
-{
- struct ueventd_subsystem *s = (ueventd_subsystem*) state->context;
- int kw;
+ std::string sysfs_attribute;
+ if (is_sysfs) sysfs_attribute = *it++;
- if (nargs == 0) {
- return;
+ // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
+ std::string& perm_string = *it++;
+ char* end_pointer = 0;
+ mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
+ if (end_pointer == nullptr || *end_pointer != '\0') {
+ return Error() << "invalid mode '" << perm_string << "'";
}
- kw = lookup_keyword(args[0]);
- switch (kw) {
- case K_devname:
- if (!strcmp(args[1], "uevent_devname"))
- s->devname_src = DEVNAME_UEVENT_DEVNAME;
- else if (!strcmp(args[1], "uevent_devpath"))
- s->devname_src = DEVNAME_UEVENT_DEVPATH;
- else
- parse_error(state, "invalid devname '%s'\n", args[1]);
- break;
-
- case K_dirname:
- if (args[1][0] == '/')
- s->dirname = args[1];
- else
- parse_error(state, "dirname '%s' does not start with '/'\n",
- args[1]);
- break;
-
- default:
- parse_error(state, "invalid option '%s'\n", args[0]);
+ std::string& uid_string = *it++;
+ passwd* pwd = getpwnam(uid_string.c_str());
+ if (!pwd) {
+ return Error() << "invalid uid '" << uid_string << "'";
}
-}
+ uid_t uid = pwd->pw_uid;
-static void parse_new_section(struct parse_state *state, int kw,
- int nargs, char **args)
-{
- printf("[ %s %s ]\n", args[0],
- nargs > 1 ? args[1] : "");
-
- switch(kw) {
- case K_subsystem:
- state->context = parse_subsystem(state, nargs, args);
- if (state->context) {
- state->parse_line = parse_line_subsystem;
- return;
- }
- break;
+ std::string& gid_string = *it++;
+ struct group* grp = getgrnam(gid_string.c_str());
+ if (!grp) {
+ return Error() << "invalid gid '" << gid_string << "'";
}
- state->parse_line = parse_line_no_op;
-}
+ gid_t gid = grp->gr_gid;
-static void parse_line(struct parse_state *state, char **args, int nargs)
-{
- int kw = lookup_keyword(args[0]);
- int kw_nargs = kw_nargs(kw);
-
- if (nargs < kw_nargs) {
- parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
- kw_nargs > 2 ? "arguments" : "argument");
- return;
- }
-
- if (kw_is(kw, SECTION)) {
- parse_new_section(state, kw, nargs, args);
- } else if (kw_is(kw, OPTION)) {
- state->parse_line(state, nargs, args);
+ if (is_sysfs) {
+ out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid);
} else {
- parse_line_device(state, nargs, args);
+ out_dev_permissions->emplace_back(name, perm, uid, gid);
}
+ return Success();
}
-static void parse_config(const char *fn, const std::string& data)
-{
- char *args[UEVENTD_PARSER_MAXARGS];
+Result<Success> SubsystemParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
+ if (args.size() != 2) {
+ return Error() << "subsystems must have exactly one name";
+ }
- int nargs = 0;
- parse_state state;
- state.filename = fn;
- state.line = 1;
- state.ptr = strdup(data.c_str()); // TODO: fix this code!
- state.nexttoken = 0;
- state.parse_line = parse_line_no_op;
- for (;;) {
- int token = next_token(&state);
- switch (token) {
- case T_EOF:
- parse_line(&state, args, nargs);
- return;
- case T_NEWLINE:
- if (nargs) {
- parse_line(&state, args, nargs);
- nargs = 0;
- }
- state.line++;
- break;
- case T_TEXT:
- if (nargs < UEVENTD_PARSER_MAXARGS) {
- args[nargs++] = state.text;
- }
- break;
+ if (std::find(subsystems_->begin(), subsystems_->end(), args[1]) != subsystems_->end()) {
+ return Error() << "ignoring duplicate subsystem entry";
+ }
+
+ subsystem_ = Subsystem(std::move(args[1]));
+
+ return Success();
+}
+
+Result<Success> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
+ if (args[1] == "uevent_devname") {
+ subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
+ return Success();
+ }
+ if (args[1] == "uevent_devpath") {
+ subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
+ return Success();
+ }
+
+ return Error() << "invalid devname '" << args[1] << "'";
+}
+
+Result<Success> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
+ if (args[1].front() != '/') {
+ return Error() << "dirname '" << args[1] << " ' does not start with '/'";
+ }
+
+ subsystem_.dir_name_ = args[1];
+ return Success();
+}
+
+Result<Success> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ using OptionParser = Result<Success> (SubsystemParser::*)(std::vector<std::string> && args);
+
+ static class OptionParserMap : public KeywordMap<OptionParser> {
+ private:
+ const Map& map() const override {
+ // clang-format off
+ static const Map option_parsers = {
+ {"devname", {1, 1, &SubsystemParser::ParseDevName}},
+ {"dirname", {1, 1, &SubsystemParser::ParseDirName}},
+ };
+ // clang-format on
+ return option_parsers;
}
- }
+ } parser_map;
+
+ auto parser = parser_map.FindFunction(args);
+
+ if (!parser) return Error() << parser.error();
+
+ return std::invoke(*parser, this, std::move(args));
}
-int ueventd_parse_config_file(const char *fn)
-{
- std::string data;
- if (!read_file(fn, &data)) {
- return -1;
- }
-
- data.push_back('\n'); // TODO: fix parse_config.
- parse_config(fn, data);
- return 0;
+void SubsystemParser::EndSection() {
+ subsystems_->emplace_back(std::move(subsystem_));
}
-static void parse_line_device(parse_state*, int nargs, char** args) {
- set_device_permission(nargs, args);
-}
+} // namespace init
+} // namespace android
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 907cc49..18d1027 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2007 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,15 +14,39 @@
* limitations under the License.
*/
-#ifndef _INIT_UEVENTD_PARSER_H_
-#define _INIT_UEVENTD_PARSER_H_
+#ifndef _INIT_UEVENTD_PARSER_H
+#define _INIT_UEVENTD_PARSER_H
-#include "ueventd.h"
+#include <string>
+#include <vector>
-#define UEVENTD_PARSER_MAXARGS 5
+#include "devices.h"
+#include "parser.h"
-int ueventd_parse_config_file(const char *fn);
-void set_device_permission(int nargs, char **args);
-struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name);
+namespace android {
+namespace init {
+
+class SubsystemParser : public SectionParser {
+ public:
+ SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
+ void EndSection() override;
+
+ private:
+ Result<Success> ParseDevName(std::vector<std::string>&& args);
+ Result<Success> ParseDirName(std::vector<std::string>&& args);
+
+ Subsystem subsystem_;
+ std::vector<Subsystem>* subsystems_;
+};
+
+Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
+ std::vector<SysfsPermissions>* out_sysfs_permissions,
+ std::vector<Permissions>* out_dev_permissions);
+
+} // namespace init
+} // namespace android
#endif
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
new file mode 100644
index 0000000..7290051
--- /dev/null
+++ b/init/ueventd_test.cpp
@@ -0,0 +1,204 @@
+/*
+ * 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 <linux/futex.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <chrono>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/scopeguard.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+
+using namespace std::chrono_literals;
+using namespace std::string_literals;
+
+template <typename T, typename F>
+void WriteFromMultipleThreads(std::vector<std::pair<std::string, T>>& files_and_parameters,
+ F function) {
+ auto num_threads = files_and_parameters.size();
+ pthread_barrier_t barrier;
+ pthread_barrier_init(&barrier, nullptr, num_threads);
+ auto barrier_destroy =
+ android::base::make_scope_guard([&barrier]() { pthread_barrier_destroy(&barrier); });
+
+ auto make_thread_function = [&function, &barrier](const auto& file, const auto& parameter) {
+ return [&]() {
+ function(parameter);
+ pthread_barrier_wait(&barrier);
+ android::base::WriteStringToFile("<empty>", file);
+ };
+ };
+
+ std::vector<std::thread> threads;
+ // TODO(b/63712782): Structured bindings + templated containers are broken in clang :(
+ // for (const auto& [file, parameter] : files_and_parameters) {
+ for (const auto& pair : files_and_parameters) {
+ const auto& file = pair.first;
+ const auto& parameter = pair.second;
+ threads.emplace_back(std::thread(make_thread_function(file, parameter)));
+ }
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+}
+
+TEST(ueventd, setegid_IsPerThread) {
+ if (getuid() != 0) {
+ GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ return;
+ }
+
+ TemporaryDir dir;
+
+ gid_t gid = 0;
+ std::vector<std::pair<std::string, gid_t>> files_and_gids;
+ std::generate_n(std::back_inserter(files_and_gids), 100, [&gid, &dir]() {
+ gid++;
+ return std::pair(dir.path + "/gid_"s + std::to_string(gid), gid);
+ });
+
+ WriteFromMultipleThreads(files_and_gids, [](gid_t gid) { EXPECT_EQ(0, setegid(gid)); });
+
+ for (const auto& [file, expected_gid] : files_and_gids) {
+ struct stat info;
+ ASSERT_EQ(0, stat(file.c_str(), &info));
+ EXPECT_EQ(expected_gid, info.st_gid);
+ }
+}
+
+TEST(ueventd, setfscreatecon_IsPerThread) {
+ if (getuid() != 0) {
+ GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ return;
+ }
+ if (!is_selinux_enabled() || security_getenforce() == 1) {
+ GTEST_LOG_(INFO) << "Skipping test, SELinux must be enabled and in permissive mode.";
+ return;
+ }
+
+ const char* const contexts[] = {
+ "u:object_r:audio_device:s0",
+ "u:object_r:sensors_device:s0",
+ "u:object_r:video_device:s0"
+ "u:object_r:zero_device:s0",
+ };
+
+ TemporaryDir dir;
+ std::vector<std::pair<std::string, std::string>> files_and_contexts;
+ for (const char* context : contexts) {
+ files_and_contexts.emplace_back(dir.path + "/context_"s + context, context);
+ }
+
+ WriteFromMultipleThreads(files_and_contexts, [](const std::string& context) {
+ EXPECT_EQ(0, setfscreatecon(context.c_str()));
+ });
+
+ for (const auto& [file, expected_context] : files_and_contexts) {
+ char* file_context;
+ ASSERT_GT(getfilecon(file.c_str(), &file_context), 0);
+ EXPECT_EQ(expected_context, file_context);
+ freecon(file_context);
+ }
+}
+
+TEST(ueventd, selabel_lookup_MultiThreaded) {
+ if (getuid() != 0) {
+ GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ return;
+ }
+
+ // Test parameters
+ constexpr auto num_threads = 10;
+ constexpr auto run_time = 200ms;
+
+ std::unique_ptr<selabel_handle, decltype(&selabel_close)> sehandle(
+ selinux_android_file_context_handle(), &selabel_close);
+
+ ASSERT_TRUE(sehandle);
+
+ struct {
+ const char* file;
+ int mode;
+ std::string expected_context;
+ } files_and_modes[] = {
+ {"/dev/zero", 020666, ""},
+ {"/dev/null", 020666, ""},
+ {"/dev/random", 020666, ""},
+ {"/dev/urandom", 020666, ""},
+ };
+
+ // Precondition, ensure that we can lookup all of these from a single thread, and store the
+ // expected context for each.
+ for (size_t i = 0; i < arraysize(files_and_modes); ++i) {
+ char* secontext;
+ ASSERT_EQ(0, selabel_lookup(sehandle.get(), &secontext, files_and_modes[i].file,
+ files_and_modes[i].mode));
+ files_and_modes[i].expected_context = secontext;
+ freecon(secontext);
+ }
+
+ // Now that we know we can access them, and what their context should be, run in parallel.
+ std::atomic_bool stopped = false;
+ std::atomic_uint num_api_failures = 0;
+ std::atomic_uint num_context_check_failures = 0;
+ std::atomic_uint num_successes = 0;
+
+ auto thread_function = [&]() {
+ while (!stopped) {
+ for (size_t i = 0; i < arraysize(files_and_modes); ++i) {
+ char* secontext;
+ int result = selabel_lookup(sehandle.get(), &secontext, files_and_modes[i].file,
+ files_and_modes[i].mode);
+ if (result != 0) {
+ num_api_failures++;
+ } else {
+ if (files_and_modes[i].expected_context != secontext) {
+ num_context_check_failures++;
+ } else {
+ num_successes++;
+ }
+ freecon(secontext);
+ }
+ }
+ }
+ };
+
+ std::vector<std::thread> threads;
+ std::generate_n(back_inserter(threads), num_threads,
+ [&]() { return std::thread(thread_function); });
+
+ std::this_thread::sleep_for(run_time);
+ stopped = true;
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ EXPECT_EQ(0U, num_api_failures);
+ EXPECT_EQ(0U, num_context_check_failures);
+ EXPECT_GT(num_successes, 0U);
+}
diff --git a/init/util.cpp b/init/util.cpp
index 4e3537b..a19a6f3 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -14,26 +14,21 @@
* limitations under the License.
*/
+#include "util.h"
+
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
-#include <ftw.h>
#include <pwd.h>
#include <stdarg.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
#include <time.h>
#include <unistd.h>
-#include <selinux/android.h>
-#include <selinux/label.h>
-
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-
#include <thread>
#include <android-base/file.h>
@@ -42,59 +37,50 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-
#include <cutils/android_reboot.h>
-/* for ANDROID_SOCKET_* */
#include <cutils/sockets.h>
+#include <selinux/android.h>
-#include "init.h"
-#include "log.h"
#include "reboot.h"
-#include "util.h"
+#include "selinux.h"
-static unsigned int do_decode_uid(const char *s)
-{
- unsigned int v;
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#endif
- if (!s || *s == '\0')
- return UINT_MAX;
+using android::base::boot_clock;
+using namespace std::literals::string_literals;
- if (isalpha(s[0])) {
- struct passwd* pwd = getpwnam(s);
- if (!pwd)
- return UINT_MAX;
+namespace android {
+namespace init {
+
+const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
+
+// DecodeUid() - decodes and returns the given string, which can be either the
+// numeric or name representation, into the integer uid or gid.
+Result<uid_t> DecodeUid(const std::string& name) {
+ if (isalpha(name[0])) {
+ passwd* pwd = getpwnam(name.c_str());
+ if (!pwd) return ErrnoError() << "getpwnam failed";
+
return pwd->pw_uid;
}
errno = 0;
- v = (unsigned int) strtoul(s, 0, 0);
- if (errno)
- return UINT_MAX;
- return v;
+ uid_t result = static_cast<uid_t>(strtoul(name.c_str(), 0, 0));
+ if (errno) return ErrnoError() << "strtoul failed";
+
+ return result;
}
/*
- * decode_uid - decodes and returns the given string, which can be either the
- * numeric or name representation, into the integer uid or gid. Returns
- * UINT_MAX on error.
- */
-unsigned int decode_uid(const char *s) {
- unsigned int v = do_decode_uid(s);
- if (v == UINT_MAX) {
- LOG(ERROR) << "decode_uid: Unable to find UID for '" << s << "'; returning UINT_MAX";
- }
- return v;
-}
-
-/*
- * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR
+ * CreateSocket - creates a Unix domain socket in ANDROID_SOCKET_DIR
* ("/dev/socket") as dictated in init.rc. This socket is inherited by the
* daemon. We communicate the file descriptor's value via the environment
* variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
*/
-int create_socket(const char *name, int type, mode_t perm, uid_t uid,
- gid_t gid, const char *socketcon)
-{
+int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
+ const char* socketcon) {
if (socketcon) {
if (setsockcreatecon(socketcon) == -1) {
PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
@@ -121,18 +107,25 @@
return -1;
}
- char *filecon = NULL;
- if (sehandle) {
- if (selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK) == 0) {
- setfscreatecon(filecon);
+ std::string secontext;
+ if (SelabelLookupFileContext(addr.sun_path, S_IFSOCK, &secontext) && !secontext.empty()) {
+ setfscreatecon(secontext.c_str());
+ }
+
+ if (passcred) {
+ int on = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
+ PLOG(ERROR) << "Failed to set SO_PASSCRED '" << name << "'";
+ return -1;
}
}
int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
int savederrno = errno;
- setfscreatecon(NULL);
- freecon(filecon);
+ if (!secontext.empty()) {
+ setfscreatecon(nullptr);
+ }
if (ret) {
errno = savederrno;
@@ -161,114 +154,68 @@
return -1;
}
-bool read_file(const std::string& path, std::string* content) {
- content->clear();
-
+Result<std::string> ReadFile(const std::string& path) {
android::base::unique_fd fd(
TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
if (fd == -1) {
- return false;
+ return ErrnoError() << "open() failed";
}
// For security reasons, disallow world-writable
// or group-writable files.
struct stat sb;
if (fstat(fd, &sb) == -1) {
- PLOG(ERROR) << "fstat failed for '" << path << "'";
- return false;
+ return ErrnoError() << "fstat failed()";
}
if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
- LOG(ERROR) << "skipping insecure file '" << path << "'";
- return false;
+ return Error() << "Skipping insecure file";
}
- return android::base::ReadFdToString(fd, content);
+ std::string content;
+ if (!android::base::ReadFdToString(fd, &content)) {
+ return ErrnoError() << "Unable to read file contents";
+ }
+ return content;
}
-bool write_file(const std::string& path, const std::string& content) {
+Result<Success> WriteFile(const std::string& path, const std::string& content) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(
open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
if (fd == -1) {
- PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
- return false;
+ return ErrnoError() << "open() failed";
}
- bool success = android::base::WriteStringToFd(content, fd);
- if (!success) {
- PLOG(ERROR) << "write_file: Unable to write to '" << path << "'";
+ if (!android::base::WriteStringToFd(content, fd)) {
+ return ErrnoError() << "Unable to write file contents";
}
- return success;
+ return Success();
}
-boot_clock::time_point boot_clock::now() {
- timespec ts;
- clock_gettime(CLOCK_BOOTTIME, &ts);
- return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
- std::chrono::nanoseconds(ts.tv_nsec));
-}
-
-int mkdir_recursive(const char *pathname, mode_t mode)
-{
- char buf[128];
- const char *slash;
- const char *p = pathname;
- int width;
- int ret;
- struct stat info;
-
- while ((slash = strchr(p, '/')) != NULL) {
- width = slash - pathname;
- p = slash + 1;
- if (width < 0)
- break;
- if (width == 0)
- continue;
- if ((unsigned int)width > sizeof(buf) - 1) {
- LOG(ERROR) << "path too long for mkdir_recursive";
- return -1;
- }
- memcpy(buf, pathname, width);
- buf[width] = 0;
- if (stat(buf, &info) != 0) {
- ret = make_dir(buf, mode);
- if (ret && errno != EEXIST)
- return ret;
+bool mkdir_recursive(const std::string& path, mode_t mode) {
+ std::string::size_type slash = 0;
+ while ((slash = path.find('/', slash + 1)) != std::string::npos) {
+ auto directory = path.substr(0, slash);
+ struct stat info;
+ if (stat(directory.c_str(), &info) != 0) {
+ auto ret = make_dir(directory, mode);
+ if (!ret && errno != EEXIST) return false;
}
}
- ret = make_dir(pathname, mode);
- if (ret && errno != EEXIST)
- return ret;
- return 0;
-}
-
-/*
- * replaces any unacceptable characters with '_', the
- * length of the resulting string is equal to the input string
- */
-void sanitize(char *s)
-{
- const char* accept =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789"
- "_-.";
-
- if (!s)
- return;
-
- while (*s) {
- s += strspn(s, accept);
- if (*s) *s++ = '_';
- }
+ auto ret = make_dir(path, mode);
+ if (!ret && errno != EEXIST) return false;
+ return true;
}
int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
- boot_clock::time_point timeout_time = boot_clock::now() + timeout;
- while (boot_clock::now() < timeout_time) {
+ android::base::Timer t;
+ while (t.duration() < timeout) {
struct stat sb;
- if (stat(filename, &sb) != -1) return 0;
-
+ if (stat(filename, &sb) != -1) {
+ LOG(INFO) << "wait for '" << filename << "' took " << t;
+ return 0;
+ }
std::this_thread::sleep_for(10ms);
}
+ LOG(WARNING) << "wait for '" << filename << "' timed out and took " << t;
return -1;
}
@@ -285,32 +232,21 @@
}
}
-int make_dir(const char *path, mode_t mode)
-{
- int rc;
-
- char *secontext = NULL;
-
- if (sehandle) {
- selabel_lookup(sehandle, &secontext, path, mode);
- setfscreatecon(secontext);
+bool make_dir(const std::string& path, mode_t mode) {
+ std::string secontext;
+ if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
+ setfscreatecon(secontext.c_str());
}
- rc = mkdir(path, mode);
+ int rc = mkdir(path.c_str(), mode);
- if (secontext) {
+ if (!secontext.empty()) {
int save_errno = errno;
- freecon(secontext);
- setfscreatecon(NULL);
+ setfscreatecon(nullptr);
errno = save_errno;
}
- return rc;
-}
-
-int restorecon(const char* pathname, int flags)
-{
- return selinux_android_restorecon(pathname, flags);
+ return rc == 0;
}
/*
@@ -412,20 +348,31 @@
return true;
}
-void panic() {
- LOG(ERROR) << "panic: rebooting to bootloader";
- DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
+static std::string init_android_dt_dir() {
+ // Use the standard procfs-based path by default
+ std::string android_dt_dir = kDefaultAndroidDtDir;
+ // The platform may specify a custom Android DT path in kernel cmdline
+ import_kernel_cmdline(false,
+ [&](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.android_dt_dir") {
+ android_dt_dir = value;
+ }
+ });
+ LOG(INFO) << "Using Android DT directory " << android_dt_dir;
+ return android_dt_dir;
}
-std::ostream& operator<<(std::ostream& os, const Timer& t) {
- os << t.duration_s() << " seconds";
- return os;
+// FIXME: The same logic is duplicated in system/core/fs_mgr/
+const std::string& get_android_dt_dir() {
+ // Set once and saves time for subsequent calls to this function
+ static const std::string kAndroidDtDir = init_android_dt_dir();
+ return kAndroidDtDir;
}
-// Reads the content of device tree file under kAndroidDtDir directory.
+// Reads the content of device tree file under the platform's Android DT directory.
// Returns true if the read is success, false otherwise.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
- const std::string file_name = kAndroidDtDir + sub_path;
+ const std::string file_name = get_android_dt_dir() + sub_path;
if (android::base::ReadFileToString(file_name, dt_content)) {
if (!dt_content->empty()) {
dt_content->pop_back(); // Trims the trailing '\0' out.
@@ -444,3 +391,6 @@
}
return false;
}
+
+} // namespace init
+} // namespace android
diff --git a/init/util.h b/init/util.h
index bb281bd..2cfcf6c 100644
--- a/init/util.h
+++ b/init/util.h
@@ -25,65 +25,44 @@
#include <ostream>
#include <string>
+#include <android-base/chrono_utils.h>
+#include <selinux/label.h>
+
+#include "result.h"
+
#define COLDBOOT_DONE "/dev/.coldboot_done"
-const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
-
+using android::base::boot_clock;
using namespace std::chrono_literals;
-int create_socket(const char *name, int type, mode_t perm,
- uid_t uid, gid_t gid, const char *socketcon);
+namespace android {
+namespace init {
-bool read_file(const std::string& path, std::string* content);
-bool write_file(const std::string& path, const std::string& content);
+int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
+ const char* socketcon);
-// A std::chrono clock based on CLOCK_BOOTTIME.
-class boot_clock {
- public:
- typedef std::chrono::nanoseconds duration;
- typedef std::chrono::time_point<boot_clock, duration> time_point;
- static constexpr bool is_steady = true;
+Result<std::string> ReadFile(const std::string& path);
+Result<Success> WriteFile(const std::string& path, const std::string& content);
- static time_point now();
-};
+Result<uid_t> DecodeUid(const std::string& name);
-class Timer {
- public:
- Timer() : start_(boot_clock::now()) {
- }
-
- double duration_s() const {
- typedef std::chrono::duration<double> double_duration;
- return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
- }
-
- int64_t duration_ms() const {
- return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_).count();
- }
-
- private:
- boot_clock::time_point start_;
-};
-
-std::ostream& operator<<(std::ostream& os, const Timer& t);
-
-unsigned int decode_uid(const char *s);
-
-int mkdir_recursive(const char *pathname, mode_t mode);
-void sanitize(char *p);
+bool mkdir_recursive(const std::string& pathname, mode_t mode);
int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
void import_kernel_cmdline(bool in_qemu,
const std::function<void(const std::string&, const std::string&, bool)>&);
-int make_dir(const char *path, mode_t mode);
-int restorecon(const char *pathname, int flags = 0);
+bool make_dir(const std::string& path, mode_t mode);
std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
bool is_dir(const char* pathname);
bool expand_props(const std::string& src, std::string* dst);
-void panic() __attribute__((__noreturn__));
-
-// Reads or compares the content of device tree file under kAndroidDtDir directory.
+// Returns the platform's Android DT directory as specified in the kernel cmdline.
+// If the platform does not configure a custom DT path, returns the standard one (based in procfs).
+const std::string& get_android_dt_dir();
+// Reads or compares the content of device tree file under the platform's Android DT directory.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 0c0350a..3ae53a4 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -24,53 +24,60 @@
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
-TEST(util, read_file_ENOENT) {
- std::string s("hello");
- errno = 0;
- EXPECT_FALSE(read_file("/proc/does-not-exist", &s));
- EXPECT_EQ(ENOENT, errno);
- EXPECT_EQ("", s); // s was cleared.
+using namespace std::literals::string_literals;
+
+namespace android {
+namespace init {
+
+TEST(util, ReadFile_ENOENT) {
+ errno = 0;
+ auto file_contents = ReadFile("/proc/does-not-exist");
+ EXPECT_EQ(ENOENT, errno);
+ ASSERT_FALSE(file_contents);
+ EXPECT_EQ("open() failed: No such file or directory", file_contents.error_string());
}
-TEST(util, read_file_group_writeable) {
+TEST(util, ReadFileGroupWriteable) {
std::string s("hello");
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, s)) << strerror(errno);
+ EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
- EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
- EXPECT_EQ("", s); // s was cleared.
+ auto file_contents = ReadFile(tf.path);
+ ASSERT_FALSE(file_contents) << strerror(errno);
+ EXPECT_EQ("Skipping insecure file", file_contents.error_string());
}
-TEST(util, read_file_world_writeable) {
+TEST(util, ReadFileWorldWiteable) {
std::string s("hello");
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
+ EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
- EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
- EXPECT_EQ("", s); // s was cleared.
+ auto file_contents = ReadFile(tf.path);
+ ASSERT_FALSE(file_contents) << strerror(errno);
+ EXPECT_EQ("Skipping insecure file", file_contents.error_string());
}
-TEST(util, read_file_symbolic_link) {
- std::string s("hello");
+TEST(util, ReadFileSymbolicLink) {
errno = 0;
// lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
- EXPECT_FALSE(read_file("/charger", &s));
+ auto file_contents = ReadFile("/charger");
EXPECT_EQ(ELOOP, errno);
- EXPECT_EQ("", s); // s was cleared.
+ ASSERT_FALSE(file_contents);
+ EXPECT_EQ("open() failed: Too many symbolic links encountered", file_contents.error_string());
}
-TEST(util, read_file_success) {
- std::string s("hello");
- EXPECT_TRUE(read_file("/proc/version", &s));
- EXPECT_GT(s.length(), 6U);
- EXPECT_EQ('\n', s[s.length() - 1]);
- s[5] = 0;
- EXPECT_STREQ("Linux", s.c_str());
+TEST(util, ReadFileSuccess) {
+ auto file_contents = ReadFile("/proc/version");
+ ASSERT_TRUE(file_contents);
+ EXPECT_GT(file_contents->length(), 6U);
+ EXPECT_EQ('\n', file_contents->at(file_contents->length() - 1));
+ (*file_contents)[5] = 0;
+ EXPECT_STREQ("Linux", file_contents->c_str());
}
-TEST(util, write_file_binary) {
+TEST(util, WriteFileBinary) {
std::string contents("abcd");
contents.push_back('\0');
contents.push_back('\0');
@@ -79,22 +86,22 @@
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, contents)) << strerror(errno);
+ EXPECT_TRUE(WriteFile(tf.path, contents)) << strerror(errno);
- std::string read_back_contents;
- EXPECT_TRUE(read_file(tf.path, &read_back_contents)) << strerror(errno);
- EXPECT_EQ(contents, read_back_contents);
- EXPECT_EQ(10u, read_back_contents.size());
+ auto read_back_contents = ReadFile(tf.path);
+ ASSERT_TRUE(read_back_contents) << strerror(errno);
+ EXPECT_EQ(contents, *read_back_contents);
+ EXPECT_EQ(10u, read_back_contents->size());
}
-TEST(util, write_file_not_exist) {
+TEST(util, WriteFileNotExist) {
std::string s("hello");
- std::string s2("hello");
TemporaryDir test_dir;
std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
- EXPECT_TRUE(write_file(path, s));
- EXPECT_TRUE(read_file(path, &s2));
- EXPECT_EQ(s, s2);
+ EXPECT_TRUE(WriteFile(path, s));
+ auto file_contents = ReadFile(path);
+ ASSERT_TRUE(file_contents);
+ EXPECT_EQ(s, *file_contents);
struct stat sb;
int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
EXPECT_NE(-1, fd);
@@ -103,20 +110,63 @@
EXPECT_EQ(0, unlink(path.c_str()));
}
-TEST(util, write_file_exist) {
- std::string s2("");
+TEST(util, WriteFileExist) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, "1hello1")) << strerror(errno);
- EXPECT_TRUE(read_file(tf.path, &s2));
- EXPECT_STREQ("1hello1", s2.c_str());
- EXPECT_TRUE(write_file(tf.path, "2ll2"));
- EXPECT_TRUE(read_file(tf.path, &s2));
- EXPECT_STREQ("2ll2", s2.c_str());
+ EXPECT_TRUE(WriteFile(tf.path, "1hello1")) << strerror(errno);
+ auto file_contents = ReadFile(tf.path);
+ ASSERT_TRUE(file_contents);
+ EXPECT_EQ("1hello1", *file_contents);
+ EXPECT_TRUE(WriteFile(tf.path, "2ll2"));
+ file_contents = ReadFile(tf.path);
+ ASSERT_TRUE(file_contents);
+ EXPECT_EQ("2ll2", *file_contents);
}
-TEST(util, decode_uid) {
- EXPECT_EQ(0U, decode_uid("root"));
- EXPECT_EQ(UINT_MAX, decode_uid("toot"));
- EXPECT_EQ(123U, decode_uid("123"));
+TEST(util, DecodeUid) {
+ auto decoded_uid = DecodeUid("root");
+ EXPECT_TRUE(decoded_uid);
+ EXPECT_EQ(0U, *decoded_uid);
+
+ decoded_uid = DecodeUid("toot");
+ EXPECT_FALSE(decoded_uid);
+ EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error_string());
+
+ decoded_uid = DecodeUid("123");
+ EXPECT_TRUE(decoded_uid);
+ EXPECT_EQ(123U, *decoded_uid);
}
+
+TEST(util, is_dir) {
+ TemporaryDir test_dir;
+ EXPECT_TRUE(is_dir(test_dir.path));
+ TemporaryFile tf;
+ EXPECT_FALSE(is_dir(tf.path));
+}
+
+TEST(util, mkdir_recursive) {
+ TemporaryDir test_dir;
+ std::string path = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
+ EXPECT_TRUE(mkdir_recursive(path, 0755));
+ std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
+ EXPECT_TRUE(is_dir(path1.c_str()));
+ std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
+ EXPECT_TRUE(is_dir(path1.c_str()));
+ std::string path3 = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
+ EXPECT_TRUE(is_dir(path1.c_str()));
+}
+
+TEST(util, mkdir_recursive_extra_slashes) {
+ TemporaryDir test_dir;
+ std::string path = android::base::StringPrintf("%s/three////directories/deep//", test_dir.path);
+ EXPECT_TRUE(mkdir_recursive(path, 0755));
+ std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
+ EXPECT_TRUE(is_dir(path1.c_str()));
+ std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
+ EXPECT_TRUE(is_dir(path1.c_str()));
+ std::string path3 = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
+ EXPECT_TRUE(is_dir(path1.c_str()));
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index b196147..e0164b4 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -16,17 +16,24 @@
#include <errno.h>
#include <fcntl.h>
+#include <linux/watchdog.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <linux/watchdog.h>
+#include <android-base/logging.h>
#include "log.h"
-#include "util.h"
+
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#endif
#define DEV_NAME "/dev/watchdog"
+namespace android {
+namespace init {
+
int watchdogd_main(int argc, char **argv) {
InitKernelLogging(argv);
@@ -69,3 +76,6 @@
sleep(interval);
}
}
+
+} // namespace init
+} // namespace android
diff --git a/init/watchdogd.h b/init/watchdogd.h
index 8b48ab8..73f77d5 100644
--- a/init/watchdogd.h
+++ b/init/watchdogd.h
@@ -17,6 +17,12 @@
#ifndef _INIT_WATCHDOGD_H_
#define _INIT_WATCHDOGD_H_
+namespace android {
+namespace init {
+
int watchdogd_main(int argc, char **argv);
+} // namespace init
+} // namespace android
+
#endif
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index 0d57814..29ffe32 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -8,7 +8,6 @@
"-Wall",
"-Werror",
],
- clang: true
}
cc_library_shared {
@@ -25,6 +24,7 @@
cc_test {
name: "libappfuse_test",
+ test_suites: ["device-tests"],
defaults: ["libappfuse_defaults"],
shared_libs: ["libappfuse"],
srcs: [
diff --git a/libappfuse/AndroidTest.xml b/libappfuse/AndroidTest.xml
new file mode 100644
index 0000000..a9cd754
--- /dev/null
+++ b/libappfuse/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for libappfuse_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="libappfuse_test->/data/local/tmp/libappfuse_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="libappfuse_test" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
new file mode 100644
index 0000000..9a637ac
--- /dev/null
+++ b/libasyncio/Android.bp
@@ -0,0 +1,44 @@
+//
+// 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.
+//
+
+libasyncio_cppflags = [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+]
+
+cc_library {
+ name: "libasyncio",
+ vendor_available: true,
+ host_supported: true,
+ srcs: [
+ "AsyncIO.cpp",
+ ],
+ cppflags: libasyncio_cppflags,
+
+ export_include_dirs: ["include"],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
+}
diff --git a/libasyncio/AsyncIO.cpp b/libasyncio/AsyncIO.cpp
new file mode 100644
index 0000000..7430bc8
--- /dev/null
+++ b/libasyncio/AsyncIO.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 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 <asyncio/AsyncIO.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+int io_setup(unsigned nr, aio_context_t* ctxp) {
+ memset(ctxp, 0, sizeof(*ctxp));
+ return syscall(__NR_io_setup, nr, ctxp);
+}
+
+int io_destroy(aio_context_t ctx) {
+ return syscall(__NR_io_destroy, ctx);
+}
+
+int io_submit(aio_context_t ctx, long nr, iocb** iocbpp) {
+ return syscall(__NR_io_submit, ctx, nr, iocbpp);
+}
+
+int io_getevents(aio_context_t ctx, long min_nr, long max_nr, io_event* events, timespec* timeout) {
+ return syscall(__NR_io_getevents, ctx, min_nr, max_nr, events, timeout);
+}
+
+int io_cancel(aio_context_t ctx, iocb* iocbp, io_event* result) {
+ return syscall(__NR_io_cancel, ctx, iocbp, result);
+}
+
+void io_prep(iocb* iocb, int fd, const void* buf, uint64_t count, int64_t offset, bool read) {
+ memset(iocb, 0, sizeof(*iocb));
+ iocb->aio_fildes = fd;
+ iocb->aio_lio_opcode = read ? IOCB_CMD_PREAD : IOCB_CMD_PWRITE;
+ iocb->aio_reqprio = 0;
+ iocb->aio_buf = reinterpret_cast<uint64_t>(buf);
+ iocb->aio_nbytes = count;
+ iocb->aio_offset = offset;
+}
diff --git a/libasyncio/include/asyncio/AsyncIO.h b/libasyncio/include/asyncio/AsyncIO.h
new file mode 100644
index 0000000..e3fb93a
--- /dev/null
+++ b/libasyncio/include/asyncio/AsyncIO.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 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 _ASYNCIO_H
+#define _ASYNCIO_H
+
+#include <cstring>
+#include <cstdint>
+#include <linux/aio_abi.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Provides kernel aio operations.
+ */
+
+int io_setup(unsigned nr, aio_context_t* ctxp);
+int io_destroy(aio_context_t ctx);
+int io_submit(aio_context_t ctx, long nr, iocb** iocbpp);
+int io_getevents(aio_context_t ctx, long min_nr, long max_nr, io_event* events, timespec* timeout);
+int io_cancel(aio_context_t ctx, iocb*, io_event* result);
+void io_prep(iocb* iocb, int fd, const void* buf, uint64_t count, int64_t offset, bool read);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ASYNCIO_H
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 285aa6e..e889bdd 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -53,6 +53,8 @@
"UnwindCurrent.cpp",
"UnwindMap.cpp",
"UnwindPtrace.cpp",
+ "UnwindStack.cpp",
+ "UnwindStackMap.cpp",
]
cc_library_headers {
@@ -64,6 +66,10 @@
cc_library {
name: "libbacktrace",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
defaults: ["libbacktrace_common"],
host_supported: true,
@@ -84,6 +90,7 @@
"libbase",
"liblog",
"libunwind",
+ "libunwindstack",
],
static_libs: ["libcutils"],
@@ -97,6 +104,7 @@
"libbase",
"liblog",
"libunwind",
+ "libunwindstack",
],
static_libs: ["libcutils"],
@@ -108,11 +116,13 @@
"libbase",
"liblog",
"libunwind",
+ "libunwindstack",
],
- static_libs: ["libcutils"],
+ static_libs: ["libasync_safe", "libcutils"],
},
},
+ whole_static_libs: ["libdemangle"],
}
cc_library_shared {
@@ -129,11 +139,13 @@
linux: {
shared_libs: [
"libunwind",
+ "libunwindstack",
],
},
android: {
shared_libs: [
"libunwind",
+ "libunwindstack",
],
},
}
@@ -160,6 +172,8 @@
shared_libs = [
"libbase",
"libunwind",
+ "libunwindstack",
+ "libziparchive",
],
}
@@ -190,6 +204,7 @@
"libcutils",
"liblog",
"libunwind",
+ "libunwindstack",
],
group_static_libs: true,
@@ -228,4 +243,25 @@
static_libs: ["libutils"],
},
},
+
+ data: [
+ "testdata/arm/*",
+ "testdata/arm64/*",
+ "testdata/x86/*",
+ "testdata/x86_64/*",
+ ],
+}
+
+cc_benchmark {
+ name: "backtrace_benchmarks",
+ defaults: ["libbacktrace_common"],
+
+ srcs: [
+ "backtrace_benchmarks.cpp",
+ ],
+
+ shared_libs: [
+ "libbacktrace",
+ "libbase",
+ ],
}
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 3545661..81f5e32 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -27,6 +27,8 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
+#include <demangle.h>
+
#include "BacktraceLog.h"
#include "thread_utils.h"
#include "UnwindCurrent.h"
@@ -62,8 +64,7 @@
if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
return "";
}
- std::string func_name = GetFunctionNameRaw(pc, offset);
- return func_name;
+ return demangle(GetFunctionNameRaw(pc, offset).c_str());
}
bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, word_t* out_value) {
@@ -83,10 +84,8 @@
}
std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
- uintptr_t relative_pc;
std::string map_name;
if (BacktraceMap::IsValid(frame->map)) {
- relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc);
if (!frame->map.name.empty()) {
map_name = frame->map.name.c_str();
if (map_name[0] == '[' && map_name[map_name.size() - 1] == ']') {
@@ -98,10 +97,9 @@
}
} else {
map_name = "<unknown>";
- relative_pc = frame->pc;
}
- std::string line(StringPrintf("#%02zu pc %" PRIPTR " ", frame->num, relative_pc));
+ std::string line(StringPrintf("#%02zu pc %" PRIPTR " ", frame->num, frame->rel_pc));
line += map_name;
// Special handling for non-zero offset maps, we need to print that
// information.
diff --git a/libbacktrace/BacktraceAsyncSafeLog.h b/libbacktrace/BacktraceAsyncSafeLog.h
new file mode 100644
index 0000000..14f51be
--- /dev/null
+++ b/libbacktrace/BacktraceAsyncSafeLog.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
+#define _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
+
+#if defined(__ANDROID__)
+
+#include <async_safe/log.h>
+
+// Logging macros for use in signal handler, only available on target.
+#define BACK_ASYNC_SAFE_LOGW(format, ...) \
+ async_safe_format_log(ANDROID_LOG_WARN, "libbacktrace", "%s: " format, __PRETTY_FUNCTION__, \
+ ##__VA_ARGS__)
+
+#define BACK_ASYNC_SAFE_LOGE(format, ...) \
+ async_safe_format_log(ANDROID_LOG_ERROR, "libbacktrace", "%s: " format, __PRETTY_FUNCTION__, \
+ ##__VA_ARGS__)
+
+#else
+
+#define BACK_ASYNC_SAFE_LOGW(format, ...)
+
+#define BACK_ASYNC_SAFE_LOGE(format, ...)
+
+#endif
+
+#endif // _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index d7a3b01..fb76b85 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -31,8 +31,8 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
+#include "BacktraceAsyncSafeLog.h"
#include "BacktraceCurrent.h"
-#include "BacktraceLog.h"
#include "ThreadEntry.h"
#include "thread_utils.h"
@@ -47,7 +47,7 @@
*out_value = *reinterpret_cast<word_t*>(ptr);
return true;
} else {
- BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
+ BACK_ASYNC_SAFE_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
*out_value = static_cast<word_t>(-1);
return false;
}
@@ -114,7 +114,8 @@
static void SignalLogOnly(int, siginfo_t*, void*) {
ErrnoRestorer restore;
- BACK_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(), THREAD_SIGNAL);
+ BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(),
+ THREAD_SIGNAL);
}
static void SignalHandler(int, siginfo_t*, void* sigcontext) {
@@ -122,7 +123,7 @@
ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
if (!entry) {
- BACK_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
+ BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
return;
}
@@ -141,7 +142,7 @@
entry->Wake();
} else {
// At this point, it is possible that entry has been freed, so just exit.
- BACK_LOGE("Timed out waiting for unwind thread to indicate it completed.");
+ BACK_ASYNC_SAFE_LOGE("Timed out waiting for unwind thread to indicate it completed.");
}
}
@@ -159,7 +160,7 @@
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
sigemptyset(&act.sa_mask);
if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
- BACK_LOGE("sigaction failed: %s", strerror(errno));
+ BACK_ASYNC_SAFE_LOGE("sigaction failed: %s", strerror(errno));
ThreadEntry::Remove(entry);
pthread_mutex_unlock(&g_sigaction_mutex);
error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
@@ -212,7 +213,7 @@
// Wait for the thread to indicate it is done with the ThreadEntry.
if (!entry->Wait(3)) {
// Send a warning, but do not mark as a failure to unwind.
- BACK_LOGW("Timed out waiting for signal handler to indicate it finished.");
+ BACK_ASYNC_SAFE_LOGW("Timed out waiting for signal handler to indicate it finished.");
}
} else {
// Check to see if the thread has disappeared.
@@ -220,7 +221,7 @@
error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
} else {
error_ = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
- BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
+ BACK_ASYNC_SAFE_LOGE("Timed out waiting for signal handler to get ucontext data.");
}
}
diff --git a/libbacktrace/ThreadEntry.cpp b/libbacktrace/ThreadEntry.cpp
index 084c1aa..9bd59e4 100644
--- a/libbacktrace/ThreadEntry.cpp
+++ b/libbacktrace/ThreadEntry.cpp
@@ -21,7 +21,7 @@
#include <time.h>
#include <ucontext.h>
-#include "BacktraceLog.h"
+#include "BacktraceAsyncSafeLog.h"
#include "ThreadEntry.h"
// Initialize static member variables.
@@ -106,7 +106,7 @@
while (wait_value_ != value) {
int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
if (ret != 0) {
- BACK_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret));
+ BACK_ASYNC_SAFE_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret));
wait_completed = false;
break;
}
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index 3c509e6..2c87fa8 100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -133,6 +133,11 @@
backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
prev->stack_size = frame->sp - prev->sp;
}
+ if (BacktraceMap::IsValid(frame->map)) {
+ frame->rel_pc = frame->pc - frame->map.start + frame->map.load_bias;
+ } else {
+ frame->rel_pc = frame->pc;
+ }
num_frames++;
} else {
num_ignore_frames--;
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index af79562..0b8232b 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -57,7 +57,7 @@
map.start = unw_map.start;
map.end = unw_map.end;
map.offset = unw_map.offset;
- map.load_base = unw_map.load_base;
+ map.load_bias = unw_map.load_base;
map.flags = unw_map.flags;
map.name = unw_map.path;
@@ -106,7 +106,7 @@
map.start = unw_map.start;
map.end = unw_map.end;
map.offset = unw_map.offset;
- map.load_base = unw_map.load_base;
+ map.load_bias = unw_map.load_base;
map.flags = unw_map.flags;
map.name = unw_map.path;
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 42ac1bc..87282ef 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -135,6 +135,11 @@
}
FillInMap(frame->pc, &frame->map);
+ if (BacktraceMap::IsValid(frame->map)) {
+ frame->rel_pc = frame->pc - frame->map.start + frame->map.load_bias;
+ } else {
+ frame->rel_pc = frame->pc;
+ }
frame->func_name = GetFunctionName(frame->pc, &frame->func_offset, &frame->map);
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
new file mode 100644
index 0000000..61812ab
--- /dev/null
+++ b/libbacktrace/UnwindStack.cpp
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+
+#define _GNU_SOURCE 1
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ucontext.h>
+
+#include <memory>
+#include <string>
+
+#if !defined(__ANDROID__)
+#include <cutils/threads.h>
+#endif
+
+#include <backtrace/Backtrace.h>
+#include <demangle.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+#include "BacktraceLog.h"
+#include "UnwindStack.h"
+#include "UnwindStackMap.h"
+
+static std::string GetFunctionName(BacktraceMap* back_map, uintptr_t pc, uintptr_t* offset) {
+ *offset = 0;
+ unwindstack::Maps* maps = reinterpret_cast<UnwindStackMap*>(back_map)->stack_maps();
+
+ // Get the map for this
+ unwindstack::MapInfo* map_info = maps->Find(pc);
+ if (map_info == nullptr || map_info->flags & PROT_DEVICE_MAP) {
+ return "";
+ }
+
+ UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
+ unwindstack::Elf* elf = map_info->GetElf(stack_map->process_memory(), true);
+
+ std::string name;
+ uint64_t func_offset;
+ if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info), &name, &func_offset)) {
+ return "";
+ }
+ *offset = func_offset;
+ return name;
+}
+
+static bool IsUnwindLibrary(const std::string& map_name) {
+ const std::string library(basename(map_name.c_str()));
+ return library == "libunwindstack.so" || library == "libbacktrace.so";
+}
+
+static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
+ std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames) {
+ UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
+ unwindstack::Maps* maps = stack_map->stack_maps();
+ bool adjust_rel_pc = false;
+ size_t num_frames = 0;
+ frames->clear();
+ while (num_frames < MAX_BACKTRACE_FRAMES) {
+ if (regs->pc() == 0) {
+ break;
+ }
+ unwindstack::MapInfo* map_info = maps->Find(regs->pc());
+ if (map_info == nullptr) {
+ break;
+ }
+
+ unwindstack::Elf* elf = map_info->GetElf(stack_map->process_memory(), true);
+ uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
+
+ bool skip_frame = num_frames == 0 && IsUnwindLibrary(map_info->name);
+ if (num_ignore_frames == 0 && !skip_frame) {
+ uint64_t adjusted_rel_pc = rel_pc;
+ if (adjust_rel_pc) {
+ adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
+ }
+ frames->resize(num_frames + 1);
+ backtrace_frame_data_t* frame = &frames->at(num_frames);
+ frame->num = num_frames;
+ // This will point to the adjusted absolute pc. regs->pc() is
+ // unaltered.
+ frame->pc = map_info->start + adjusted_rel_pc;
+ frame->sp = regs->sp();
+ frame->rel_pc = adjusted_rel_pc;
+ frame->stack_size = 0;
+
+ frame->map.start = map_info->start;
+ frame->map.end = map_info->end;
+ frame->map.offset = map_info->offset;
+ frame->map.load_bias = elf->GetLoadBias();
+ frame->map.flags = map_info->flags;
+ frame->map.name = map_info->name;
+
+ uint64_t func_offset = 0;
+ if (elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
+ frame->func_name = demangle(frame->func_name.c_str());
+ } else {
+ frame->func_name = "";
+ }
+ frame->func_offset = func_offset;
+ if (num_frames > 0) {
+ // Set the stack size for the previous frame.
+ backtrace_frame_data_t* prev = &frames->at(num_frames - 1);
+ prev->stack_size = frame->sp - prev->sp;
+ }
+ num_frames++;
+ } else if (!skip_frame && num_ignore_frames > 0) {
+ num_ignore_frames--;
+ }
+ adjust_rel_pc = true;
+
+ // Do not unwind through a device map.
+ if (map_info->flags & PROT_DEVICE_MAP) {
+ break;
+ }
+ unwindstack::MapInfo* sp_info = maps->Find(regs->sp());
+ if (sp_info->flags & PROT_DEVICE_MAP) {
+ break;
+ }
+
+ if (!elf->Step(rel_pc + map_info->elf_offset, regs, stack_map->process_memory().get())) {
+ break;
+ }
+ }
+
+ return true;
+}
+
+UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
+ : BacktraceCurrent(pid, tid, map) {}
+
+std::string UnwindStackCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ return ::GetFunctionName(GetMap(), pc, offset);
+}
+
+bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
+ std::unique_ptr<unwindstack::Regs> regs;
+ if (ucontext == nullptr) {
+ regs.reset(unwindstack::Regs::CreateFromLocal());
+ // Fill in the registers from this function. Do it here to avoid
+ // one extra function call appearing in the unwind.
+ unwindstack::RegsGetLocal(regs.get());
+ } else {
+ regs.reset(
+ unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentMachineType(), ucontext));
+ }
+
+ error_ = BACKTRACE_UNWIND_NO_ERROR;
+ return ::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames);
+}
+
+UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
+ : BacktracePtrace(pid, tid, map) {}
+
+std::string UnwindStackPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ return ::GetFunctionName(GetMap(), pc, offset);
+}
+
+bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, ucontext_t* context) {
+ std::unique_ptr<unwindstack::Regs> regs;
+ if (context == nullptr) {
+ regs.reset(unwindstack::Regs::RemoteGet(Tid()));
+ } else {
+ regs.reset(
+ unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentMachineType(), context));
+ }
+
+ error_ = BACKTRACE_UNWIND_NO_ERROR;
+ return ::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames);
+}
+
+Backtrace* Backtrace::CreateNew(pid_t pid, pid_t tid, BacktraceMap* map) {
+ if (pid == BACKTRACE_CURRENT_PROCESS) {
+ pid = getpid();
+ if (tid == BACKTRACE_CURRENT_THREAD) {
+ tid = gettid();
+ }
+ } else if (tid == BACKTRACE_CURRENT_THREAD) {
+ tid = pid;
+ }
+
+ if (map == nullptr) {
+// This would cause the wrong type of map object to be created, so disallow.
+#if defined(__ANDROID__)
+ __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__,
+ "Backtrace::CreateNew() must be called with a real map pointer.");
+#else
+ BACK_LOGE("Backtrace::CreateNew() must be called with a real map pointer.");
+ abort();
+#endif
+ }
+
+ if (pid == getpid()) {
+ return new UnwindStackCurrent(pid, tid, map);
+ } else {
+ return new UnwindStackPtrace(pid, tid, map);
+ }
+}
diff --git a/libbacktrace/UnwindStack.h b/libbacktrace/UnwindStack.h
new file mode 100644
index 0000000..be9ef63
--- /dev/null
+++ b/libbacktrace/UnwindStack.h
@@ -0,0 +1,50 @@
+/*
+ * 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 _LIBBACKTRACE_UNWIND_STACK_H
+#define _LIBBACKTRACE_UNWIND_STACK_H
+
+#include <stdint.h>
+
+#include <string>
+
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Memory.h>
+
+#include "BacktraceCurrent.h"
+#include "BacktracePtrace.h"
+
+class UnwindStackCurrent : public BacktraceCurrent {
+ public:
+ UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map);
+ virtual ~UnwindStackCurrent() = default;
+
+ std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
+
+ bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
+};
+
+class UnwindStackPtrace : public BacktracePtrace {
+ public:
+ UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
+ virtual ~UnwindStackPtrace() = default;
+
+ bool Unwind(size_t num_ignore_frames, ucontext_t* context) override;
+
+ std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+};
+
+#endif // _LIBBACKTRACE_UNWIND_STACK_H
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
new file mode 100644
index 0000000..d4a2444
--- /dev/null
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 <stdlib.h>
+#include <sys/types.h>
+
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+
+#include "UnwindStackMap.h"
+
+//-------------------------------------------------------------------------
+UnwindStackMap::UnwindStackMap(pid_t pid) : BacktraceMap(pid) {}
+
+bool UnwindStackMap::Build() {
+ if (pid_ == 0) {
+ pid_ = getpid();
+ stack_maps_.reset(new unwindstack::LocalMaps);
+ } else {
+ stack_maps_.reset(new unwindstack::RemoteMaps(pid_));
+ }
+
+ // Create the process memory object.
+ process_memory_ = unwindstack::Memory::CreateProcessMemory(pid_);
+
+ if (!stack_maps_->Parse()) {
+ return false;
+ }
+
+ // Iterate through the maps and fill in the backtrace_map_t structure.
+ for (auto& map_info : *stack_maps_) {
+ backtrace_map_t map;
+ map.start = map_info.start;
+ map.end = map_info.end;
+ map.offset = map_info.offset;
+ // Set to -1 so that it is demand loaded.
+ map.load_bias = static_cast<uintptr_t>(-1);
+ map.flags = map_info.flags;
+ map.name = map_info.name;
+
+ maps_.push_back(map);
+ }
+
+ return true;
+}
+
+void UnwindStackMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
+ BacktraceMap::FillIn(addr, map);
+ if (map->load_bias != static_cast<uintptr_t>(-1)) {
+ return;
+ }
+
+ // Fill in the load_bias.
+ unwindstack::MapInfo* map_info = stack_maps_->Find(addr);
+ if (map_info == nullptr) {
+ return;
+ }
+ unwindstack::Elf* elf = map_info->GetElf(process_memory_, true);
+ map->load_bias = elf->GetLoadBias();
+}
+
+//-------------------------------------------------------------------------
+// BacktraceMap create function.
+//-------------------------------------------------------------------------
+BacktraceMap* BacktraceMap::CreateNew(pid_t pid, bool uncached) {
+ BacktraceMap* map;
+
+ if (uncached) {
+ // Force use of the base class to parse the maps when this call is made.
+ map = new BacktraceMap(pid);
+ } else if (pid == getpid()) {
+ map = new UnwindStackMap(0);
+ } else {
+ map = new UnwindStackMap(pid);
+ }
+ if (!map->Build()) {
+ delete map;
+ return nullptr;
+ }
+ return map;
+}
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
new file mode 100644
index 0000000..b93b340
--- /dev/null
+++ b/libbacktrace/UnwindStackMap.h
@@ -0,0 +1,46 @@
+/*
+ * 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 _LIBBACKTRACE_UNWINDSTACK_MAP_H
+#define _LIBBACKTRACE_UNWINDSTACK_MAP_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Maps.h>
+
+class UnwindStackMap : public BacktraceMap {
+ public:
+ explicit UnwindStackMap(pid_t pid);
+ ~UnwindStackMap() = default;
+
+ bool Build() override;
+
+ void FillIn(uintptr_t addr, backtrace_map_t* map) override;
+
+ unwindstack::Maps* stack_maps() { return stack_maps_.get(); }
+
+ const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
+
+ protected:
+ std::unique_ptr<unwindstack::Maps> stack_maps_;
+ std::shared_ptr<unwindstack::Memory> process_memory_;
+};
+
+#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/backtrace_benchmarks.cpp b/libbacktrace/backtrace_benchmarks.cpp
new file mode 100644
index 0000000..30c2a55
--- /dev/null
+++ b/libbacktrace/backtrace_benchmarks.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+
+#include <benchmark/benchmark.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+// Definitions of prctl arguments to set a vma name in Android kernels.
+#define ANDROID_PR_SET_VMA 0x53564d41
+#define ANDROID_PR_SET_VMA_ANON_NAME 0
+
+constexpr size_t kNumMaps = 2000;
+constexpr size_t kNumIterations = 1000;
+
+static bool CountMaps(pid_t pid, size_t* num_maps) {
+ // Minimize the calls that might allocate memory. If too much memory
+ // gets allocated, then this routine will add extra maps and the next
+ // call will fail to get the same number of maps as before.
+ int fd =
+ open((std::string("/proc/") + std::to_string(pid) + "/maps").c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ fprintf(stderr, "Cannot open map file for pid %d: %s\n", pid, strerror(errno));
+ return false;
+ }
+ *num_maps = 0;
+ while (true) {
+ char buffer[2048];
+ ssize_t bytes = read(fd, buffer, sizeof(buffer));
+ if (bytes <= 0) {
+ break;
+ }
+ // Count the '\n'.
+ for (size_t i = 0; i < static_cast<size_t>(bytes); i++) {
+ if (buffer[i] == '\n') {
+ ++*num_maps;
+ }
+ }
+ }
+
+ close(fd);
+ return true;
+}
+
+static void CreateMap(benchmark::State& state, BacktraceMap* (*map_func)(pid_t, bool)) {
+ state.PauseTiming();
+ // Create a remote process so that the map data is exactly the same.
+ // Also, so that we can create a set number of maps.
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ size_t num_maps;
+ if (!CountMaps(getpid(), &num_maps)) {
+ exit(1);
+ }
+ // Create uniquely named maps.
+ std::vector<void*> maps;
+ for (size_t i = num_maps; i < kNumMaps; i++) {
+ int flags = PROT_READ | PROT_WRITE;
+ // Alternate page type to make sure a map entry is added for each call.
+ if ((i % 2) == 0) {
+ flags |= PROT_EXEC;
+ }
+ void* memory = mmap(nullptr, PAGE_SIZE, flags, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (memory == MAP_FAILED) {
+ fprintf(stderr, "Failed to create map: %s\n", strerror(errno));
+ exit(1);
+ }
+ memset(memory, 0x1, PAGE_SIZE);
+ if (prctl(ANDROID_PR_SET_VMA, ANDROID_PR_SET_VMA_ANON_NAME, memory, PAGE_SIZE, "test_map") ==
+ -1) {
+ fprintf(stderr, "Failed: %s\n", strerror(errno));
+ }
+ maps.push_back(memory);
+ }
+
+ if (!CountMaps(getpid(), &num_maps)) {
+ exit(1);
+ }
+
+ if (num_maps != kNumMaps) {
+ fprintf(stderr, "Maps set incorrectly: %zu found, %zu expected.\n", num_maps, kNumMaps);
+ std::string str;
+ android::base::ReadFileToString("/proc/self/maps", &str);
+ fprintf(stderr, "%s\n", str.c_str());
+ exit(1);
+ }
+
+ // Wait for an hour at most.
+ sleep(3600);
+ exit(1);
+ } else if (pid < 0) {
+ fprintf(stderr, "Fork failed: %s\n", strerror(errno));
+ return;
+ }
+
+ size_t num_maps = 0;
+ for (size_t i = 0; i < 2000; i++) {
+ if (CountMaps(pid, &num_maps) && num_maps == kNumMaps) {
+ break;
+ }
+ usleep(1000);
+ }
+ if (num_maps != kNumMaps) {
+ fprintf(stderr, "Timed out waiting for the number of maps available: %zu\n", num_maps);
+ return;
+ }
+
+ state.ResumeTiming();
+ while (state.KeepRunning()) {
+ for (size_t i = 0; i < static_cast<size_t>(state.range(0)); i++) {
+ BacktraceMap* map = map_func(pid, false);
+ if (map == nullptr) {
+ fprintf(stderr, "Failed to create map\n");
+ return;
+ }
+ delete map;
+ }
+ }
+ state.PauseTiming();
+
+ kill(pid, SIGKILL);
+ waitpid(pid, nullptr, 0);
+}
+
+static void BM_create_map(benchmark::State& state) {
+ CreateMap(state, BacktraceMap::Create);
+}
+BENCHMARK(BM_create_map)->Arg(kNumIterations);
+
+static void BM_create_map_new(benchmark::State& state) {
+ CreateMap(state, BacktraceMap::CreateNew);
+}
+BENCHMARK(BM_create_map_new)->Arg(kNumIterations);
+
+BENCHMARK_MAIN();
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 465b3f9..0a1f33d 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -27,6 +27,7 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <backtrace/Backtrace.h>
@@ -129,6 +130,10 @@
return nullptr;
}
+std::string GetTestPath(std::string path) {
+ return android::base::GetExecutableDirectory() + "/testdata/" + ABI_STRING + '/' + path;
+}
+
// This test is disable because it is for generating test data.
TEST(libbacktrace, DISABLED_generate_offline_testdata) {
// Create a thread to generate the needed stack and registers information.
@@ -167,9 +172,9 @@
// 2. Dump maps
for (auto it = map->begin(); it != map->end(); ++it) {
testdata += android::base::StringPrintf(
- "map: start: %" PRIxPTR " end: %" PRIxPTR " offset: %" PRIxPTR
- " load_base: %" PRIxPTR " flags: %d name: %s\n",
- it->start, it->end, it->offset, it->load_base, it->flags, it->name.c_str());
+ "map: start: %" PRIxPTR " end: %" PRIxPTR " offset: %" PRIxPTR " load_bias: %" PRIxPTR
+ " flags: %d name: %s\n",
+ it->start, it->end, it->offset, it->load_bias, it->flags, it->name.c_str());
}
// 3. Dump registers
testdata += android::base::StringPrintf("registers: %zu ", sizeof(arg.unw_context));
@@ -206,20 +211,6 @@
return "";
}
-static std::string GetArch() {
-#if defined(__arm__)
- return "arm";
-#elif defined(__aarch64__)
- return "aarch64";
-#elif defined(__i386__)
- return "x86";
-#elif defined(__x86_64__)
- return "x86_64";
-#else
- return "";
-#endif
-}
-
struct OfflineTestData {
int pid;
int tid;
@@ -246,9 +237,9 @@
backtrace_map_t& map = testdata->maps.back();
int pos;
sscanf(line.c_str(),
- "map: start: %" SCNxPTR " end: %" SCNxPTR " offset: %" SCNxPTR
- " load_base: %" SCNxPTR " flags: %d name: %n",
- &map.start, &map.end, &map.offset, &map.load_base, &map.flags, &pos);
+ "map: start: %" SCNxPTR " end: %" SCNxPTR " offset: %" SCNxPTR " load_bias: %" SCNxPTR
+ " flags: %d name: %n",
+ &map.start, &map.end, &map.offset, &map.load_bias, &map.flags, &pos);
map.name = android::base::Trim(line.substr(pos));
} else if (android::base::StartsWith(line, "registers:")) {
size_t size;
@@ -280,20 +271,15 @@
return true;
}
-static void BacktraceOfflineTest(const std::string& testlib_name) {
- const std::string arch = GetArch();
- if (arch.empty()) {
- GTEST_LOG_(INFO) << "This test does nothing on current arch.";
- return;
- }
- const std::string testlib_path = "testdata/" + arch + "/" + testlib_name;
- struct stat st;
- if (stat(testlib_path.c_str(), &st) == -1) {
- GTEST_LOG_(INFO) << "This test is skipped as " << testlib_path << " doesn't exist.";
+static void BacktraceOfflineTest(const char* arch, const std::string& testlib_name) {
+ // TODO: For now, we can only run this on the same arch as the library arch.
+ if (std::string(ABI_STRING) != arch) {
+ GTEST_LOG_(INFO) << "Ignoring arch " << arch << " for lib " << testlib_name;
return;
}
- const std::string offline_testdata_path = "testdata/" + arch + "/offline_testdata";
+ const std::string testlib_path(GetTestPath(testlib_name));
+ const std::string offline_testdata_path(GetTestPath("offline_testdata"));
OfflineTestData testdata;
ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
@@ -339,35 +325,40 @@
testdata.symbols));
}
+// For now, these tests can only run on the given architectures.
TEST(libbacktrace, offline_eh_frame) {
- BacktraceOfflineTest("libbacktrace_test_eh_frame.so");
+ BacktraceOfflineTest("arm64", "libbacktrace_test_eh_frame.so");
+ BacktraceOfflineTest("x86_64", "libbacktrace_test_eh_frame.so");
}
TEST(libbacktrace, offline_debug_frame) {
- BacktraceOfflineTest("libbacktrace_test_debug_frame.so");
+ BacktraceOfflineTest("arm", "libbacktrace_test_debug_frame.so");
+ BacktraceOfflineTest("x86", "libbacktrace_test_debug_frame.so");
}
TEST(libbacktrace, offline_gnu_debugdata) {
- BacktraceOfflineTest("libbacktrace_test_gnu_debugdata.so");
+ BacktraceOfflineTest("arm", "libbacktrace_test_gnu_debugdata.so");
+ BacktraceOfflineTest("x86", "libbacktrace_test_gnu_debugdata.so");
}
TEST(libbacktrace, offline_arm_exidx) {
- BacktraceOfflineTest("libbacktrace_test_arm_exidx.so");
+ BacktraceOfflineTest("arm", "libbacktrace_test_arm_exidx.so");
}
// This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
// overlap with each other, which appears in /system/lib/libart.so.
TEST(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
- const std::string arch = GetArch();
- if (arch.empty() || arch != "arm") {
- GTEST_LOG_(INFO) << "This test does nothing on current arch.";
+ // TODO: For now, only run on the given arch.
+ if (std::string(ABI_STRING) != "arm") {
+ GTEST_LOG_(INFO) << "Skipping test since offline for arm on " << ABI_STRING
+ << " isn't supported.";
return;
}
- const std::string testlib_path = "testdata/" + arch + "/libart.so";
+ const std::string testlib_path(GetTestPath("libart.so"));
struct stat st;
ASSERT_EQ(0, stat(testlib_path.c_str(), &st)) << "can't find testlib " << testlib_path;
- const std::string offline_testdata_path = "testdata/" + arch + "/offline_testdata_for_libart";
+ const std::string offline_testdata_path(GetTestPath("offline_testdata_for_libart"));
OfflineTestData testdata;
ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
@@ -392,8 +383,8 @@
// The last frame is outside of libart.so
ASSERT_EQ(testdata.symbols.size() + 1, backtrace->NumFrames());
for (size_t i = 0; i + 1 < backtrace->NumFrames(); ++i) {
- uintptr_t vaddr_in_file = backtrace->GetFrame(i)->pc - testdata.maps[0].start +
- testdata.maps[0].load_base;
+ uintptr_t vaddr_in_file =
+ backtrace->GetFrame(i)->pc - testdata.maps[0].start + testdata.maps[0].load_bias;
std::string name = FunctionNameForAddress(vaddr_in_file, testdata.symbols);
ASSERT_EQ(name, testdata.symbols[i].name);
}
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index fb463b0..9fe2d1c 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -304,8 +304,10 @@
ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, nullptr), 0);
}
-static void VerifyProcTest(pid_t pid, pid_t tid, bool share_map, bool (*ReadyFunc)(Backtrace*),
- void (*VerifyFunc)(Backtrace*)) {
+static void VerifyProcTest(pid_t pid, pid_t tid, bool (*ReadyFunc)(Backtrace*),
+ void (*VerifyFunc)(Backtrace*),
+ Backtrace* (*back_func)(pid_t, pid_t, BacktraceMap*),
+ BacktraceMap* (*map_func)(pid_t, bool)) {
pid_t ptrace_tid;
if (tid < 0) {
ptrace_tid = pid;
@@ -322,10 +324,8 @@
WaitForStop(ptrace_tid);
std::unique_ptr<BacktraceMap> map;
- if (share_map) {
- map.reset(BacktraceMap::Create(pid));
- }
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
+ map.reset(map_func(pid, false));
+ std::unique_ptr<Backtrace> backtrace(back_func(pid, tid, map.get()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
@@ -349,21 +349,22 @@
ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
_exit(1);
}
- VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump);
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump,
+ Backtrace::Create, BacktraceMap::Create);
kill(pid, SIGKILL);
int status;
ASSERT_EQ(waitpid(pid, &status, 0), pid);
}
-TEST(libbacktrace, ptrace_trace_shared_map) {
+TEST(libbacktrace, ptrace_trace_new) {
pid_t pid;
if ((pid = fork()) == 0) {
ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
_exit(1);
}
-
- VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump);
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump,
+ Backtrace::CreateNew, BacktraceMap::CreateNew);
kill(pid, SIGKILL);
int status;
@@ -376,7 +377,22 @@
ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, nullptr, nullptr), 0);
_exit(1);
}
- VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump);
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump, Backtrace::Create,
+ BacktraceMap::Create);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+TEST(libbacktrace, ptrace_max_trace_new) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES + 10, nullptr, nullptr), 0);
+ _exit(1);
+ }
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump,
+ Backtrace::CreateNew, BacktraceMap::CreateNew);
kill(pid, SIGKILL);
int status;
@@ -403,7 +419,22 @@
ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
_exit(1);
}
- VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames,
+ Backtrace::Create, BacktraceMap::Create);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+TEST(libbacktrace, ptrace_ignore_frames_new) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ _exit(1);
+ }
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames,
+ Backtrace::CreateNew, BacktraceMap::CreateNew);
kill(pid, SIGKILL);
int status;
@@ -466,7 +497,47 @@
if (pid == *it) {
continue;
}
- VerifyProcTest(pid, *it, false, ReadyLevelBacktrace, VerifyLevelDump);
+ VerifyProcTest(pid, *it, ReadyLevelBacktrace, VerifyLevelDump, Backtrace::Create,
+ BacktraceMap::Create);
+ }
+
+ FinishRemoteProcess(pid);
+}
+
+TEST(libbacktrace, ptrace_threads_new) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, nullptr) == 0);
+ }
+ ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ _exit(1);
+ }
+
+ // Check to see that all of the threads are running before unwinding.
+ std::vector<pid_t> threads;
+ uint64_t start = NanoTime();
+ do {
+ usleep(US_PER_MSEC);
+ threads.clear();
+ GetThreads(pid, &threads);
+ } while ((threads.size() != NUM_PTRACE_THREADS + 1) && ((NanoTime() - start) <= 5 * NS_PER_SEC));
+ ASSERT_EQ(threads.size(), static_cast<size_t>(NUM_PTRACE_THREADS + 1));
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+ WaitForStop(pid);
+ for (std::vector<int>::const_iterator it = threads.begin(); it != threads.end(); ++it) {
+ // Skip the current forked process, we only care about the threads.
+ if (pid == *it) {
+ continue;
+ }
+ VerifyProcTest(pid, *it, ReadyLevelBacktrace, VerifyLevelDump, Backtrace::CreateNew,
+ BacktraceMap::CreateNew);
}
FinishRemoteProcess(pid);
@@ -784,6 +855,7 @@
backtrace_frame_data_t frame;
frame.num = 1;
frame.pc = 2;
+ frame.rel_pc = 2;
frame.sp = 0;
frame.stack_size = 0;
frame.func_offset = 0;
@@ -799,9 +871,10 @@
// Check map name empty, but exists.
frame.pc = 0xb0020;
+ frame.rel_pc = 0x20;
frame.map.start = 0xb0000;
frame.map.end = 0xbffff;
- frame.map.load_base = 0;
+ frame.map.load_bias = 0;
#if defined(__LP64__)
EXPECT_EQ("#01 pc 0000000000000020 <anonymous:00000000000b0000>",
#else
@@ -813,7 +886,7 @@
frame.pc = 0xc0020;
frame.map.start = 0xc0000;
frame.map.end = 0xcffff;
- frame.map.load_base = 0;
+ frame.map.load_bias = 0;
frame.map.name = "[anon:thread signal stack]";
#if defined(__LP64__)
EXPECT_EQ("#01 pc 0000000000000020 [anon:thread signal stack:00000000000c0000]",
@@ -824,6 +897,7 @@
// Check relative pc is set and map name is set.
frame.pc = 0x12345679;
+ frame.rel_pc = 0x12345678;
frame.map.name = "MapFake";
frame.map.start = 1;
frame.map.end = 1;
@@ -852,9 +926,10 @@
#endif
backtrace->FormatFrameData(&frame));
- // Check func_name is set, func offset is non-zero, and load_base is non-zero.
+ // Check func_name is set, func offset is non-zero, and load_bias is non-zero.
+ frame.rel_pc = 0x123456dc;
frame.func_offset = 645;
- frame.map.load_base = 100;
+ frame.map.load_bias = 100;
#if defined(__LP64__)
EXPECT_EQ("#01 pc 00000000123456dc MapFake (ProcFake+645)",
#else
@@ -1575,7 +1650,7 @@
munmap(device_map, DEVICE_MAP_SIZE);
}
-TEST(libbacktrace, unwind_disallow_device_map_remote) {
+TEST(libbacktrace, unwind_disallow_device_map_remote_new) {
void* device_map;
SetupDeviceMap(&device_map);
@@ -1584,13 +1659,11 @@
CreateRemoteProcess(&pid);
// Now create an unwind object.
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::CreateNew(pid));
+ ASSERT_TRUE(map.get() != nullptr);
+ std::unique_ptr<Backtrace> backtrace(Backtrace::CreateNew(pid, pid, map.get()));
- // TODO: Currently unwind from context doesn't work on remote
- // unwind. Keep this test because the new unwinder should support
- // this eventually, or we can delete this test.
- // properly with unwind from context.
- // UnwindFromDevice(backtrace.get(), device_map);
+ UnwindFromDevice(backtrace.get(), device_map);
FinishRemoteProcess(pid);
@@ -1629,7 +1702,9 @@
;
}
-static void UnwindThroughSignal(bool use_action) {
+static void UnwindThroughSignal(bool use_action,
+ Backtrace* (*back_func)(pid_t, pid_t, BacktraceMap*),
+ BacktraceMap* (*map_func)(pid_t, bool)) {
volatile int value = 0;
pid_t pid;
if ((pid = fork()) == 0) {
@@ -1655,7 +1730,8 @@
WaitForStop(pid);
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+ std::unique_ptr<BacktraceMap> map(map_func(pid, false));
+ std::unique_ptr<Backtrace> backtrace(back_func(pid, pid, map.get()));
size_t bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(const_cast<int*>(&value)),
reinterpret_cast<uint8_t*>(&read_value), sizeof(read_value));
@@ -1673,6 +1749,7 @@
// Wait for the process to get to the signal handler loop.
Backtrace::const_iterator frame_iter;
start = NanoTime();
+ std::unique_ptr<BacktraceMap> map;
std::unique_ptr<Backtrace> backtrace;
while (true) {
usleep(1000);
@@ -1681,7 +1758,9 @@
WaitForStop(pid);
- backtrace.reset(Backtrace::Create(pid, pid));
+ map.reset(map_func(pid, false));
+ ASSERT_TRUE(map.get() != nullptr);
+ backtrace.reset(back_func(pid, pid, map.get()));
ASSERT_TRUE(backtrace->Unwind(0));
bool found = false;
for (frame_iter = backtrace->begin(); frame_iter != backtrace->end(); ++frame_iter) {
@@ -1737,9 +1816,21 @@
FinishRemoteProcess(pid);
}
-TEST(libbacktrace, unwind_remote_through_signal_using_handler) { UnwindThroughSignal(false); }
+TEST(libbacktrace, unwind_remote_through_signal_using_handler) {
+ UnwindThroughSignal(false, Backtrace::Create, BacktraceMap::Create);
+}
-TEST(libbacktrace, unwind_remote_through_signal_using_action) { UnwindThroughSignal(true); }
+TEST(libbacktrace, unwind_remote_through_signal_using_handler_new) {
+ UnwindThroughSignal(false, Backtrace::CreateNew, BacktraceMap::CreateNew);
+}
+
+TEST(libbacktrace, unwind_remote_through_signal_using_action) {
+ UnwindThroughSignal(true, Backtrace::Create, BacktraceMap::Create);
+}
+
+TEST(libbacktrace, unwind_remote_through_signal_using_action_new) {
+ UnwindThroughSignal(true, Backtrace::CreateNew, BacktraceMap::CreateNew);
+}
#if defined(ENABLE_PSS_TESTS)
#include "GetPss.h"
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 4f73a65..d67ea50 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -55,6 +55,7 @@
struct backtrace_frame_data_t {
size_t num; // The current fame number.
uintptr_t pc; // The absolute pc.
+ uintptr_t rel_pc; // The relative pc.
uintptr_t sp; // The top of the stack.
size_t stack_size; // The size of the stack, zero indicate an unknown stack size.
backtrace_map_t map; // The map associated with the given pc.
@@ -89,6 +90,8 @@
// If map is NULL, then create the map and manage it internally.
// If map is not NULL, the map is still owned by the caller.
static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
+ // Same as above, but uses a different underlying unwinder.
+ static Backtrace* CreateNew(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
// Create an offline Backtrace object that can be used to do an unwind without a process
// that is still running. If cache_file is set to true, then elf information will be cached
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index 8ab0dfa..963c34b 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -41,7 +41,7 @@
uintptr_t start = 0;
uintptr_t end = 0;
uintptr_t offset = 0;
- uintptr_t load_base = 0;
+ uintptr_t load_bias = 0;
int flags = 0;
std::string name;
};
@@ -52,6 +52,8 @@
// Passing a map created with uncached set to true to Backtrace::Create()
// is unsupported.
static BacktraceMap* Create(pid_t pid, bool uncached = false);
+ // Same as above, but is compatible with the new unwinder.
+ static BacktraceMap* CreateNew(pid_t pid, bool uncached = false);
static BacktraceMap* Create(pid_t pid, const std::vector<backtrace_map_t>& maps);
@@ -95,14 +97,6 @@
return map.end > 0;
}
- static uintptr_t GetRelativePc(const backtrace_map_t& map, uintptr_t pc) {
- if (IsValid(map)) {
- return pc - map.start + map.load_base;
- } else {
- return pc;
- }
- }
-
protected:
BacktraceMap(pid_t pid);
diff --git a/libbacktrace/include/backtrace/backtrace_constants.h b/libbacktrace/include/backtrace/backtrace_constants.h
index f8c1575..373a1e5 100644
--- a/libbacktrace/include/backtrace/backtrace_constants.h
+++ b/libbacktrace/include/backtrace/backtrace_constants.h
@@ -20,10 +20,10 @@
// When the pid to be traced is set to this value, then trace the current
// process. If the tid value is not BACKTRACE_NO_TID, then the specified
// thread from the current process will be traced.
-#define BACKTRACE_CURRENT_PROCESS -1
+#define BACKTRACE_CURRENT_PROCESS (-1)
// When the tid to be traced is set to this value, then trace the specified
// current thread of the specified pid.
-#define BACKTRACE_CURRENT_THREAD -1
+#define BACKTRACE_CURRENT_THREAD (-1)
#define MAX_BACKTRACE_FRAMES 64
diff --git a/libbacktrace/testdata/arm/offline_testdata b/libbacktrace/testdata/arm/offline_testdata
index 43d305a..6acea29 100644
--- a/libbacktrace/testdata/arm/offline_testdata
+++ b/libbacktrace/testdata/arm/offline_testdata
@@ -1,98 +1,98 @@
pid: 32232 tid: 32233
-map: start: aad19000 end: aad6c000 offset: 0 load_base: 0 flags: 5 name: /data/backtrace_test32
-map: start: aad6c000 end: aad6e000 offset: 52000 load_base: 0 flags: 1 name: /data/backtrace_test32
-map: start: aad6e000 end: aad6f000 offset: 54000 load_base: 0 flags: 3 name: /data/backtrace_test32
-map: start: e7380000 end: e7400000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
-map: start: e745f000 end: e7463000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libnetd_client.so
-map: start: e7463000 end: e7464000 offset: 3000 load_base: 0 flags: 1 name: /system/lib/libnetd_client.so
-map: start: e7464000 end: e7465000 offset: 4000 load_base: 0 flags: 3 name: /system/lib/libnetd_client.so
-map: start: e7480000 end: e7500000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
-map: start: e7558000 end: e756c000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libunwind.so
-map: start: e756c000 end: e756d000 offset: 0 load_base: 0 flags: 0 name:
-map: start: e756d000 end: e756e000 offset: 14000 load_base: 0 flags: 1 name: /system/lib/libunwind.so
-map: start: e756e000 end: e756f000 offset: 15000 load_base: 0 flags: 3 name: /system/lib/libunwind.so
-map: start: e756f000 end: e75b5000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e75d4000 end: e75e1000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libbase.so
-map: start: e75e1000 end: e75e2000 offset: c000 load_base: 0 flags: 1 name: /system/lib/libbase.so
-map: start: e75e2000 end: e75e3000 offset: d000 load_base: 0 flags: 3 name: /system/lib/libbase.so
-map: start: e7600000 end: e7616000 offset: 0 load_base: 0 flags: 5 name: /system/lib/liblzma.so
-map: start: e7616000 end: e7617000 offset: 15000 load_base: 0 flags: 1 name: /system/lib/liblzma.so
-map: start: e7617000 end: e7618000 offset: 16000 load_base: 0 flags: 3 name: /system/lib/liblzma.so
-map: start: e7618000 end: e761d000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e7647000 end: e7656000 offset: 0 load_base: 0 flags: 5 name: /system/lib/liblog.so
-map: start: e7656000 end: e7657000 offset: e000 load_base: 0 flags: 1 name: /system/lib/liblog.so
-map: start: e7657000 end: e7658000 offset: f000 load_base: 0 flags: 3 name: /system/lib/liblog.so
-map: start: e7681000 end: e76a2000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libm.so
-map: start: e76a2000 end: e76a3000 offset: 20000 load_base: 0 flags: 1 name: /system/lib/libm.so
-map: start: e76a3000 end: e76a4000 offset: 21000 load_base: 0 flags: 3 name: /system/lib/libm.so
-map: start: e76eb000 end: e76ee000 offset: 0 load_base: 0 flags: 5 name: /data/libbacktrace_test.so
-map: start: e76ee000 end: e76ef000 offset: 2000 load_base: 0 flags: 1 name: /data/libbacktrace_test.so
-map: start: e76ef000 end: e76f0000 offset: 3000 load_base: 0 flags: 3 name: /data/libbacktrace_test.so
-map: start: e7712000 end: e771f000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libbacktrace.so
-map: start: e771f000 end: e7720000 offset: 0 load_base: 0 flags: 0 name:
-map: start: e7720000 end: e7721000 offset: d000 load_base: 0 flags: 1 name: /system/lib/libbacktrace.so
-map: start: e7721000 end: e7722000 offset: e000 load_base: 0 flags: 3 name: /system/lib/libbacktrace.so
-map: start: e7761000 end: e7778000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libutils.so
-map: start: e7778000 end: e7779000 offset: 16000 load_base: 0 flags: 1 name: /system/lib/libutils.so
-map: start: e7779000 end: e777a000 offset: 17000 load_base: 0 flags: 3 name: /system/lib/libutils.so
-map: start: e77a5000 end: e782d000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libc.so
-map: start: e782d000 end: e7831000 offset: 87000 load_base: 0 flags: 1 name: /system/lib/libc.so
-map: start: e7831000 end: e7833000 offset: 8b000 load_base: 0 flags: 3 name: /system/lib/libc.so
-map: start: e7833000 end: e7834000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e7834000 end: e7835000 offset: 0 load_base: 0 flags: 1 name: [anon:.bss]
-map: start: e7835000 end: e783b000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e7845000 end: e8437000 offset: 0 load_base: 2b000 flags: 5 name: /system/lib/libLLVM.so
-map: start: e8437000 end: e8438000 offset: 0 load_base: 0 flags: 0 name:
-map: start: e8438000 end: e848a000 offset: bf2000 load_base: 0 flags: 1 name: /system/lib/libLLVM.so
-map: start: e848a000 end: e848b000 offset: c44000 load_base: 0 flags: 3 name: /system/lib/libLLVM.so
-map: start: e848b000 end: e84a1000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e84eb000 end: e84f7000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libcutils.so
-map: start: e84f7000 end: e84f8000 offset: 0 load_base: 0 flags: 0 name:
-map: start: e84f8000 end: e84f9000 offset: c000 load_base: 0 flags: 1 name: /system/lib/libcutils.so
-map: start: e84f9000 end: e84fa000 offset: d000 load_base: 0 flags: 3 name: /system/lib/libcutils.so
-map: start: e852e000 end: e85b3000 offset: 0 load_base: 2000 flags: 5 name: /system/lib/libc++.so
-map: start: e85b3000 end: e85b4000 offset: 0 load_base: 0 flags: 0 name:
-map: start: e85b4000 end: e85b8000 offset: 85000 load_base: 0 flags: 1 name: /system/lib/libc++.so
-map: start: e85b8000 end: e85b9000 offset: 89000 load_base: 0 flags: 3 name: /system/lib/libc++.so
-map: start: e85b9000 end: e85ba000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e85ce000 end: e85cf000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
-map: start: e85e4000 end: e85e5000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: e8607000 end: e8608000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: e8680000 end: e8700000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
-map: start: e870d000 end: e8719000 offset: 0 load_base: 0 flags: 3 name:
-map: start: e8719000 end: e871b000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
-map: start: e871b000 end: e873b000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
-map: start: e873b000 end: e875b000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
-map: start: e875b000 end: e875c000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e875c000 end: e875d000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
-map: start: e875d000 end: e875e000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
-map: start: e875e000 end: e875f000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: e875f000 end: e877f000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
-map: start: e877f000 end: e879f000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
-map: start: e879f000 end: e87a0000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e87a0000 end: e87a1000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: e87a1000 end: e87a2000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e87a2000 end: e87a3000 offset: 0 load_base: 0 flags: 0 name:
-map: start: e87a3000 end: e87a4000 offset: 0 load_base: 0 flags: 3 name:
-map: start: e87a4000 end: e87a5000 offset: 0 load_base: 0 flags: 0 name:
-map: start: e87a5000 end: e87a6000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_lob]
-map: start: e87a6000 end: e87a7000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: e87a7000 end: e87a8000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e87a8000 end: e87a9000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: e87a9000 end: e87aa000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e87aa000 end: e87ab000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: e87ab000 end: e87ac000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
-map: start: e87ac000 end: e87ad000 offset: 0 load_base: 0 flags: 0 name: [anon:thread signal stack guard page]
-map: start: e87ad000 end: e87af000 offset: 0 load_base: 0 flags: 3 name: [anon:thread signal stack]
-map: start: e87af000 end: e87b0000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
-map: start: e87b0000 end: e880d000 offset: 0 load_base: 0 flags: 5 name: /system/bin/linker
-map: start: e880d000 end: e880f000 offset: 5c000 load_base: 0 flags: 1 name: /system/bin/linker
-map: start: e880f000 end: e8810000 offset: 5e000 load_base: 0 flags: 3 name: /system/bin/linker
-map: start: e8810000 end: e8812000 offset: 0 load_base: 0 flags: 3 name:
-map: start: e8812000 end: e8813000 offset: 0 load_base: 0 flags: 1 name:
-map: start: e8813000 end: e8815000 offset: 0 load_base: 0 flags: 3 name:
-map: start: ff886000 end: ff8a9000 offset: 0 load_base: 0 flags: 3 name: [stack]
-map: start: ffff0000 end: ffff1000 offset: 0 load_base: 0 flags: 5 name: [vectors]
+map: start: aad19000 end: aad6c000 offset: 0 load_bias: 0 flags: 5 name: /data/backtrace_test32
+map: start: aad6c000 end: aad6e000 offset: 52000 load_bias: 0 flags: 1 name: /data/backtrace_test32
+map: start: aad6e000 end: aad6f000 offset: 54000 load_bias: 0 flags: 3 name: /data/backtrace_test32
+map: start: e7380000 end: e7400000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e745f000 end: e7463000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libnetd_client.so
+map: start: e7463000 end: e7464000 offset: 3000 load_bias: 0 flags: 1 name: /system/lib/libnetd_client.so
+map: start: e7464000 end: e7465000 offset: 4000 load_bias: 0 flags: 3 name: /system/lib/libnetd_client.so
+map: start: e7480000 end: e7500000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e7558000 end: e756c000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libunwind.so
+map: start: e756c000 end: e756d000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e756d000 end: e756e000 offset: 14000 load_bias: 0 flags: 1 name: /system/lib/libunwind.so
+map: start: e756e000 end: e756f000 offset: 15000 load_bias: 0 flags: 3 name: /system/lib/libunwind.so
+map: start: e756f000 end: e75b5000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e75d4000 end: e75e1000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libbase.so
+map: start: e75e1000 end: e75e2000 offset: c000 load_bias: 0 flags: 1 name: /system/lib/libbase.so
+map: start: e75e2000 end: e75e3000 offset: d000 load_bias: 0 flags: 3 name: /system/lib/libbase.so
+map: start: e7600000 end: e7616000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/liblzma.so
+map: start: e7616000 end: e7617000 offset: 15000 load_bias: 0 flags: 1 name: /system/lib/liblzma.so
+map: start: e7617000 end: e7618000 offset: 16000 load_bias: 0 flags: 3 name: /system/lib/liblzma.so
+map: start: e7618000 end: e761d000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e7647000 end: e7656000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/liblog.so
+map: start: e7656000 end: e7657000 offset: e000 load_bias: 0 flags: 1 name: /system/lib/liblog.so
+map: start: e7657000 end: e7658000 offset: f000 load_bias: 0 flags: 3 name: /system/lib/liblog.so
+map: start: e7681000 end: e76a2000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libm.so
+map: start: e76a2000 end: e76a3000 offset: 20000 load_bias: 0 flags: 1 name: /system/lib/libm.so
+map: start: e76a3000 end: e76a4000 offset: 21000 load_bias: 0 flags: 3 name: /system/lib/libm.so
+map: start: e76eb000 end: e76ee000 offset: 0 load_bias: 0 flags: 5 name: /data/libbacktrace_test.so
+map: start: e76ee000 end: e76ef000 offset: 2000 load_bias: 0 flags: 1 name: /data/libbacktrace_test.so
+map: start: e76ef000 end: e76f0000 offset: 3000 load_bias: 0 flags: 3 name: /data/libbacktrace_test.so
+map: start: e7712000 end: e771f000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libbacktrace.so
+map: start: e771f000 end: e7720000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e7720000 end: e7721000 offset: d000 load_bias: 0 flags: 1 name: /system/lib/libbacktrace.so
+map: start: e7721000 end: e7722000 offset: e000 load_bias: 0 flags: 3 name: /system/lib/libbacktrace.so
+map: start: e7761000 end: e7778000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libutils.so
+map: start: e7778000 end: e7779000 offset: 16000 load_bias: 0 flags: 1 name: /system/lib/libutils.so
+map: start: e7779000 end: e777a000 offset: 17000 load_bias: 0 flags: 3 name: /system/lib/libutils.so
+map: start: e77a5000 end: e782d000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libc.so
+map: start: e782d000 end: e7831000 offset: 87000 load_bias: 0 flags: 1 name: /system/lib/libc.so
+map: start: e7831000 end: e7833000 offset: 8b000 load_bias: 0 flags: 3 name: /system/lib/libc.so
+map: start: e7833000 end: e7834000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e7834000 end: e7835000 offset: 0 load_bias: 0 flags: 1 name: [anon:.bss]
+map: start: e7835000 end: e783b000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e7845000 end: e8437000 offset: 0 load_bias: 2b000 flags: 5 name: /system/lib/libLLVM.so
+map: start: e8437000 end: e8438000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e8438000 end: e848a000 offset: bf2000 load_bias: 0 flags: 1 name: /system/lib/libLLVM.so
+map: start: e848a000 end: e848b000 offset: c44000 load_bias: 0 flags: 3 name: /system/lib/libLLVM.so
+map: start: e848b000 end: e84a1000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e84eb000 end: e84f7000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libcutils.so
+map: start: e84f7000 end: e84f8000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e84f8000 end: e84f9000 offset: c000 load_bias: 0 flags: 1 name: /system/lib/libcutils.so
+map: start: e84f9000 end: e84fa000 offset: d000 load_bias: 0 flags: 3 name: /system/lib/libcutils.so
+map: start: e852e000 end: e85b3000 offset: 0 load_bias: 2000 flags: 5 name: /system/lib/libc++.so
+map: start: e85b3000 end: e85b4000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e85b4000 end: e85b8000 offset: 85000 load_bias: 0 flags: 1 name: /system/lib/libc++.so
+map: start: e85b8000 end: e85b9000 offset: 89000 load_bias: 0 flags: 3 name: /system/lib/libc++.so
+map: start: e85b9000 end: e85ba000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e85ce000 end: e85cf000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: e85e4000 end: e85e5000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e8607000 end: e8608000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e8680000 end: e8700000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e870d000 end: e8719000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: e8719000 end: e871b000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: e871b000 end: e873b000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
+map: start: e873b000 end: e875b000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: e875b000 end: e875c000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e875c000 end: e875d000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: e875d000 end: e875e000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: e875e000 end: e875f000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e875f000 end: e877f000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
+map: start: e877f000 end: e879f000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: e879f000 end: e87a0000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a0000 end: e87a1000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87a1000 end: e87a2000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a2000 end: e87a3000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e87a3000 end: e87a4000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: e87a4000 end: e87a5000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e87a5000 end: e87a6000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_lob]
+map: start: e87a6000 end: e87a7000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e87a7000 end: e87a8000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a8000 end: e87a9000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87a9000 end: e87aa000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87aa000 end: e87ab000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87ab000 end: e87ac000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: e87ac000 end: e87ad000 offset: 0 load_bias: 0 flags: 0 name: [anon:thread signal stack guard page]
+map: start: e87ad000 end: e87af000 offset: 0 load_bias: 0 flags: 3 name: [anon:thread signal stack]
+map: start: e87af000 end: e87b0000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: e87b0000 end: e880d000 offset: 0 load_bias: 0 flags: 5 name: /system/bin/linker
+map: start: e880d000 end: e880f000 offset: 5c000 load_bias: 0 flags: 1 name: /system/bin/linker
+map: start: e880f000 end: e8810000 offset: 5e000 load_bias: 0 flags: 3 name: /system/bin/linker
+map: start: e8810000 end: e8812000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: e8812000 end: e8813000 offset: 0 load_bias: 0 flags: 1 name:
+map: start: e8813000 end: e8815000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: ff886000 end: ff8a9000 offset: 0 load_bias: 0 flags: 3 name: [stack]
+map: start: ffff0000 end: ffff1000 offset: 0 load_bias: 0 flags: 5 name: [vectors]
registers: 64 34868affdc8871e8150000001c0000001c000000150000000e00000007000000e08771e834868aff2354d2aa24f9ffffdc8871e88c8771e875b86ee778ba6ee7
stack: start: e8715000 end: e8719000 size: 16384 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc8871e87dba6ee734868affdc8871e8dc8871e85dba6ee7070000000e000000150000001c000000dc8871e85dba6ee71c000000150000000e00000007000000100000000c0000000800000004000000ddb86ee75dba6ee7dc8871e804000000080000000c00000010000000dc8871e85dba6ee7100000000c000000080000000400000008000000060000000400000002000000288871e835b96ee75dba6ee7dc8871e802000000040000000600000008000000dc8871e85dba6ee70800000006000000040000000200000004000000030000000200000001000000708871e88db96ee75dba6ee7dc8871e801000000020000000300000004000000dc8871e85dba6ee70400000003000000020000000100000004000000208971e8208971e878000000e87d00003dba6ee75dba6ee7dc8871e878000000c5807ce7fc7183e734868aff78868aff78868aff34868aff34868aff78868affe0879437208971e84154d2aa0020000034868aff34868aff34868aff78000000c9b87ee7b1b87ee7a3f47be7288971e8b1b87ee7208971e800000000f83481e800000000e97d0000e87d000000000000005071e82039000000100000000000000000000000000000000000002354d2aa34868aff00000000002071e801000000000000000000000000000000708971e8208971e8000000000000000000000000e0879437000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
function: start: 0 end: e76eb835 name: unknown_start
diff --git a/libbacktrace/testdata/arm/offline_testdata_for_libart b/libbacktrace/testdata/arm/offline_testdata_for_libart
index 63f6a07..03e1df5 100644
--- a/libbacktrace/testdata/arm/offline_testdata_for_libart
+++ b/libbacktrace/testdata/arm/offline_testdata_for_libart
@@ -1,10 +1,10 @@
pid: 32232 tid: 32233
registers: 64 000000000000000000000000000000006473602451b3e2e700000000d82fd1ff5600000000908eec00000000d42dd1ff00000000c02dd1ff617171e9617171e9
-map: start: e9380000 end: e9766000 offset: 0 load_base: b000 flags: 5 name: /system/lib/libart.so
+map: start: e9380000 end: e9766000 offset: 0 load_bias: b000 flags: 5 name: /system/lib/libart.so
stack: start: ffd12dc0 end: ffd16000 size: 12864 00000000000c5024070000000300000005070a0a0100000051b3e2e700000000d82fd1ff560000004c2ed1ff000000000000000081b771e9d82fd1ff000000004c2ed1ff0c2ed1ff40a8d27024bf76e900908eec000000000834d1ff0000000000000000000000000d000000050000000000000000000000080000000101d1ff44b8bfeb4b0000000000000000000000e8b8952400000000fc2ed1ff4fb3e2e7bc49ac6f00908eecb02ed1ffd82fd1ff040000008c908eec942fd1ffd5c141e9d82fd1ff4fb3e2e7542fd1ff336c68e940000000400000007030d1fff031d1ff00000000bc49ac6f5c30d1ff942fd1ff842fd1ffd82fd1ff00000000b8f1786f4fb3e2e7610d67e9d82fd1ff4fb3e2e77880adeb7980adeb7a80adeb7b80adeb7c80adeb7d80adeb7e80adeb7f80adeb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007430d1ff02000000e8b89524e8d895240200000000908eec5c30d1ffbc49ac6f4fb3e2e74030d1ffe8d8952400000000b8f1786fbc49ac6f332367e94fb3e2e701000000637171e9637171e9000000005c30d1ff8430d1ffe0c08bec882fd1ff4fb3e2e70200000004000000942fd1ffe8b8952400908eec58d8952458d895247fbd69e90500000000400fe40100000000908eec58d89524060000009c86bd6f6b876fe900908eece0c08bec00008eec0000000000000000000000000000000044b8bfeb4b000000009be86f040000000038d1ff01000000c8e7446f060000000000000000908eec30d89524e8b895249c86bd6f7893476f00908eec00000000358c6fe970400fe4116e71e9a0285a6fa4d49c6f4489bd6f30d8952458d89524e8d8952400908eeca431d1ff2c31d1ffb75861e90100000000908eec30528bec409181e958d8952431abed6fac33576fb438d1ff030000007800502400000000a0005024060000007893476f00908eec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004489bd6f78005024d00c5024a0005024a431d1ff2c31d1ff9b99aa71a4d49c6f30d8952400000000e8d895244489bd6fa8e5bc6fc8b895240100000000000000b033d1ff56000000637171e900000000d00c5024c8b89524000000000100000000000000b033d1ff560000006431d1ffa431d1ff000000009fb671e9b033d1ff00000000a431d1ff6431d1ffc431d1ff000000000000000081b771e9b033d1ff00000000c431d1ff8431d1ff01000000020000002429000001000000dc501b002033d1ff0100000018f9736f0100000000908eec58d8952440f180e9a8ec01245b215ce8a4d49c6f00908eec0832d1ffb033d1ff040000008c908eeca832d1ffabc141e9b033d1ff5b215ce82832d1ffb033d1ff080000008c908eec000000000035d1ff0834d1ffa832d1ffa4d49c6f04000000cca312e800908eec6832d1ffb033d1ff0834d1ff6bc354e9b033d1ff5b215ce8cca312e800908eec8832d1ffb033d1ff0834d1ff6bc354e900908eeca4d49c6f44b8bfeb1833d1ff000000006832d1ffb033d1ff478054e9b033d1ff1b8054e90834d1ffa4d49c6f0000000000000000000000000000000008000000000000000834d1ff0000000000000000000000000000000000000000000000000000000058d895240000000000000000000000000000000000000000000000000000000058d89524b17e54e98c56af6f00000000a4d49c6f288944e800908eec00000000d032d1ff0000000000000000000000000000000000000000000000007e8ea6c358a58cec00f580e90834d1ffa4d49c6f58d8952400908eecb033d1ffe9100000da8844e8833c70e9e9100000b033d1ff0200000058d8952408b1796f0200000000908eecda8844e82c34d1ff00908eece9100000005d70e9070000007d1300006034d1ff98d170e9b033d1ff0834d1ff148844e800908eecb033d1ffa034d1ffa833d1ff0100000044b8bfeb41f252e9e91fdeeaa491deea000000004700000001000000d9c4ddea0000000000000000b834d1ff00b051ff0834d1ff00908eecf833d1ffa034d1ff148844e800000000020000004d4c53e900000000000000000000000000908eec44b8bfeb0834d1ff3835d1ff148844e85035d1ffbb936fe90000000044b8bfebb033d1ffda8844e8148844e8000000000d0000005a0000007d137d13d00c502400000000600480240400000070048024f80c5024170000000100000002000000000000000040000000000000d0018024d00c502400000000600480240000000070048024f80c5024000000000000000000000000000000000000000000000000d001802465906fe97b2e5ce8000000000300000000000000881388131a00000001000000000000004cdd76e9010000007b2e5ce8020000009835d1ff5835d1ffc435d1ff010000000000000000000000010000000000000000dd76e90834d1ff0d0000000100000000000000000000005035d1ff9036d1ff00000000a435d1ff7e8ea6c3080000000000000000000000000000000000000038cb7e7044b8bfeb7d2e5ce800000000c037d1ff5600000000908eec00000000cc35d1ff55af71e9e0285a6f040000000800000001000000a437d1ff010000001c73d870000000000000000043000000339768e9040000006c36d1ff0e000000b436d1ff8cc97e706c36d1ff0e00000018eb01243173d870040000007d2e5ce800000000c037d1ff5600000000000000cc35d1ff637171e90000000018eb012402000000010000007d2e5ce800000000c037d1ff560000004436d1ff000000000000000081b771e9c037d1ff000000004436d1ff0436d1ff00e68dec0800000001000000a437d1ff010000001c73d870000000000000000043000000339768e9040000006c36d1ff0e000000b436d1ff8cc97e706c36d1ff0e000000adf861e918eb01243173d870040000007b2e5ce844b8bfeb00908eeca836d1ffc037d1ff040000008c908eec7c37d1ffd5c141e9c037d1ff7b2e5ce80000000000908eecd036d1ff00000000b038d1ff183ad1ff0000000044b8bfeb1038d1ff7c37d1ff6c37d1ffc037d1ff7b2e5ce8000000007b2e5ce8610d67e9c037d1ff7b2e5ce8280477e99835456f960300009a35456f10aa5e6f9a35456f9835456f68b85e6f881e77e9b30a47e9e81382e94c95b4ec7100000000908eec9c908eec30528bec1038d1ff7b2e5ce800000000c78469e91038d1ff0aeb3b52208989ec150645e9010000001038d1ff6c37d1ff44b8bfeb6c37d1ff00000000d837d1ff1038d1ff7b2e5ce8000000006c38d1ff8f0b67e97b2e5ce818eb012400000000000000000838d1ff7b2e5ce802000000040000007c37d1ff18eb01249835456f00000000901e77e9180000000300000000908eec480000004800000043000000640477e97669747954687265070000001a00000060eb0124000000000000000000000000a500000044b8bfeb1038d1ff00908eeceeac73e943000000640477e9901e77e9e6ac73e961705ce96c38d1ff18eb012400908eeceeac73e943000000640477e9000059008bc95ce900908eec30528bec409181e900908eec430000005900000000528bec409181e900004300710000000300000030528bec89c75ce944b8bfebe2050000103dd1ff03000000a3f6a7eb89c75ce96c38d1ff7e8ea6c389c75ce997f5a7eb710000000000000030528bec7e8ea6c3e83cd1ff2079002488beba6ff0ca726f5600000000908eec000000005439d1ff8b1db8aa803a89ec7e8ea6c3000000009173d870ec55af6f00000000010000004892796f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003801802488beba6ff0ca726f56000000000000005439d1ff9d3daa71cc55af6f7039d1ff0b0000006839d1ff7d2e5ce800000000483bd1ff637171e900000000207900240b00000058a58cec40f180e9010000007d2e5ce800000000483bd1ff56000000cc39d1ff000000000000000081b771e9483bd1ff00000000cc39d1ff8c39d1ff05000000050000000c000000040000000100000012000000958c00000100000074d73500483bd1ff01000000e880746f0100000000908eec903ad1ff40f180e97e02000000908eec2079002400000000383ad1ff7b2e5ce8cc55af6f00908eec303ad1ff483bd1ff040000008c908eec043bd1ffd5c141e9483bd1ff7b2e5ce840f180e900908eec583ad1ff00000000000000000000000000000000cc55af6f983bd1ff043bd1fff43ad1ff483bd1ff7b2e5ce8000000007b2e5ce8610d67e9483bd1ff7b2e5ce8280477e94892796f860100004e92796f00a088ec4e92796f4892796f18a688ec881e77e9b30a47e978e388ec4c95b4ec2100000000908eec9c908eec30528bec983bd1ff7b2e5ce800000000c78469e9983bd1ff06b005fdf0298aec150645e901000000983bd1fff43ad1ffcc55af6ff43ad1ff00000000603bd1ff983bd1ff7b2e5ce800000000f43bd1ff8f0b67e97b2e5ce8e00864e80000000000000000903bd1ff7b2e5ce80200000004000000043bd1ff207900249c908eec04000000583bd1ff603bd1ff4892796f04000000ac3bd1ff01000000901e77e917885ee9010000004d5cb1eb485cb1eb00908eec4892796f00000000000000000000000000004300cc55af6f983bd1ff00908eeceeac73e943000000640477e9901e77e9e6ac73e961705ce9f43bd1ff55000000ac3bd1ffeeac73e943000000640477e900005900e3225ce900908eec30528bec409181e900908eec430000005900000000528bec409181e9000043005500000078e388ec2100000009215ce901000000ce3fb8aae83cd1ff40420f00a3f6a7eb09215ce9f43bd1ff7e8ea6c309215ce9ed0ea8eb2100000075270000003289ec0000000030528becef665c74db0e42e911ac58e99daf58e9103dd1ff010000007e8ea6c31b000000385cd1ff385cd1ff02000000103dd1ff0300000087e26deae43cd1ff0200000001000000a31eb8aa020000007c3cd1ff18ac89ec1dac89ec0f000000fc94b4ec7c3cd1ff18ac89ec7e8ea6c3e83cd1ff884dd1ff741ab8aaa81ab8aa000000000700000004000000e43cd1ff3b19b8aa000000000000000000000000000000000000000000000000884dd1ff0000000001000000844dd1ff7e8ea6c3f065b4ec00fd0000205db8aa308789ec010000000000000004000000b8e78aec18ac89ec005db8aa2ceab2eb101082e935000000000000000800000001100000ba5bd1ff99000000b8e78aec205db8aa508789ec030000000000000004000000e2050000108789ec00000000d991aeece583aeec10d0acec10d0acec50d0acec6170705f70726f63657373333200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080a4ec00000000001000009dfe6feb00000000975673eb000000002516b8aa844dd1ff08000000a84dd1ff0000000000000000000000007c4dd1ff3b996feb00000000000000000000000000000000000000005015b8aad45cb8aadc5cb8aae85cb8aa804dd1ff0000000015b8aeec08000000ba5bd1ffd45bd1ffe05bd1ffee5bd1ff0f5cd1ff335cd1ff355cd1ff385cd1ff00000000535cd1ff6f5cd1ff825cd1ff9d5cd1ffbf5cd1ffd45cd1ffee5cd1ff015dd1ff1c5dd1ffe35ed1fffc5ed1ff465fd1ffc55fd1ff0000000010000000d6b0270006000000001000001100000064000000030000003400b8aa040000002000000005000000090000000700000000d0adec080000000000000009000000ec14b8aa0b000000752700000c000000752700000d000000752700000e000000752700001700000000000000190000007c4ed1ff1a0000001f0000001f000000de5fd1ff0f0000008c4ed1ff00000000000000000000000086da76325883c1a6b44d586d68c7843576386c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000636f6d2e6578616d706c652e7375646f67616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f3d2f73797374656d2f62696e2f6170705f70726f63657373333200414e44524f49445f444154413d2f6461746100444f574e4c4f41445f43414348453d2f646174612f636163686500414e44524f49445f534f434b45545f7a79676f74655f7365636f6e646172793d3900414e44524f49445f524f4f543d2f73797374656d00415345435f4d4f554e54504f494e543d2f6d6e742f6173656300414e44524f49445f424f4f544c4f474f3d3100414e44524f49445f4153534554533d2f73797374656d2f61707000424f4f54434c415353504154483d2f73797374656d2f6672616d65776f726b2f636f72652d6f6a2e6a61723a2f73797374656d2f6672616d65776f726b2f636f72652d6c69626172742e6a61723a2f73797374656d2f6672616d65776f726b2f636f6e7363727970742e6a61723a2f73797374656d2f6672616d65776f726b2f6f6b687474702e6a61723a2f73797374656d2f6672616d65776f726b2f6c65676163792d746573742e6a61723a2f73797374656d2f6672616d65776f726b2f626f756e6379636173746c652e6a61723a2f73797374656d2f6672616d65776f726b2f6578742e6a61723a2f73797374656d2f6672616d65776f726b2f6672616d65776f726b2e6a61723a2f73797374656d2f6672616d65776f726b2f74656c6570686f6e792d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f766f69702d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f696d732d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f6170616368652d786d6c2e6a61723a2f73797374656d2f6672616d65776f726b2f6f72672e6170616368652e687474702e6c65676163792e626f6f742e6a617200414e44524f49445f53544f524147453d2f73746f7261676500504154483d2f7362696e3a2f73797374656d2f7362696e3a2f73797374656d2f62696e3a2f73797374656d2f7862696e3a2f76656e646f722f62696e3a2f76656e646f722f7862696e0053595354454d534552564552434c415353504154483d2f73797374656d2f6672616d65776f726b2f73657276696365732e6a61723a2f73797374656d2f6672616d65776f726b2f65746865726e65742d736572766963652e6a61723a2f73797374656d2f6672616d65776f726b2f776966692d736572766963652e6a61720045585445524e414c5f53544f524147453d2f736463617264002f73797374656d2f62696e2f6170705f70726f636573733332000000000000000000
function: start: 3a2121 end: 3a217a name: art_quick_invoke_stub_internal
function: start: 3a66a5 end: 3a6787 name: art_quick_invoke_static_stub
function: start: a7129 end: a72f1 name: art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)
function: start: 2fbd35 end: 2fc789 name: art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ArgArray*, art::JValue*, char const*)
function: start: 2fcf75 end: 2fd88d name: art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned int)
-function: start: 2a089d end: 2a08bb name: art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobject*)
\ No newline at end of file
+function: start: 2a089d end: 2a08bb name: art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobject*)
diff --git a/libbacktrace/testdata/aarch64/libbacktrace_test_eh_frame.so b/libbacktrace/testdata/arm64/libbacktrace_test_eh_frame.so
similarity index 100%
rename from libbacktrace/testdata/aarch64/libbacktrace_test_eh_frame.so
rename to libbacktrace/testdata/arm64/libbacktrace_test_eh_frame.so
Binary files differ
diff --git a/libbacktrace/testdata/aarch64/offline_testdata b/libbacktrace/testdata/arm64/offline_testdata
similarity index 88%
rename from libbacktrace/testdata/aarch64/offline_testdata
rename to libbacktrace/testdata/arm64/offline_testdata
index ba44e09..75a2f12 100644
--- a/libbacktrace/testdata/aarch64/offline_testdata
+++ b/libbacktrace/testdata/arm64/offline_testdata
@@ -1,100 +1,100 @@
pid: 32438 tid: 32439
-map: start: 557066e000 end: 55706ee000 offset: 0 load_base: 0 flags: 5 name: /data/backtrace_test64
-map: start: 55706ef000 end: 55706f2000 offset: 80000 load_base: 0 flags: 1 name: /data/backtrace_test64
-map: start: 55706f2000 end: 55706f3000 offset: 83000 load_base: 0 flags: 3 name: /data/backtrace_test64
-map: start: 7014200000 end: 7014600000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
-map: start: 701464c000 end: 701465c000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libcutils.so
-map: start: 701465c000 end: 701465d000 offset: 0 load_base: 0 flags: 0 name:
-map: start: 701465d000 end: 701465e000 offset: 10000 load_base: 0 flags: 1 name: /system/lib64/libcutils.so
-map: start: 701465e000 end: 701465f000 offset: 11000 load_base: 0 flags: 3 name: /system/lib64/libcutils.so
-map: start: 7014691000 end: 70146b5000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/liblzma.so
-map: start: 70146b5000 end: 70146b6000 offset: 23000 load_base: 0 flags: 1 name: /system/lib64/liblzma.so
-map: start: 70146b6000 end: 70146b7000 offset: 24000 load_base: 0 flags: 3 name: /system/lib64/liblzma.so
-map: start: 70146b7000 end: 70146bc000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 70146c9000 end: 70158b5000 offset: 0 load_base: af000 flags: 5 name: /system/lib64/libLLVM.so
-map: start: 70158b5000 end: 701596b000 offset: 11eb000 load_base: 0 flags: 1 name: /system/lib64/libLLVM.so
-map: start: 701596b000 end: 701596c000 offset: 12a1000 load_base: 0 flags: 3 name: /system/lib64/libLLVM.so
-map: start: 701596c000 end: 701599f000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 70159c2000 end: 70159f9000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libm.so
-map: start: 70159f9000 end: 70159fa000 offset: 36000 load_base: 0 flags: 1 name: /system/lib64/libm.so
-map: start: 70159fa000 end: 70159fb000 offset: 37000 load_base: 0 flags: 3 name: /system/lib64/libm.so
-map: start: 7015a1e000 end: 7015a2e000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libbacktrace.so
-map: start: 7015a2e000 end: 7015a2f000 offset: f000 load_base: 0 flags: 1 name: /system/lib64/libbacktrace.so
-map: start: 7015a2f000 end: 7015a30000 offset: 10000 load_base: 0 flags: 3 name: /system/lib64/libbacktrace.so
-map: start: 7015a5e000 end: 7015a7d000 offset: 0 load_base: 1000 flags: 5 name: /system/lib64/libutils.so
-map: start: 7015a7d000 end: 7015a7e000 offset: 0 load_base: 0 flags: 0 name:
-map: start: 7015a7e000 end: 7015a7f000 offset: 1f000 load_base: 0 flags: 1 name: /system/lib64/libutils.so
-map: start: 7015a7f000 end: 7015a80000 offset: 20000 load_base: 0 flags: 3 name: /system/lib64/libutils.so
-map: start: 7015a99000 end: 7015b6d000 offset: 0 load_base: 9000 flags: 5 name: /system/lib64/libc++.so
-map: start: 7015b6d000 end: 7015b6e000 offset: 0 load_base: 0 flags: 0 name:
-map: start: 7015b6e000 end: 7015b76000 offset: d4000 load_base: 0 flags: 1 name: /system/lib64/libc++.so
-map: start: 7015b76000 end: 7015b77000 offset: dc000 load_base: 0 flags: 3 name: /system/lib64/libc++.so
-map: start: 7015b77000 end: 7015b7a000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 7015b81000 end: 7015b92000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/liblog.so
-map: start: 7015b92000 end: 7015b93000 offset: 10000 load_base: 0 flags: 1 name: /system/lib64/liblog.so
-map: start: 7015b93000 end: 7015b94000 offset: 11000 load_base: 0 flags: 3 name: /system/lib64/liblog.so
-map: start: 7015be3000 end: 7015ca3000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libc.so
-map: start: 7015ca3000 end: 7015ca9000 offset: bf000 load_base: 0 flags: 1 name: /system/lib64/libc.so
-map: start: 7015ca9000 end: 7015cab000 offset: c5000 load_base: 0 flags: 3 name: /system/lib64/libc.so
-map: start: 7015cab000 end: 7015cac000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 7015cac000 end: 7015cad000 offset: 0 load_base: 0 flags: 1 name: [anon:.bss]
-map: start: 7015cad000 end: 7015cb4000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 7015cf5000 end: 7015cf6000 offset: 0 load_base: 0 flags: 5 name: /data/libbacktrace_test.so
-map: start: 7015cf6000 end: 7015cf7000 offset: 0 load_base: 0 flags: 1 name: /data/libbacktrace_test.so
-map: start: 7015cf7000 end: 7015cf8000 offset: 1000 load_base: 0 flags: 3 name: /data/libbacktrace_test.so
-map: start: 7015d1f000 end: 7015d39000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libunwind.so
-map: start: 7015d39000 end: 7015d3a000 offset: 19000 load_base: 0 flags: 1 name: /system/lib64/libunwind.so
-map: start: 7015d3a000 end: 7015d3b000 offset: 1a000 load_base: 0 flags: 3 name: /system/lib64/libunwind.so
-map: start: 7015d3b000 end: 7015da4000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 7015de8000 end: 7015df7000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libbase.so
-map: start: 7015df7000 end: 7015df8000 offset: 0 load_base: 0 flags: 0 name:
-map: start: 7015df8000 end: 7015df9000 offset: f000 load_base: 0 flags: 1 name: /system/lib64/libbase.so
-map: start: 7015df9000 end: 7015dfa000 offset: 10000 load_base: 0 flags: 3 name: /system/lib64/libbase.so
-map: start: 7015e35000 end: 7015e36000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
-map: start: 7015e4f000 end: 7015e50000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7015f11000 end: 7015f13000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libnetd_client.so
-map: start: 7015f13000 end: 7015f14000 offset: 1000 load_base: 0 flags: 1 name: /system/lib64/libnetd_client.so
-map: start: 7015f14000 end: 7015f15000 offset: 2000 load_base: 0 flags: 3 name: /system/lib64/libnetd_client.so
-map: start: 7015f6c000 end: 7015f79000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7015f79000 end: 7015f99000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
-map: start: 7015f99000 end: 7015f9a000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7015f9a000 end: 7015f9b000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7015fa6000 end: 7015fa7000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7015fa8000 end: 7015fa9000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: 7015faf000 end: 7015fcf000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
-map: start: 7015fcf000 end: 7015fd0000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7015fd0000 end: 7015fd1000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7015fd1000 end: 7015fd2000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7015fd4000 end: 7015fd7000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7015fd7000 end: 7015fdb000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
-map: start: 7015fdb000 end: 7015fdc000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
-map: start: 7015fdc000 end: 7015fdd000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7015fdd000 end: 7015fde000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: 7015fde000 end: 7015ffe000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
-map: start: 7015ffe000 end: 701601e000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
-map: start: 701601e000 end: 701601f000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 701601f000 end: 7016020000 offset: 0 load_base: 0 flags: 0 name:
-map: start: 7016020000 end: 7016021000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7016021000 end: 7016022000 offset: 0 load_base: 0 flags: 0 name:
-map: start: 7016022000 end: 7016023000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_lob]
-map: start: 7016023000 end: 7016025000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: 7016025000 end: 7016026000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7016026000 end: 7016027000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7016027000 end: 7016028000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7016028000 end: 7016029000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
-map: start: 7016029000 end: 701602a000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 701602a000 end: 701602b000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
-map: start: 701602b000 end: 701602c000 offset: 0 load_base: 0 flags: 0 name: [anon:thread signal stack guard page]
-map: start: 701602c000 end: 7016030000 offset: 0 load_base: 0 flags: 3 name: [anon:thread signal stack]
-map: start: 7016030000 end: 7016031000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
-map: start: 7016031000 end: 7016033000 offset: 0 load_base: 0 flags: 5 name: [vdso]
-map: start: 7016033000 end: 70160dd000 offset: 0 load_base: 0 flags: 5 name: /system/bin/linker64
-map: start: 70160dd000 end: 70160e0000 offset: a9000 load_base: 0 flags: 1 name: /system/bin/linker64
-map: start: 70160e0000 end: 70160e1000 offset: ac000 load_base: 0 flags: 3 name: /system/bin/linker64
-map: start: 70160e1000 end: 70160e4000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 70160e4000 end: 70160e5000 offset: 0 load_base: 0 flags: 1 name:
-map: start: 70160e5000 end: 70160e8000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7fd8baf000 end: 7fd8be6000 offset: 0 load_base: 0 flags: 3 name: [stack]
+map: start: 557066e000 end: 55706ee000 offset: 0 load_bias: 0 flags: 5 name: /data/backtrace_test64
+map: start: 55706ef000 end: 55706f2000 offset: 80000 load_bias: 0 flags: 1 name: /data/backtrace_test64
+map: start: 55706f2000 end: 55706f3000 offset: 83000 load_bias: 0 flags: 3 name: /data/backtrace_test64
+map: start: 7014200000 end: 7014600000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: 701464c000 end: 701465c000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libcutils.so
+map: start: 701465c000 end: 701465d000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: 701465d000 end: 701465e000 offset: 10000 load_bias: 0 flags: 1 name: /system/lib64/libcutils.so
+map: start: 701465e000 end: 701465f000 offset: 11000 load_bias: 0 flags: 3 name: /system/lib64/libcutils.so
+map: start: 7014691000 end: 70146b5000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/liblzma.so
+map: start: 70146b5000 end: 70146b6000 offset: 23000 load_bias: 0 flags: 1 name: /system/lib64/liblzma.so
+map: start: 70146b6000 end: 70146b7000 offset: 24000 load_bias: 0 flags: 3 name: /system/lib64/liblzma.so
+map: start: 70146b7000 end: 70146bc000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 70146c9000 end: 70158b5000 offset: 0 load_bias: af000 flags: 5 name: /system/lib64/libLLVM.so
+map: start: 70158b5000 end: 701596b000 offset: 11eb000 load_bias: 0 flags: 1 name: /system/lib64/libLLVM.so
+map: start: 701596b000 end: 701596c000 offset: 12a1000 load_bias: 0 flags: 3 name: /system/lib64/libLLVM.so
+map: start: 701596c000 end: 701599f000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 70159c2000 end: 70159f9000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libm.so
+map: start: 70159f9000 end: 70159fa000 offset: 36000 load_bias: 0 flags: 1 name: /system/lib64/libm.so
+map: start: 70159fa000 end: 70159fb000 offset: 37000 load_bias: 0 flags: 3 name: /system/lib64/libm.so
+map: start: 7015a1e000 end: 7015a2e000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libbacktrace.so
+map: start: 7015a2e000 end: 7015a2f000 offset: f000 load_bias: 0 flags: 1 name: /system/lib64/libbacktrace.so
+map: start: 7015a2f000 end: 7015a30000 offset: 10000 load_bias: 0 flags: 3 name: /system/lib64/libbacktrace.so
+map: start: 7015a5e000 end: 7015a7d000 offset: 0 load_bias: 1000 flags: 5 name: /system/lib64/libutils.so
+map: start: 7015a7d000 end: 7015a7e000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: 7015a7e000 end: 7015a7f000 offset: 1f000 load_bias: 0 flags: 1 name: /system/lib64/libutils.so
+map: start: 7015a7f000 end: 7015a80000 offset: 20000 load_bias: 0 flags: 3 name: /system/lib64/libutils.so
+map: start: 7015a99000 end: 7015b6d000 offset: 0 load_bias: 9000 flags: 5 name: /system/lib64/libc++.so
+map: start: 7015b6d000 end: 7015b6e000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: 7015b6e000 end: 7015b76000 offset: d4000 load_bias: 0 flags: 1 name: /system/lib64/libc++.so
+map: start: 7015b76000 end: 7015b77000 offset: dc000 load_bias: 0 flags: 3 name: /system/lib64/libc++.so
+map: start: 7015b77000 end: 7015b7a000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015b81000 end: 7015b92000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/liblog.so
+map: start: 7015b92000 end: 7015b93000 offset: 10000 load_bias: 0 flags: 1 name: /system/lib64/liblog.so
+map: start: 7015b93000 end: 7015b94000 offset: 11000 load_bias: 0 flags: 3 name: /system/lib64/liblog.so
+map: start: 7015be3000 end: 7015ca3000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libc.so
+map: start: 7015ca3000 end: 7015ca9000 offset: bf000 load_bias: 0 flags: 1 name: /system/lib64/libc.so
+map: start: 7015ca9000 end: 7015cab000 offset: c5000 load_bias: 0 flags: 3 name: /system/lib64/libc.so
+map: start: 7015cab000 end: 7015cac000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015cac000 end: 7015cad000 offset: 0 load_bias: 0 flags: 1 name: [anon:.bss]
+map: start: 7015cad000 end: 7015cb4000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015cf5000 end: 7015cf6000 offset: 0 load_bias: 0 flags: 5 name: /data/libbacktrace_test.so
+map: start: 7015cf6000 end: 7015cf7000 offset: 0 load_bias: 0 flags: 1 name: /data/libbacktrace_test.so
+map: start: 7015cf7000 end: 7015cf8000 offset: 1000 load_bias: 0 flags: 3 name: /data/libbacktrace_test.so
+map: start: 7015d1f000 end: 7015d39000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libunwind.so
+map: start: 7015d39000 end: 7015d3a000 offset: 19000 load_bias: 0 flags: 1 name: /system/lib64/libunwind.so
+map: start: 7015d3a000 end: 7015d3b000 offset: 1a000 load_bias: 0 flags: 3 name: /system/lib64/libunwind.so
+map: start: 7015d3b000 end: 7015da4000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015de8000 end: 7015df7000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libbase.so
+map: start: 7015df7000 end: 7015df8000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: 7015df8000 end: 7015df9000 offset: f000 load_bias: 0 flags: 1 name: /system/lib64/libbase.so
+map: start: 7015df9000 end: 7015dfa000 offset: 10000 load_bias: 0 flags: 3 name: /system/lib64/libbase.so
+map: start: 7015e35000 end: 7015e36000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: 7015e4f000 end: 7015e50000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015f11000 end: 7015f13000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libnetd_client.so
+map: start: 7015f13000 end: 7015f14000 offset: 1000 load_bias: 0 flags: 1 name: /system/lib64/libnetd_client.so
+map: start: 7015f14000 end: 7015f15000 offset: 2000 load_bias: 0 flags: 3 name: /system/lib64/libnetd_client.so
+map: start: 7015f6c000 end: 7015f79000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7015f79000 end: 7015f99000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
+map: start: 7015f99000 end: 7015f9a000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015f9a000 end: 7015f9b000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015fa6000 end: 7015fa7000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fa8000 end: 7015fa9000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7015faf000 end: 7015fcf000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: 7015fcf000 end: 7015fd0000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fd0000 end: 7015fd1000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015fd1000 end: 7015fd2000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fd4000 end: 7015fd7000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7015fd7000 end: 7015fdb000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: 7015fdb000 end: 7015fdc000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: 7015fdc000 end: 7015fdd000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7015fdd000 end: 7015fde000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7015fde000 end: 7015ffe000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
+map: start: 7015ffe000 end: 701601e000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: 701601e000 end: 701601f000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 701601f000 end: 7016020000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: 7016020000 end: 7016021000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7016021000 end: 7016022000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: 7016022000 end: 7016023000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_lob]
+map: start: 7016023000 end: 7016025000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7016025000 end: 7016026000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7016026000 end: 7016027000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7016027000 end: 7016028000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7016028000 end: 7016029000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: 7016029000 end: 701602a000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 701602a000 end: 701602b000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: 701602b000 end: 701602c000 offset: 0 load_bias: 0 flags: 0 name: [anon:thread signal stack guard page]
+map: start: 701602c000 end: 7016030000 offset: 0 load_bias: 0 flags: 3 name: [anon:thread signal stack]
+map: start: 7016030000 end: 7016031000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: 7016031000 end: 7016033000 offset: 0 load_bias: 0 flags: 5 name: [vdso]
+map: start: 7016033000 end: 70160dd000 offset: 0 load_bias: 0 flags: 5 name: /system/bin/linker64
+map: start: 70160dd000 end: 70160e0000 offset: a9000 load_bias: 0 flags: 1 name: /system/bin/linker64
+map: start: 70160e0000 end: 70160e1000 offset: ac000 load_bias: 0 flags: 3 name: /system/bin/linker64
+map: start: 70160e1000 end: 70160e4000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 70160e4000 end: 70160e5000 offset: 0 load_bias: 0 flags: 1 name:
+map: start: 70160e5000 end: 70160e8000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd8baf000 end: 7fd8be6000 offset: 0 load_bias: 0 flags: 3 name: [stack]
registers: 4560 679a0b1670000000f3eb6d705500000090f56e7055000000000000000000000000206f705500000014e0677055000000d038bed87f0000004cde67705500000041000000000000001900000000000000c00f241470000000d3aec914588f4bcd0400000000000000e493af157000000090f56e7055000000060000000000000023c00b1670000000b1d1fd15700000003039bed87f000000c898041670000000304cbed87f000000b8130e1670000000303cbed87f000000f838bed87f0000000e0000000000000015000000000000001c00000000000000ec59cf1570000000b863fd15700000005064fd15700000000000000000000000ec59cf15700000000200000000000000b863fd1570000000144abed87f0000006064fd15700000005064fd157000000000010000000000005826bed87f000000d86fcf15700000006057cf157000000000000000000000005064fd15700000005064fd15700000005064fd1570000000b67e00000000000040fd677055000000d064fd15700000000030fd157000000002000000000000000100000000000000fcb58a56000000000063fd15700000009857cf1570000000c062fd15700000001c5acf157000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000003003167000000001000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000033000000000000000300000000000000003303167000000008330316700000000000000006000000f8320316700000005839bed87f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000c84cbed87f000000e84cbed87f000000984dbed87f00000078170e167000000002fd0000000000001400000000000000ff8100000100000000000000000000000000000000000000000000000000000078145700000000000010000000000000902b000000000000bdd04058000000000000000000000000bdd04058000000000000000000000000ccb58a560000000042487408000000000000000000000000cc57041670000000004704167000000010c0fd157000000010c0fd157000000090c0fd15700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000010000000000000002f48bed87f000000d3aec914588f4bcd2045bed87f0000002045bed87f0000002f48bed87f00000001000000000000002f000080000000005045bed87f0000000045bed87f000000c0a0c315700000006045bed87f0000006045bed87f000000010000000000000001000000000000000344bed87f00000001000000100000009c3fbed87f0000009b3fbed87f0000000344bed87f0000009a3fbed87f0000000100000000000000953fbed87f0000004344bed80100000001000000010000002c48bed87f0000000444bed87f0000004344bed80100000000000000000000000100000000000000b03fbed87f000000000000000200000000000000000000000000000000000000d3aec914588f4bcd000000000100000000000000000000000100000000000000f03fbed87f000000000000000200000000000000000000000000000000000000d3aec914588f4bcd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a03fbed87f0000000000000000000000000000000000000000000000000000000000000000000000d3aec914588f4bcd08226f70550000000000000000000000000000000000000000000000000000001048bed87f0000008047bed87f0000006047bed87f000000e0ffffff80ffffff03000000000000000000000000000000891d6e7055000000d3aec914588f4bcd5047bed87f0000005047bed87f000000861d6e705500000003000000000000002f00008000000000f0a6ca15700000004047bed87f000000c0a0c31570000000891d6e7055000000d3aec914000000000100000000000000a047bed800000000f9006e70550000000100000000000000dc41bed87f000000db41bed87f0000004346bed87f000000da41bed87f0000000100000000000000d541bed87f00000001000000010000000100000001000000861d6e70550000004446bed87f0000002c42bed80300000000000000000000000100000000000000f041bed87f0000009b1e6e700100000000000000000000000000000000000000d3aec914588f4bcd8e1e6e70550000000d000000000000002f00008000000000f0a6ca15700000003048bed87f000000c0a0c31570000000661e6e700100000000000000000000002f000000000000001000000000000000e21e6e7055000000d3aec914588f4bcdb048bed87f000000b048bed87f000000e21e6e70550000002f000000000000002f00008000000000f0a6ca1570000000a048bed87f000000c0a0c315700000008e1e6e7055000000000000000000000022000000000000000000000000000000205827147000000022000000000000003c43bed87f0000003b43bed87f000000a347bed87f0000003a43bed87f00000001000000000000003543bed87f000000f048bed8010000000100000001000000dd1e6e7055000000a447bed87f0000008c43bed82f000000000000000000000001000000000000005043bed87f000000861d6e700300000000000000000000000000000000000000d3aec914588f4bcd721e6e7055000000f447bed87f000000981123141800000000000000000000000100000000000000a043bed87f000000861d6e70030000000000000000000000d042bed87f0000000000000000000000981123147000000098112314700000009811231470000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000504abed87f000000b049bed87f0000008049bed87f00000000000000000000004043bed87f0000000000000000000000991123147000000000000000000000008e1e6e70550000000d00000000000000a04abed87f000000000000000000000000000000000000000000000000000000504abed87f000000e049bed87f000000a049bed87f000000c8ffffff80ffffff591e6e70550000000d0000000000000098112314700000000000000000000000df1e6e7055000000010000000000000020582714700000002200000000000000f049bed87f000000c8ffffff80ffffff9811231470000000980023147000000098112314700000009811231470000000741e6e7055000000060000000000000009f12914700000000c00000000000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b149bed87f000000b149bed87f000000f049be317f000000f049bed87f000000f049bed87f000000b149bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000f149bed87f000000f049bed87f000000d3aec914588f4bcdfcb58a56000000006cbb687055000000160000000000000098112314700000009911231470000000991123147000000098112314700000009911231470000000fcb58a5600000000010000000000000000000000000000000100000000000000604a050000000000e845bed87f0000006048bed87f000000a046bed87f00000017000000000000002c48bed87f0000009046bed87f000000ac73c515700000009911231470000000d3aec914588f4bcd1048bed87f0000008047bed87f0000006047bed87f000000e8ffffff80ffffffffffffffffffffff99112314700000006148bed87f000000981123141500000008020000ffffffff6048bed87f000000160000000000000058112314700000001700000000000000a849bed87f0000000000000000000000284abed87f0000001700000000000000284abed87f000000284abed87f000000d249bed87f00000001000000000000009a112314700000001600000000000000284abed87f000000ffffffffffffffff284abed87f000000284abed87f000000284abed87f000000284abed87f0000001700000000000000ffffffffffffffff284abed87f000000284abed87f000000ff49bed87f000000284abed87f00000000000000000000000000000000000000d049bed87f000000d049bed87f000000004abed87f000000d049bed87f0000000100000000000000d049bed87f000000d049bed87f000000d049bed87f00000017000000000000000100000000000000ffffffffffffffff991123147000000001000000000000003448bed87f0000009911231470000000b0ca687055000000ffffffffffffffff010000000000000099112314700000007047bed87f000000d3aec914588f4bcdfcb58a56000000000100000000000000000000000000000050226f70550000000000000000000000b44b05000000000000102a1470000000861d6e70550000000300000000000000f0a6ca1570000000a047bed87f0000006c79c31570000000f048bed87f0000008048bed87f0000004048bed87f000000c8ffffff80ffffff0000000000000000d3aec914588f4bcdf849bed87f000000f0a6ca15700000000200000000000000085b0e1670000000e048bed87f0000002c6fc515700000009048bed87f000000d3aec914588f4bcd0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a89912314700000000000000000000000e9216f70550000008991231470000000e449bed87f0000008991231470000000000000000000000000000000000000008891231470000000899123147000000089912314700000008891231470000000170000000000000088912314700000008891231470000000d3aec914588f4bcd899123147000000016000000000000000000000000000000e9216f705500000088912314700000008891231470000000889123147000000088912314700000008891231470000000f0a6ca15700000000049bed87f0000006c79c31570000000889123147000000088912314700000008891231470000000889123147000000088912314700000008891231470000000889123147000000089912314700000008991231470000000085b0e1670000000404abed87f0000002c6fc51570000000c80a6f7055000000d3aec914588f4bcd0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a3b3325736d001b5b6d002c20776865726520002573203d202573000a526570650000000000000000000000000000000000000000000000008891231470000000000000000000000088912314700000008891231470000000889123147000000088912314700000000000000000000000889123147000000088912314700000008891231470000000470000000000000000502a1470000000d3aec914588f4bcdf05727147000000000b2221470000000604abed87f0000005c716c7055000000fcb58a56000000007c706c7055000000fcb58a5600000000ea4b050000000000
stack: start: 7015fd3000 end: 7015fd7000 size: 16384 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f838bed87f0000004038bed87f000000b863fd1570000000b863fd1570000000b863fd1570000000ec59cf15700000001c000000150000000e000000070000003063fd15700000001c58cf1570000000b863fd1570000000ec59cf1570000000100000000c00000008000000040000006063fd15700000007c58cf1570000000b863fd1570000000ec59cf1570000000080000000600000004000000020000009063fd1570000000dc58cf1570000000b863fd1570000000ec59cf157000000004000000030000000200000001000000d063fd1570000000c459cf15700000000100000000000000144abed87f0000004038bed87f0000004038bed87f000000144abed87f000000d3aec914588f4bcd1064fd157000000074fd6770550000004038bed87f0000004038bed87f000000ec84c41570000000e484c41570000000c484c4157000000000000000000000004064fd15700000004015c01570000000b67e0000000000000000000000000000705a0e1670000000185b0e167000000000000000000000000000000000000000705a0e16700000000000000000000000b77e0000b67e000000000000550000000030fd157000000050340000000000000010000000000000000000000000000000b222147000000000102a14700000000000000000000000000000000000000040fd6770550000004038bed87f000000000000000000000000a0fa1570000000010000000000000000000000000000000000000000000000e864fd15700000005064fd1570000000000000000000000000000000000000000000000000000000d3aec914588f4bcd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
function: start: 0 end: 7015cf5760 name: unknown_start
diff --git a/libbacktrace/testdata/x86/offline_testdata b/libbacktrace/testdata/x86/offline_testdata
index 3e4e06c..e8b7a99 100644
--- a/libbacktrace/testdata/x86/offline_testdata
+++ b/libbacktrace/testdata/x86/offline_testdata
@@ -1,75 +1,75 @@
pid: 34545 tid: 34546
-map: start: f705a000 end: f705c000 offset: 0 load_base: 0 flags: 3 name:
-map: start: f705c000 end: f707f000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
-map: start: f707f000 end: f7080000 offset: 22000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
-map: start: f7080000 end: f7081000 offset: 23000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
-map: start: f7081000 end: f7088000 offset: 0 load_base: 0 flags: 3 name:
-map: start: f7088000 end: f7230000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libc-2.19.so
-map: start: f7230000 end: f7231000 offset: 1a8000 load_base: 0 flags: 0 name: /lib/i386-linux-gnu/libc-2.19.so
-map: start: f7231000 end: f7233000 offset: 1a8000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libc-2.19.so
-map: start: f7233000 end: f7234000 offset: 1aa000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libc-2.19.so
-map: start: f7234000 end: f7237000 offset: 0 load_base: 0 flags: 3 name:
-map: start: f7237000 end: f727b000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libm-2.19.so
-map: start: f727b000 end: f727c000 offset: 43000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libm-2.19.so
-map: start: f727c000 end: f727d000 offset: 44000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libm-2.19.so
-map: start: f727d000 end: f7299000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libgcc_s.so.1
-map: start: f7299000 end: f729a000 offset: 1b000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libgcc_s.so.1
-map: start: f729a000 end: f72b8000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libtinfo.so.5.9
-map: start: f72b8000 end: f72b9000 offset: 1e000 load_base: 0 flags: 0 name: /lib/i386-linux-gnu/libtinfo.so.5.9
-map: start: f72b9000 end: f72bb000 offset: 1e000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libtinfo.so.5.9
-map: start: f72bb000 end: f72bc000 offset: 20000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libtinfo.so.5.9
-map: start: f72bc000 end: f72bd000 offset: 0 load_base: 0 flags: 3 name:
-map: start: f72bd000 end: f72e0000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libncurses.so.5.9
-map: start: f72e0000 end: f72e1000 offset: 22000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libncurses.so.5.9
-map: start: f72e1000 end: f72e2000 offset: 23000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libncurses.so.5.9
-map: start: f72e2000 end: f72e5000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libdl-2.19.so
-map: start: f72e5000 end: f72e6000 offset: 2000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libdl-2.19.so
-map: start: f72e6000 end: f72e7000 offset: 3000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libdl-2.19.so
-map: start: f72e7000 end: f72ee000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/librt-2.19.so
-map: start: f72ee000 end: f72ef000 offset: 6000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/librt-2.19.so
-map: start: f72ef000 end: f72f0000 offset: 7000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/librt-2.19.so
-map: start: f72f0000 end: f7308000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libpthread-2.19.so
-map: start: f7308000 end: f7309000 offset: 18000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libpthread-2.19.so
-map: start: f7309000 end: f730a000 offset: 19000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libpthread-2.19.so
-map: start: f730a000 end: f730c000 offset: 0 load_base: 0 flags: 3 name:
-map: start: f732f000 end: f7331000 offset: 0 load_base: 0 flags: 3 name:
-map: start: f7331000 end: f7425000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
-map: start: f7425000 end: f7426000 offset: f4000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
-map: start: f7426000 end: f742a000 offset: f4000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
-map: start: f742a000 end: f742b000 offset: f8000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
-map: start: f742b000 end: f742d000 offset: 0 load_base: 0 flags: 3 name:
-map: start: f742d000 end: f7446000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
-map: start: f7446000 end: f7447000 offset: 18000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
-map: start: f7447000 end: f7448000 offset: 19000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
-map: start: f7448000 end: f7457000 offset: 0 load_base: 0 flags: 3 name:
-map: start: f7457000 end: f745c000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
-map: start: f745c000 end: f745d000 offset: 4000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
-map: start: f745d000 end: f745e000 offset: 5000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
-map: start: f745e000 end: f7467000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
-map: start: f7467000 end: f7468000 offset: 9000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
-map: start: f7468000 end: f7469000 offset: 9000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
-map: start: f7469000 end: f746a000 offset: a000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
-map: start: f746a000 end: f7477000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
-map: start: f7477000 end: f7478000 offset: c000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
-map: start: f7478000 end: f7479000 offset: d000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
-map: start: f7479000 end: f7489000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
-map: start: f7489000 end: f748a000 offset: f000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
-map: start: f748a000 end: f748b000 offset: 10000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
-map: start: f748b000 end: f748c000 offset: 0 load_base: 0 flags: 3 name:
-map: start: f748c000 end: f748d000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
-map: start: f748d000 end: f748e000 offset: 0 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
-map: start: f748e000 end: f748f000 offset: 1000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
-map: start: f748f000 end: f7491000 offset: 0 load_base: 0 flags: 3 name:
-map: start: f7491000 end: f74b1000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/ld-2.19.so
-map: start: f74b1000 end: f74b2000 offset: 1f000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/ld-2.19.so
-map: start: f74b2000 end: f74b3000 offset: 20000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/ld-2.19.so
-map: start: f74b3000 end: f77c6000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
-map: start: f77c6000 end: f77c7000 offset: 0 load_base: ffffe000 flags: 5 name: [vdso]
-map: start: f77c7000 end: f77d4000 offset: 313000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
-map: start: f77d4000 end: f77d5000 offset: 320000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
-map: start: f77d5000 end: f77d6000 offset: 0 load_base: 0 flags: 3 name:
-map: start: f7ec6000 end: f7ee7000 offset: 0 load_base: 0 flags: 3 name: [heap]
-map: start: ffe4e000 end: ffe70000 offset: 0 load_base: 0 flags: 3 name: [stack]
+map: start: f705a000 end: f705c000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f705c000 end: f707f000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f707f000 end: f7080000 offset: 22000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f7080000 end: f7081000 offset: 23000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f7081000 end: f7088000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f7088000 end: f7230000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7230000 end: f7231000 offset: 1a8000 load_bias: 0 flags: 0 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7231000 end: f7233000 offset: 1a8000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7233000 end: f7234000 offset: 1aa000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7234000 end: f7237000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f7237000 end: f727b000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727b000 end: f727c000 offset: 43000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727c000 end: f727d000 offset: 44000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727d000 end: f7299000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libgcc_s.so.1
+map: start: f7299000 end: f729a000 offset: 1b000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libgcc_s.so.1
+map: start: f729a000 end: f72b8000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72b8000 end: f72b9000 offset: 1e000 load_bias: 0 flags: 0 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72b9000 end: f72bb000 offset: 1e000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72bb000 end: f72bc000 offset: 20000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72bc000 end: f72bd000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f72bd000 end: f72e0000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e0000 end: f72e1000 offset: 22000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e1000 end: f72e2000 offset: 23000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e2000 end: f72e5000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e5000 end: f72e6000 offset: 2000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e6000 end: f72e7000 offset: 3000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e7000 end: f72ee000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72ee000 end: f72ef000 offset: 6000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72ef000 end: f72f0000 offset: 7000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72f0000 end: f7308000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f7308000 end: f7309000 offset: 18000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f7309000 end: f730a000 offset: 19000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f730a000 end: f730c000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f732f000 end: f7331000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f7331000 end: f7425000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f7425000 end: f7426000 offset: f4000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f7426000 end: f742a000 offset: f4000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f742a000 end: f742b000 offset: f8000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f742b000 end: f742d000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f742d000 end: f7446000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7446000 end: f7447000 offset: 18000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7447000 end: f7448000 offset: 19000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7448000 end: f7457000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f7457000 end: f745c000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745c000 end: f745d000 offset: 4000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745d000 end: f745e000 offset: 5000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745e000 end: f7467000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7467000 end: f7468000 offset: 9000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7468000 end: f7469000 offset: 9000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7469000 end: f746a000 offset: a000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f746a000 end: f7477000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7477000 end: f7478000 offset: c000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7478000 end: f7479000 offset: d000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7479000 end: f7489000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f7489000 end: f748a000 offset: f000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f748a000 end: f748b000 offset: 10000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f748b000 end: f748c000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f748c000 end: f748d000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748d000 end: f748e000 offset: 0 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748e000 end: f748f000 offset: 1000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748f000 end: f7491000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f7491000 end: f74b1000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b1000 end: f74b2000 offset: 1f000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b2000 end: f74b3000 offset: 20000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b3000 end: f77c6000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77c6000 end: f77c7000 offset: 0 load_bias: ffffe000 flags: 5 name: [vdso]
+map: start: f77c7000 end: f77d4000 offset: 313000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77d4000 end: f77d5000 offset: 320000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77d5000 end: f77d6000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f7ec6000 end: f7ee7000 offset: 0 load_bias: 0 flags: 3 name: [heap]
+map: start: ffe4e000 end: ffe70000 offset: 0 load_bias: 0 flags: 3 name: [stack]
registers: 348 00000000abdae6fff83b7df704000000a6ec77f7abdae6ff00000000afdae6ff78dae6ff150000001c000000b8f132f7a0f132f7d0df48f7a0ca48f728d9e6ff000000008c8decf78c8decf7ceca48f7a8dae6ff8d8decf70a0000000000000014dae6ff8c8decf78c8decf78c8decf78c8decf78c8decf7a9dae6ff06000000c03a23f78c8decf78c8decf78c8decf78c8decf78c8decf78c8decf78c8decf78d8decf78d8decf7c03a23f7543b23f7000033f75f5f0ff7c03a23f7503423f77000000098000000020000000f2700006c0000000e00000080000000000000008c8decf7000000008c8decf77f03ffff0000ffffffffffff0000000000000000000000000000ffff040000000f27000008000000003023f78d8decf7f069ec000046bdaa308decf715537df7f83b7df78c8decf74c1c71f78c8decf715537df70000000000000000f069ecf7f83b7df75064ecf792e671f7f069ecf7
stack: start: f732c000 end: f7330000 size: 16384 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009d9d49f761b009f71e382ff7000000000000000000000000000000000000000002000000f6372ff704c82bf70000000000204bf7f0a908f7ceca48f728d9e6fff4a449f70000000020f332f720f332f7d0df48f7f8f132f794c748f720f332f70c144205978142a8d4be08f7d0df48f720f332f7a0ca48f71c000000150000000e000000070000001c000000a0ca48f7d0df48f748f232f739c848f7070000000e000000150000001c000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f7100000000c000000080000000400000010000000a0ca48f7d0df48f798f232f7c9c848f704000000080000000c00000010000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f70800000006000000040000000200000008000000a0ca48f7d0df48f7e8f232f759c948f702000000040000000600000008000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f7040000000300000002000000010000000046bdaa00000000d0df48f738f332f77cca48f701000000020000000300000004000000a0ca48f720f332f700000000f83b7df7696d4df7d0df48f788dae6ff28d9e6ff28d9e6ff88dae6ff58f332f70046bdaa40fb32f7f83b7df758f332f78d6d4df728d9e6ff88dae6fff83b7df728d9e6ff28d9e6ff009030f728f432f7726f2ff728d9e6ff40fb32f740fb32f740fb32f790f332f700000000000000000000000000000000000000000000000000000000009030f740fb32f7000f3d0028f432f703b12c75032f144e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a06e2ff700000000000f3d00000000008e3f17f740fb32f7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a03823f7c4fd32f700000000e0341df7e02e1df7e03d1df70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040fb32f7188eecf740fb32f70100000030647cf70046bdaa28658876000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060a705f7a4b130f7f2860000f1860000b0fb32f7ecffffff000000000000000090f332f7000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ccfb32f70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000c2638cfa7f3e1d0000000000000000000000000000000000406d4df728d9e6ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c032f7004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
function: start: 0 end: f748c740 name: unknown_start
diff --git a/libbacktrace/testdata/x86_64/offline_testdata b/libbacktrace/testdata/x86_64/offline_testdata
index baf6450..f8d0dc0 100644
--- a/libbacktrace/testdata/x86_64/offline_testdata
+++ b/libbacktrace/testdata/x86_64/offline_testdata
@@ -1,86 +1,86 @@
pid: 25683 tid: 25692
-map: start: 7fd5aa784000 end: 7fd5aa93e000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libc-2.19.so
-map: start: 7fd5aa93e000 end: 7fd5aab3e000 offset: 1ba000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libc-2.19.so
-map: start: 7fd5aab3e000 end: 7fd5aab42000 offset: 1ba000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libc-2.19.so
-map: start: 7fd5aab42000 end: 7fd5aab44000 offset: 1be000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libc-2.19.so
-map: start: 7fd5aab44000 end: 7fd5aab49000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7fd5aab49000 end: 7fd5aac4e000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libm-2.19.so
-map: start: 7fd5aac4e000 end: 7fd5aae4d000 offset: 105000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libm-2.19.so
-map: start: 7fd5aae4d000 end: 7fd5aae4e000 offset: 104000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libm-2.19.so
-map: start: 7fd5aae4e000 end: 7fd5aae4f000 offset: 105000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libm-2.19.so
-map: start: 7fd5aae4f000 end: 7fd5aae65000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
-map: start: 7fd5aae65000 end: 7fd5ab064000 offset: 16000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
-map: start: 7fd5ab064000 end: 7fd5ab065000 offset: 15000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
-map: start: 7fd5ab065000 end: 7fd5ab08a000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
-map: start: 7fd5ab08a000 end: 7fd5ab289000 offset: 25000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
-map: start: 7fd5ab289000 end: 7fd5ab28d000 offset: 24000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
-map: start: 7fd5ab28d000 end: 7fd5ab28e000 offset: 28000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
-map: start: 7fd5ab28e000 end: 7fd5ab2b0000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
-map: start: 7fd5ab2b0000 end: 7fd5ab4af000 offset: 22000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
-map: start: 7fd5ab4af000 end: 7fd5ab4b0000 offset: 21000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
-map: start: 7fd5ab4b0000 end: 7fd5ab4b1000 offset: 22000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
-map: start: 7fd5ab4b1000 end: 7fd5ab4b4000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libdl-2.19.so
-map: start: 7fd5ab4b4000 end: 7fd5ab6b3000 offset: 3000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libdl-2.19.so
-map: start: 7fd5ab6b3000 end: 7fd5ab6b4000 offset: 2000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libdl-2.19.so
-map: start: 7fd5ab6b4000 end: 7fd5ab6b5000 offset: 3000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libdl-2.19.so
-map: start: 7fd5ab6b5000 end: 7fd5ab6bc000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/librt-2.19.so
-map: start: 7fd5ab6bc000 end: 7fd5ab8bb000 offset: 7000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/librt-2.19.so
-map: start: 7fd5ab8bb000 end: 7fd5ab8bc000 offset: 6000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/librt-2.19.so
-map: start: 7fd5ab8bc000 end: 7fd5ab8bd000 offset: 7000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/librt-2.19.so
-map: start: 7fd5ab8bd000 end: 7fd5ab8d6000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
-map: start: 7fd5ab8d6000 end: 7fd5abad5000 offset: 19000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
-map: start: 7fd5abad5000 end: 7fd5abad6000 offset: 18000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
-map: start: 7fd5abad6000 end: 7fd5abad7000 offset: 19000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
-map: start: 7fd5abad7000 end: 7fd5abadb000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7fd5abadb000 end: 7fd5abafe000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/ld-2.19.so
-map: start: 7fd5abb17000 end: 7fd5abb1a000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7fd5abb1a000 end: 7fd5abb40000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
-map: start: 7fd5abb40000 end: 7fd5abb41000 offset: 25000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
-map: start: 7fd5abb41000 end: 7fd5abb42000 offset: 26000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
-map: start: 7fd5abb42000 end: 7fd5abb4b000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7fd5abb6a000 end: 7fd5abb70000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7fd5abb70000 end: 7fd5abc62000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
-map: start: 7fd5abc62000 end: 7fd5abc63000 offset: f2000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
-map: start: 7fd5abc63000 end: 7fd5abc6b000 offset: f2000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
-map: start: 7fd5abc6b000 end: 7fd5abc6c000 offset: fa000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
-map: start: 7fd5abc6c000 end: 7fd5abc70000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7fd5abc70000 end: 7fd5abc8d000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
-map: start: 7fd5abc8d000 end: 7fd5abc8e000 offset: 1c000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
-map: start: 7fd5abc8e000 end: 7fd5abc8f000 offset: 1d000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
-map: start: 7fd5abc8f000 end: 7fd5abcb8000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7fd5abcb8000 end: 7fd5abcbe000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
-map: start: 7fd5abcbe000 end: 7fd5abcbf000 offset: 6000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
-map: start: 7fd5abcbf000 end: 7fd5abcc0000 offset: 6000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
-map: start: 7fd5abcc0000 end: 7fd5abcc1000 offset: 7000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
-map: start: 7fd5abcc1000 end: 7fd5abcc2000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7fd5abcc2000 end: 7fd5abccd000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
-map: start: 7fd5abccd000 end: 7fd5abcce000 offset: b000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
-map: start: 7fd5abcce000 end: 7fd5abccf000 offset: b000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
-map: start: 7fd5abccf000 end: 7fd5abcd0000 offset: c000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
-map: start: 7fd5abcd0000 end: 7fd5abcdf000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
-map: start: 7fd5abcdf000 end: 7fd5abce0000 offset: f000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
-map: start: 7fd5abce0000 end: 7fd5abce1000 offset: f000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
-map: start: 7fd5abce1000 end: 7fd5abce2000 offset: 10000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
-map: start: 7fd5abce2000 end: 7fd5abcf5000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
-map: start: 7fd5abcf5000 end: 7fd5abcf6000 offset: 12000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
-map: start: 7fd5abcf6000 end: 7fd5abcf7000 offset: 13000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
-map: start: 7fd5abcf7000 end: 7fd5abcf8000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
-map: start: 7fd5abcf8000 end: 7fd5abcf9000 offset: 1000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
-map: start: 7fd5abcf9000 end: 7fd5abcfa000 offset: 1000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
-map: start: 7fd5abcfa000 end: 7fd5abcfb000 offset: 2000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
-map: start: 7fd5abcfb000 end: 7fd5abcfd000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7fd5abcfd000 end: 7fd5abcfe000 offset: 22000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/ld-2.19.so
-map: start: 7fd5abcfe000 end: 7fd5abcff000 offset: 23000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/ld-2.19.so
-map: start: 7fd5abcff000 end: 7fd5abd00000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7fd5abd00000 end: 7fd5ac053000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
-map: start: 7fd5ac053000 end: 7fd5ac054000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7fd5ac054000 end: 7fd5ac06f000 offset: 353000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
-map: start: 7fd5ac06f000 end: 7fd5ac070000 offset: 36e000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
-map: start: 7fd5ac070000 end: 7fd5ac071000 offset: 0 load_base: 0 flags: 3 name:
-map: start: 7fd5ad54e000 end: 7fd5ad56f000 offset: 0 load_base: 0 flags: 3 name: [heap]
-map: start: 7ffcf47ed000 end: 7ffcf480f000 offset: 0 load_base: 0 flags: 3 name: [stack]
-map: start: 7ffcf48d5000 end: 7ffcf48d7000 offset: 0 load_base: ffffffffff700000 flags: 5 name: [vdso]
-map: start: ffffffffff600000 end: ffffffffff601000 offset: 0 load_base: 0 flags: 5 name: [vsyscall]
+map: start: 7fd5aa784000 end: 7fd5aa93e000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aa93e000 end: 7fd5aab3e000 offset: 1ba000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab3e000 end: 7fd5aab42000 offset: 1ba000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab42000 end: 7fd5aab44000 offset: 1be000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab44000 end: 7fd5aab49000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5aab49000 end: 7fd5aac4e000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aac4e000 end: 7fd5aae4d000 offset: 105000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4d000 end: 7fd5aae4e000 offset: 104000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4e000 end: 7fd5aae4f000 offset: 105000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4f000 end: 7fd5aae65000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5aae65000 end: 7fd5ab064000 offset: 16000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5ab064000 end: 7fd5ab065000 offset: 15000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5ab065000 end: 7fd5ab08a000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab08a000 end: 7fd5ab289000 offset: 25000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab289000 end: 7fd5ab28d000 offset: 24000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab28d000 end: 7fd5ab28e000 offset: 28000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab28e000 end: 7fd5ab2b0000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab2b0000 end: 7fd5ab4af000 offset: 22000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4af000 end: 7fd5ab4b0000 offset: 21000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4b0000 end: 7fd5ab4b1000 offset: 22000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4b1000 end: 7fd5ab4b4000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab4b4000 end: 7fd5ab6b3000 offset: 3000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b3000 end: 7fd5ab6b4000 offset: 2000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b4000 end: 7fd5ab6b5000 offset: 3000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b5000 end: 7fd5ab6bc000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab6bc000 end: 7fd5ab8bb000 offset: 7000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bb000 end: 7fd5ab8bc000 offset: 6000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bc000 end: 7fd5ab8bd000 offset: 7000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bd000 end: 7fd5ab8d6000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5ab8d6000 end: 7fd5abad5000 offset: 19000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad5000 end: 7fd5abad6000 offset: 18000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad6000 end: 7fd5abad7000 offset: 19000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad7000 end: 7fd5abadb000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abadb000 end: 7fd5abafe000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abb17000 end: 7fd5abb1a000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abb1a000 end: 7fd5abb40000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb40000 end: 7fd5abb41000 offset: 25000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb41000 end: 7fd5abb42000 offset: 26000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb42000 end: 7fd5abb4b000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abb6a000 end: 7fd5abb70000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abb70000 end: 7fd5abc62000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc62000 end: 7fd5abc63000 offset: f2000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc63000 end: 7fd5abc6b000 offset: f2000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc6b000 end: 7fd5abc6c000 offset: fa000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc6c000 end: 7fd5abc70000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abc70000 end: 7fd5abc8d000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8d000 end: 7fd5abc8e000 offset: 1c000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8e000 end: 7fd5abc8f000 offset: 1d000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8f000 end: 7fd5abcb8000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abcb8000 end: 7fd5abcbe000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcbe000 end: 7fd5abcbf000 offset: 6000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcbf000 end: 7fd5abcc0000 offset: 6000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcc0000 end: 7fd5abcc1000 offset: 7000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcc1000 end: 7fd5abcc2000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abcc2000 end: 7fd5abccd000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abccd000 end: 7fd5abcce000 offset: b000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abcce000 end: 7fd5abccf000 offset: b000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abccf000 end: 7fd5abcd0000 offset: c000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abcd0000 end: 7fd5abcdf000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abcdf000 end: 7fd5abce0000 offset: f000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce0000 end: 7fd5abce1000 offset: f000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce1000 end: 7fd5abce2000 offset: 10000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce2000 end: 7fd5abcf5000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf5000 end: 7fd5abcf6000 offset: 12000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf6000 end: 7fd5abcf7000 offset: 13000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf7000 end: 7fd5abcf8000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcf8000 end: 7fd5abcf9000 offset: 1000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcf9000 end: 7fd5abcfa000 offset: 1000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcfa000 end: 7fd5abcfb000 offset: 2000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcfb000 end: 7fd5abcfd000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abcfd000 end: 7fd5abcfe000 offset: 22000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abcfe000 end: 7fd5abcff000 offset: 23000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abcff000 end: 7fd5abd00000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abd00000 end: 7fd5ac053000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac053000 end: 7fd5ac054000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5ac054000 end: 7fd5ac06f000 offset: 353000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac06f000 end: 7fd5ac070000 offset: 36e000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac070000 end: 7fd5ac071000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5ad54e000 end: 7fd5ad56f000 offset: 0 load_bias: 0 flags: 3 name: [heap]
+map: start: 7ffcf47ed000 end: 7ffcf480f000 offset: 0 load_bias: 0 flags: 3 name: [stack]
+map: start: 7ffcf48d5000 end: 7ffcf48d7000 offset: 0 load_bias: ffffffffff700000 flags: 5 name: [vdso]
+map: start: ffffffffff600000 end: ffffffffff601000 offset: 0 load_bias: 0 flags: 5 name: [vsyscall]
registers: 936 010000000000000028b480f4fc7f000028b480f4fc7f000028b480f4fc7f0000b92455add57f0000b07bcfabd57f000098deb6abd57f0000b82455add57f0000010000000000000000000000000000000000000000000000c0e354add57f000000e7b6abd57f0000c8b080f4fc7f00000e0000000000000080ddb6abd57f000000000000000000001500000000000000b07bcfabd57f00001c0000000000000060ddb6abd57f0000d07bcfabd57f0000a0b180f4fc7f000028b480f4fc7f000028b480f4fc7f000028b480f4fc7f000078b480f4fc7f000078b480f4fc7f00007f03ffff0000ffffffffffff000000000000000000000000801f0000fc7f000078b480f4fc7f000060b280f4fc7f000000006a80f3f73cf110b480f4fc7f0000de66d6abd57f00000000000000000000000000000000000000006a80f3f73cf128b480f4fc7f0000782455add57f0000fd96d6abd57f0000092555add57f0000000000000000000057b480f4fc7f0000092555add57f000060b480f4fc7f00006994d6abd57f0000b0e0c6abd57f000071b280f4fc7f000070b280f4fc7f0000256c640000000000a8b180f4fc7f000090b480f4fc7f0000082555add57f0000092555add57f0000092555add57f0000082555add57f00001700000000000000082555add57f0000082555add57f0000f93cfcabd57f0000092555add57f000016000000000000000000000000000000090c07acd57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f000010b380f4fc7f000078b480f4fc7f00000000000000000000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000092555add57f0000092555add57f0000b92455add57f000028b480f4fc7f0000c800000000000000014c7f0b0380ffff0d000000fc7f0000030000000000000033000000d57f000000b480f4fc7f00000000000000000000a0e154ad5b000000000000000000000000000000000000006e000000770000000000000000000000082555add57f00000000000000000000082555add57f0000082555add57f0000082555add57f0000082555add57f00000000000000000000082555add57f0000082555add57f0000082555add57f00006027b4aad57f0000c800000000000000a0e154add57f0000c0e354add57f0000092555add57f000000006a80f3f73cf1e0e054add57f0000e0e554add57f0000d14df6abd57f0000
stack: start: 7fd5abb6b000 end: 7fd5abb6f000 size: 16384 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006c4eaeabd57f00000000000000000000978142a8000000000f0000000000000012000000000000003888b4abd57f0000e657aeabd57f00000000000000000000a0dcb6abd57f0000307d78aad57f0000b0ddb6abd57f000028d978aad57f0000060aa10200000000a0ddb6abd57f0000000000000000000000000000000000002091b1abd57f0000e094b4abd57f000017008cabd57f0000c84d79aad57f000028e28babd57f00000000000005000000d503000001000000000000000000000068deb6abd57f000040deb6abd57f00002091b1abd57f00000100000000000000e0f3c6abd57f000088f0c6abd57f00006159aeabd57f000000000000000000002091b1abd57f000005000000000000000000000000000000010000000000000088f0c6abd57f00000000000000000000d07bcfabd57f00000000000000000000000000000000000098deb6abd57f000098deb6abd57f0000b0ddb6abd57f00006179cfabd57f000098deb6abd57f0000b07bcfabd57f00001c000000150000000e00000007000000f0ddb6abd57f0000e179cfabd57f00000000000000000000150000001c00000098deb6abd57f0000b07bcfabd57f0000100000000c000000080000000400000030deb6abd57f0000417acfabd57f000000000000000000000c0000001000000098deb6abd57f0000b07bcfabd57f00000800000006000000040000000200000070deb6abd57f0000a17acfabd57f00000000000000000000060000000800000098deb6abd57f0000b07bcfabd57f000004000000030000000200000001000000b0deb6abd57f0000817bcfabd57f0000000000000000000074b480f4fc7f0000c8b080f4fc7f0000c8b080f4fc7f000074b480f4fc7f000000006a80f3f73cf1d0deb6abd57f00002a52d5abd57f0000c8b080f4fc7f0000c8b080f4fc7f0000000000000000000084518cabd57f0000000000000000000000e7b6abd57f000000e7b6abd57f00008f990b1e3bad5a6700000000000000000000000000000000c0e354add57f000000e7b6abd57f00008f99cba356faf1988f9991bc23faf1980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7b6abd57f00007de387aad57f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006030b4aad57f0000b8edb6abd57f00000000000000000000000000000000000080b88eaad57f0000000000000000000080b28eaad57f0000000000000000000080c18eaad57f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7b6abd57f0000402555add57f000000e7b6abd57f00000100000000000000000000000000000000006a80f3f73cf1058f9d56adb3c7cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000407ab1abd57f000030a3adabd57f00005c64000053640000e0e9b6abd57f0000e0e9b6abd57f0000e0ffffffffffffff00000000000000000000000000000000f0deb6abd57f00000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010eab6abd57f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000c46ad90f52391d00000000000000000000000000000000000000000000000000f051d5abd57f0000c8b080f4fc7f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b0b6abd57f000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
function: start: 0 end: 7fd5abcf7930 name: unknown_start
diff --git a/libbinderwrapper/Android.bp b/libbinderwrapper/Android.bp
new file mode 100644
index 0000000..6fac0d8
--- /dev/null
+++ b/libbinderwrapper/Android.bp
@@ -0,0 +1,61 @@
+//
+// 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.
+//
+
+cc_defaults {
+ name: "libbinderwrapper_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+
+ // for libchrome
+ "-Wno-sign-promo",
+ ],
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "libbinder",
+ "libchrome",
+ "libutils",
+ ],
+}
+
+// libbinderwrapper shared library
+// ========================================================
+cc_library_shared {
+ name: "libbinderwrapper",
+ defaults: ["libbinderwrapper_defaults"],
+
+ srcs: [
+ "binder_wrapper.cc",
+ "real_binder_wrapper.cc",
+ ],
+}
+
+// libbinderwrapper_test_support static library
+// ========================================================
+cc_library_static {
+ name: "libbinderwrapper_test_support",
+ defaults: ["libbinderwrapper_defaults"],
+
+ static_libs: ["libgtest"],
+ shared_libs: ["libbinderwrapper"],
+
+ srcs: [
+ "binder_test_base.cc",
+ "stub_binder_wrapper.cc",
+ ],
+}
diff --git a/libbinderwrapper/Android.mk b/libbinderwrapper/Android.mk
deleted file mode 100644
index c768373..0000000
--- a/libbinderwrapper/Android.mk
+++ /dev/null
@@ -1,62 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-binderwrapperCommonCFlags := -Wall -Werror -Wno-unused-parameter
-binderwrapperCommonCFlags += -Wno-sign-promo # for libchrome
-binderwrapperCommonExportCIncludeDirs := $(LOCAL_PATH)/include
-binderwrapperCommonCIncludes := $(LOCAL_PATH)/include
-binderwrapperCommonSharedLibraries := \
- libbinder \
- libchrome \
- libutils \
-
-# libbinderwrapper shared library
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbinderwrapper
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := $(binderwrapperCommonCFlags)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(binderwrapperCommonExportCIncludeDirs)
-LOCAL_C_INCLUDES := $(binderwrapperCommonCIncludes)
-LOCAL_SHARED_LIBRARIES := $(binderwrapperCommonSharedLibraries)
-LOCAL_SRC_FILES := \
- binder_wrapper.cc \
- real_binder_wrapper.cc \
-
-include $(BUILD_SHARED_LIBRARY)
-
-# libbinderwrapper_test_support static library
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbinderwrapper_test_support
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := $(binderwrapperCommonCFlags)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(binderwrapperCommonExportCIncludeDirs)
-LOCAL_C_INCLUDES := $(binderwrapperCommonCIncludes)
-LOCAL_STATIC_LIBRARIES := libgtest
-LOCAL_SHARED_LIBRARIES := \
- $(binderwrapperCommonSharedLibraries) \
- libbinderwrapper \
-
-LOCAL_SRC_FILES := \
- binder_test_base.cc \
- stub_binder_wrapper.cc \
-
-include $(BUILD_STATIC_LIBRARY)
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index 4a5f2a7..47de12a 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -17,6 +17,9 @@
cc_library {
name: "libcrypto_utils",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
host_supported: true,
srcs: [
"android_pubkey.c",
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 513736d..cfe8d29 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -24,7 +24,6 @@
"socket_inaddr_any_server_unix.c",
"socket_local_client_unix.c",
"socket_local_server_unix.c",
- "socket_loopback_server_unix.c",
"socket_network_client_unix.c",
"sockets_unix.cpp",
"str_parms.c",
@@ -51,10 +50,14 @@
cc_library {
name: "libcutils",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
host_supported: true,
srcs: [
"config_utils.c",
- "fs_config.c",
+ "fs_config.cpp",
"canned_fs_config.c",
"hashmap.c",
"iosched_policy.c",
@@ -95,6 +98,9 @@
shared: {
enabled: false,
},
+ cflags: [
+ "-D_GNU_SOURCE",
+ ],
},
android: {
@@ -148,14 +154,13 @@
"libutils_headers",
],
export_header_lib_headers: ["libcutils_headers"],
+ local_include_dirs: ["include"],
cflags: [
"-Werror",
"-Wall",
"-Wextra",
],
-
- clang: true,
}
subdirs = ["tests"]
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c
index a33e45f..996d89d 100644
--- a/libcutils/android_reboot.c
+++ b/libcutils/android_reboot.c
@@ -35,11 +35,11 @@
restart_cmd = "shutdown";
break;
case ANDROID_RB_THERMOFF:
- restart_cmd = "thermal-shutdown";
+ restart_cmd = "shutdown,thermal";
break;
}
if (!restart_cmd) return -1;
- if (arg) {
+ if (arg && arg[0]) {
ret = asprintf(&prop_value, "%s,%s", restart_cmd, arg);
} else {
ret = asprintf(&prop_value, "%s", restart_cmd);
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index 92717c0..b4abb79 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -28,6 +28,7 @@
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
+#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.c
index e0e6a34..819a846 100644
--- a/libcutils/canned_fs_config.c
+++ b/libcutils/canned_fs_config.c
@@ -17,11 +17,13 @@
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
#include <private/canned_fs_config.h>
typedef struct {
@@ -41,7 +43,7 @@
}
int load_canned_fs_config(const char* fn) {
- char line[PATH_MAX + 200];
+ char buf[PATH_MAX + 200];
FILE* f;
f = fopen(fn, "r");
@@ -50,17 +52,21 @@
return -1;
}
- while (fgets(line, sizeof(line), f)) {
+ while (fgets(buf, sizeof(buf), f)) {
Path* p;
char* token;
+ char* line = buf;
+ bool rootdir;
while (canned_used >= canned_alloc) {
canned_alloc = (canned_alloc+1) * 2;
canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
}
p = canned_data + canned_used;
- p->path = strdup(strtok(line, " "));
- p->uid = atoi(strtok(NULL, " "));
+ if (line[0] == '/') line++;
+ rootdir = line[0] == ' ';
+ p->path = strdup(rootdir ? "" : strtok(line, " "));
+ p->uid = atoi(strtok(rootdir ? line : NULL, " "));
p->gid = atoi(strtok(NULL, " "));
p->mode = strtol(strtok(NULL, " "), NULL, 8); // mode is in octal
p->capabilities = 0;
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.cpp
similarity index 74%
rename from libcutils/fs_config.c
rename to libcutils/fs_config.cpp
index 5b9d174..1185040 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.cpp
@@ -14,15 +14,12 @@
* limitations under the License.
*/
-/* This file is used to define the properties of the filesystem
-** images generated by build tools (mkbootfs and mkyaffs2image) and
-** by the device side of adb.
-*/
+// This file is used to define the properties of the filesystem
+// images generated by build tools (mkbootfs and mkyaffs2image) and
+// by the device side of adb.
#define LOG_TAG "fs_config"
-#define _GNU_SOURCE
-
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
@@ -42,8 +39,10 @@
#define O_BINARY 0
#endif
-/* My kingdom for <endian.h> */
-static inline uint16_t get2LE(const uint8_t* src) { return src[0] | (src[1] << 8); }
+// My kingdom for <endian.h>
+static inline uint16_t get2LE(const uint8_t* src) {
+ return src[0] | (src[1] << 8);
+}
static inline uint64_t get8LE(const uint8_t* src) {
uint32_t low, high;
@@ -55,14 +54,13 @@
#define ALIGN(x, alignment) (((x) + ((alignment)-1)) & ~((alignment)-1))
-/* Rules for directories.
-** These rules are applied based on "first match", so they
-** should start with the most specific path and work their
-** way up to the root.
-*/
+// Rules for directories.
+// These rules are applied based on "first match", so they
+// should start with the most specific path and work their
+// way up to the root.
static const struct fs_path_config android_dirs[] = {
- /* clang-format off */
+ // clang-format off
{ 00770, AID_SYSTEM, AID_CACHE, 0, "cache" },
{ 00500, AID_ROOT, AID_ROOT, 0, "config" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
@@ -92,25 +90,26 @@
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor" },
{ 00755, AID_ROOT, AID_ROOT, 0, 0 },
- /* clang-format on */
+ // clang-format on
};
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__android_dirs = android_dirs;
+#endif
-/* Rules for files.
-** These rules are applied based on "first match", so they
-** should start with the most specific path and work their
-** way up to the root. Prefixes ending in * denotes wildcard
-** and will allow partial matches.
-*/
+// Rules for files.
+// These rules are applied based on "first match", so they
+// should start with the most specific path and work their
+// way up to the root. Prefixes ending in * denotes wildcard
+// and will allow partial matches.
static const char sys_conf_dir[] = "/system/etc/fs_config_dirs";
static const char sys_conf_file[] = "/system/etc/fs_config_files";
-/* No restrictions are placed on the vendor and oem file-system config files,
- * although the developer is advised to restrict the scope to the /vendor or
- * oem/ file-system since the intent is to provide support for customized
- * portions of a separate vendor.img or oem.img. Has to remain open so that
- * customization can also land on /system/vendor, /system/oem or /system/odm.
- * We expect build-time checking or filtering when constructing the associated
- * fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
- */
+// No restrictions are placed on the vendor and oem file-system config files,
+// although the developer is advised to restrict the scope to the /vendor or
+// oem/ file-system since the intent is to provide support for customized
+// portions of a separate vendor.img or oem.img. Has to remain open so that
+// customization can also land on /system/vendor, /system/oem or /system/odm.
+// We expect build-time checking or filtering when constructing the associated
+// fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
static const char ven_conf_dir[] = "/vendor/etc/fs_config_dirs";
static const char ven_conf_file[] = "/vendor/etc/fs_config_files";
static const char oem_conf_dir[] = "/oem/etc/fs_config_dirs";
@@ -125,7 +124,7 @@
};
static const struct fs_path_config android_files[] = {
- /* clang-format off */
+ // clang-format off
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" },
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral/*" },
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" },
@@ -135,20 +134,13 @@
{ 00640, AID_ROOT, AID_SHELL, 0, "data/nativetest64/tests.txt" },
{ 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest/*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64/*" },
- { 00600, AID_ROOT, AID_ROOT, 0, "default.prop" }, // legacy
- { 00600, AID_ROOT, AID_ROOT, 0, "system/etc/prop.default" },
+ { 00600, AID_ROOT, AID_ROOT, 0, "default.prop" },
{ 00600, AID_ROOT, AID_ROOT, 0, "odm/build.prop" },
{ 00600, AID_ROOT, AID_ROOT, 0, "odm/default.prop" },
{ 00444, AID_ROOT, AID_ROOT, 0, odm_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, odm_conf_file + 1 },
- { 00600, AID_ROOT, AID_ROOT, 0, "system/odm/build.prop" },
- { 00600, AID_ROOT, AID_ROOT, 0, "system/odm/default.prop" },
- { 00444, AID_ROOT, AID_ROOT, 0, "system/odm/etc/fs_config_dirs" },
- { 00444, AID_ROOT, AID_ROOT, 0, "system/odm/etc/fs_config_files" },
{ 00444, AID_ROOT, AID_ROOT, 0, oem_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, oem_conf_file + 1 },
- { 00444, AID_ROOT, AID_ROOT, 0, "system/oem/etc/fs_config_dirs" },
- { 00444, AID_ROOT, AID_ROOT, 0, "system/oem/etc/fs_config_files" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump32" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump64" },
@@ -165,63 +157,51 @@
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" },
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" },
{ 00440, AID_ROOT, AID_ROOT, 0, "system/etc/recovery.img" },
- { 00600, AID_ROOT, AID_ROOT, 0, "system/vendor/build.prop" },
- { 00600, AID_ROOT, AID_ROOT, 0, "system/vendor/default.prop" },
- { 00444, AID_ROOT, AID_ROOT, 0, "system/vendor/etc/fs_config_dirs" },
- { 00444, AID_ROOT, AID_ROOT, 0, "system/vendor/etc/fs_config_files" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/build.prop" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/default.prop" },
{ 00444, AID_ROOT, AID_ROOT, 0, ven_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, ven_conf_file + 1 },
- /* the following two files are INTENTIONALLY set-uid, but they
- * are NOT included on user builds. */
+ // the following two files are INTENTIONALLY set-uid, but they
+ // are NOT included on user builds.
{ 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" },
{ 04750, AID_ROOT, AID_SHELL, 0, "system/xbin/su" },
- /* the following files have enhanced capabilities and ARE included
- * in user builds. */
+ // the following files have enhanced capabilities and ARE included
+ // in user builds.
{ 00700, AID_SYSTEM, AID_SHELL, CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
"system/bin/inputflinger" },
{ 00550, AID_LOGD, AID_LOGD, CAP_MASK_LONG(CAP_SYSLOG) |
CAP_MASK_LONG(CAP_AUDIT_CONTROL) |
CAP_MASK_LONG(CAP_SETGID),
"system/bin/logd" },
+ { 00550, AID_SYSTEM, AID_LOG, CAP_MASK_LONG(CAP_SYSLOG),
+ "system/bin/bootstat" },
{ 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) |
CAP_MASK_LONG(CAP_SETGID),
"system/bin/run-as" },
- /* Support FIFO scheduling mode in SurfaceFlinger. */
+ // Support FIFO scheduling mode in SurfaceFlinger.
{ 00755, AID_SYSTEM, AID_GRAPHICS, CAP_MASK_LONG(CAP_SYS_NICE),
"system/bin/surfaceflinger" },
- /* Support hostapd administering a network interface. */
- { 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) |
- CAP_MASK_LONG(CAP_NET_RAW),
- "system/vendor/bin/hostapd" },
+ // Support hostapd administering a network interface.
{ 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) |
CAP_MASK_LONG(CAP_NET_RAW),
"vendor/bin/hostapd" },
- /* Support Bluetooth legacy hal accessing /sys/class/rfkill
- * Support RT scheduling in Bluetooth */
- { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
- CAP_MASK_LONG(CAP_SYS_NICE),
- "system/vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
+ // Support Bluetooth legacy hal accessing /sys/class/rfkill
+ // Support RT scheduling in Bluetooth
{ 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
CAP_MASK_LONG(CAP_SYS_NICE),
"vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
- /* Support wifi_hal_legacy administering a network interface. */
- { 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) |
- CAP_MASK_LONG(CAP_NET_RAW),
- "system/vendor/bin/hw/android.hardware.wifi@1.0-service" },
+ // Support wifi_hal_legacy administering a network interface.
{ 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) |
CAP_MASK_LONG(CAP_NET_RAW),
"vendor/bin/hw/android.hardware.wifi@1.0-service" },
- /* A non-privileged zygote that spawns
- * isolated processes for web rendering. */
+ // A non-privileged zygote that spawns isolated processes for web rendering.
{ 0750, AID_ROOT, AID_ROOT, CAP_MASK_LONG(CAP_SETUID) |
CAP_MASK_LONG(CAP_SETGID) |
CAP_MASK_LONG(CAP_SETPCAP),
@@ -231,7 +211,7 @@
CAP_MASK_LONG(CAP_SETPCAP),
"system/bin/webview_zygote64" },
- /* generic defaults */
+ // generic defaults
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "init*" },
@@ -239,14 +219,15 @@
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/xbin/*" },
{ 00644, AID_ROOT, AID_ROOT, 0, 0 },
- /* clang-format on */
+ // clang-format on
};
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__android_files = android_files;
+#endif
static size_t strip(const char* path, size_t len, const char suffix[]) {
if (len < strlen(suffix)) return len;
@@ -258,9 +239,9 @@
int fd = -1;
if (target_out_path && *target_out_path) {
- /* target_out_path is the path to the directory holding content of
- * system partition but as we cannot guarantee it ends with '/system'
- * or with or without a trailing slash, need to strip them carefully. */
+ // target_out_path is the path to the directory holding content of
+ // system partition but as we cannot guarantee it ends with '/system'
+ // or with or without a trailing slash, need to strip them carefully.
char* name = NULL;
size_t len = strlen(target_out_path);
len = strip(target_out_path, len, "/");
@@ -276,23 +257,52 @@
return fd;
}
-static bool fs_config_cmp(bool dir, const char* prefix, size_t len, const char* path, size_t plen) {
- if (dir) {
- if (plen < len) {
- return false;
- }
- } else {
- /* If name ends in * then allow partial matches. */
- if (prefix[len - 1] == '*') {
- return !strncmp(prefix, path, len - 1);
- }
- if (plen != len) {
- return false;
- }
+// if path is "vendor/<stuff>", "oem/<stuff>" or "odm/<stuff>"
+static bool is_partition(const char* path, size_t len) {
+ static const char* partitions[] = {"vendor/", "oem/", "odm/"};
+ for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
+ size_t plen = strlen(partitions[i]);
+ if (len <= plen) continue;
+ if (!strncmp(path, partitions[i], plen)) return true;
}
- return !strncmp(prefix, path, len);
+ return false;
}
+static inline bool prefix_cmp(bool partial, const char* prefix, size_t len, const char* path,
+ size_t plen) {
+ return ((partial && plen >= len) || (plen == len)) && !strncmp(prefix, path, len);
+}
+
+// alias prefixes of "<partition>/<stuff>" to "system/<partition>/<stuff>" or
+// "system/<partition>/<stuff>" to "<partition>/<stuff>"
+static bool fs_config_cmp(bool partial, const char* prefix, size_t len, const char* path,
+ size_t plen) {
+ // If name ends in * then allow partial matches.
+ if (!partial && prefix[len - 1] == '*') {
+ len--;
+ partial = true;
+ }
+
+ if (prefix_cmp(partial, prefix, len, path, plen)) return true;
+
+ static const char system[] = "system/";
+ if (!strncmp(path, system, strlen(system))) {
+ path += strlen(system);
+ plen -= strlen(system);
+ } else if (len <= strlen(system)) {
+ return false;
+ } else if (strncmp(prefix, system, strlen(system))) {
+ return false;
+ } else {
+ prefix += strlen(system);
+ len -= strlen(system);
+ }
+ return is_partition(prefix, len) && prefix_cmp(partial, prefix, len, path, plen);
+}
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__fs_config_cmp = fs_config_cmp;
+#endif
+
void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
unsigned* mode, uint64_t* capabilities) {
const struct fs_path_config* pc;
@@ -318,7 +328,7 @@
ALOGE("%s len is corrupted", conf[which][dir]);
break;
}
- prefix = calloc(1, remainder);
+ prefix = static_cast<char*>(calloc(1, remainder));
if (!prefix) {
ALOGE("%s out of memory", conf[which][dir]);
break;
@@ -329,7 +339,7 @@
break;
}
len = strnlen(prefix, remainder);
- if (len >= remainder) { /* missing a terminating null */
+ if (len >= remainder) { // missing a terminating null
free(prefix);
ALOGE("%s is corrupted", conf[which][dir]);
break;
diff --git a/libcutils/include/cutils/android_filesystem_config.h b/libcutils/include/cutils/android_filesystem_config.h
new file mode 120000
index 0000000..d2a92fe
--- /dev/null
+++ b/libcutils/include/cutils/android_filesystem_config.h
@@ -0,0 +1 @@
+../private/android_filesystem_config.h
\ No newline at end of file
diff --git a/libcutils/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index 716567a..a903adb 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -29,8 +29,8 @@
/* Properties */
#define ANDROID_RB_PROPERTY "sys.powerctl"
-/* Android reboot reason stored in this file */
-#define LAST_REBOOT_REASON_FILE "/data/misc/reboot/last_reboot_reason"
+/* Android reboot reason stored in this property */
+#define LAST_REBOOT_REASON_PROPERTY "persist.sys.boot.reason"
/* Reboot or shutdown the system.
* This call uses ANDROID_RB_PROPERTY to request reboot to init process.
diff --git a/libcutils/include/cutils/bitops.h b/libcutils/include/cutils/bitops.h
index 045830d..38d2840 100644
--- a/libcutils/include/cutils/bitops.h
+++ b/libcutils/include/cutils/bitops.h
@@ -24,94 +24,15 @@
__BEGIN_DECLS
-/*
- * Bitmask Operations
- *
- * Note this doesn't provide any locking/exclusion, and isn't atomic.
- * Additionally no bounds checking is done on the bitmask array.
- *
- * Example:
- *
- * int num_resources;
- * unsigned int resource_bits[BITS_TO_WORDS(num_resources)];
- * bitmask_init(resource_bits, num_resources);
- * ...
- * int bit = bitmask_ffz(resource_bits, num_resources);
- * bitmask_set(resource_bits, bit);
- * ...
- * if (bitmask_test(resource_bits, bit)) { ... }
- * ...
- * bitmask_clear(resource_bits, bit);
- *
- */
-
-#define BITS_PER_WORD (sizeof(unsigned int) * 8)
-#define BITS_TO_WORDS(x) (((x) + BITS_PER_WORD - 1) / BITS_PER_WORD)
-#define BIT_IN_WORD(x) ((x) % BITS_PER_WORD)
-#define BIT_WORD(x) ((x) / BITS_PER_WORD)
-#define BIT_MASK(x) (1 << BIT_IN_WORD(x))
-
-static inline void bitmask_init(unsigned int *bitmask, int num_bits)
-{
- memset(bitmask, 0, BITS_TO_WORDS(num_bits)*sizeof(unsigned int));
-}
-
-static inline int bitmask_ffz(unsigned int *bitmask, int num_bits)
-{
- int bit, result;
- size_t i;
-
- for (i = 0; i < BITS_TO_WORDS(num_bits); i++) {
- bit = ffs(~bitmask[i]);
- if (bit) {
- // ffs is 1-indexed, return 0-indexed result
- bit--;
- result = BITS_PER_WORD * i + bit;
- if (result >= num_bits)
- return -1;
- return result;
- }
- }
- return -1;
-}
-
-static inline int bitmask_weight(unsigned int *bitmask, int num_bits)
-{
- size_t i;
- int weight = 0;
-
- for (i = 0; i < BITS_TO_WORDS(num_bits); i++)
- weight += __builtin_popcount(bitmask[i]);
- return weight;
-}
-
-static inline void bitmask_set(unsigned int *bitmask, int bit)
-{
- bitmask[BIT_WORD(bit)] |= BIT_MASK(bit);
-}
-
-static inline void bitmask_clear(unsigned int *bitmask, int bit)
-{
- bitmask[BIT_WORD(bit)] &= ~BIT_MASK(bit);
-}
-
-static inline bool bitmask_test(unsigned int *bitmask, int bit)
-{
- return bitmask[BIT_WORD(bit)] & BIT_MASK(bit);
-}
-
-static inline int popcount(unsigned int x)
-{
+static inline int popcount(unsigned int x) {
return __builtin_popcount(x);
}
-static inline int popcountl(unsigned long x)
-{
+static inline int popcountl(unsigned long x) {
return __builtin_popcountl(x);
}
-static inline int popcountll(unsigned long long x)
-{
+static inline int popcountll(unsigned long long x) {
return __builtin_popcountll(x);
}
diff --git a/libcutils/include/cutils/list.h b/libcutils/include/cutils/list.h
index 4ba2cfd..dfdc53b 100644
--- a/libcutils/include/cutils/list.h
+++ b/libcutils/include/cutils/list.h
@@ -34,20 +34,20 @@
#define list_declare(name) \
struct listnode name = { \
- .next = &name, \
- .prev = &name, \
+ .next = &(name), \
+ .prev = &(name), \
}
#define list_for_each(node, list) \
- for (node = (list)->next; node != (list); node = node->next)
+ for ((node) = (list)->next; (node) != (list); (node) = (node)->next)
#define list_for_each_reverse(node, list) \
- for (node = (list)->prev; node != (list); node = node->prev)
+ for ((node) = (list)->prev; (node) != (list); (node) = (node)->prev)
#define list_for_each_safe(node, n, list) \
- for (node = (list)->next, n = node->next; \
- node != (list); \
- node = n, n = node->next)
+ for ((node) = (list)->next, (n) = (node)->next; \
+ (node) != (list); \
+ (node) = (n), (n) = (node)->next)
static inline void list_init(struct listnode *node)
{
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index 7d6a988..55754b5 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -25,8 +25,8 @@
/* Declare a char array for use with native_handle_init */
#define NATIVE_HANDLE_DECLARE_STORAGE(name, maxFds, maxInts) \
- alignas(native_handle_t) char name[ \
- sizeof(native_handle_t) + sizeof(int) * (maxFds + maxInts)]
+ alignas(native_handle_t) char (name)[ \
+ sizeof(native_handle_t) + sizeof(int) * ((maxFds) + (maxInts))]
typedef struct native_handle
{
diff --git a/libcutils/include/cutils/properties.h b/libcutils/include/cutils/properties.h
index b45f58f..d2e0871 100644
--- a/libcutils/include/cutils/properties.h
+++ b/libcutils/include/cutils/properties.h
@@ -43,12 +43,7 @@
** If the property read fails or returns an empty value, the default
** value is used (if nonnull).
*/
-int property_get(const char *key, char *value, const char *default_value)
-/* Sometimes we use not-Bionic with this, so we need this check. */
-#if defined(__BIONIC_FORTIFY)
- __overloadable __RENAME_CLANG(property_get)
-#endif
- ;
+int property_get(const char* key, char* value, const char* default_value);
/* property_get_bool: returns the value of key coerced into a
** boolean. If the property is not set, then the default value is returned.
@@ -119,27 +114,15 @@
#if defined(__clang__)
-/* Some projects use -Weverything; enable_if is clang-specific.
-** FIXME: This is marked used because we'll otherwise get complaints about an
-** unused static function. This is more robust than marking it unused, since
-** -Wused-but-marked-unused is a thing that will complain if this function is
-** actually used, thus making FORTIFY noisier when an error happens. It's going
-** to go away anyway during our FORTIFY cleanup.
-**/
+/* Some projects use -Weverything; diagnose_if is clang-specific. */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgcc-compat"
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-int property_get(const char *key, char *value, const char *default_value)
- __overloadable
- __enable_if(__bos(value) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
- __bos(value) < PROPERTY_VALUE_MAX, __property_get_err_str)
- __errorattr(__property_get_err_str)
- __attribute__((used));
+int property_get(const char* key, char* value, const char* default_value)
+ __clang_error_if(__bos(value) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
+ __bos(value) < PROPERTY_VALUE_MAX,
+ __property_get_err_str);
#pragma clang diagnostic pop
-/* No object size? No FORTIFY.
-*/
-
#else /* defined(__clang__) */
extern int __property_get_real(const char *, char *, const char *)
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
index 15391d9..cf8f5c3 100644
--- a/libcutils/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -34,7 +34,7 @@
* Check if Linux kernel enables SCHEDTUNE feature (only available in Android
* common kernel or Linaro LSK, not in mainline Linux as of v4.9)
*
- * Return value: 1 if Linux kernel CONFIG_SCHEDTUNE=y; 0 otherwise.
+ * Return value: 1 if Linux kernel CONFIG_CGROUP_SCHEDTUNE=y; 0 otherwise.
*/
extern bool schedboost_enabled();
diff --git a/libcutils/include/cutils/sockets.h b/libcutils/include/cutils/sockets.h
index d724dd6..b24468b 100644
--- a/libcutils/include/cutils/sockets.h
+++ b/libcutils/include/cutils/sockets.h
@@ -88,8 +88,6 @@
cutils_socket_t socket_network_client(const char* host, int port, int type);
int socket_network_client_timeout(const char* host, int port, int type,
int timeout, int* getaddrinfo_error);
-int socket_loopback_server(int port, int type);
-int socket_loopback_server6(int port, int type);
int socket_local_server(const char* name, int namespaceId, int type);
int socket_local_server_bind(int s, const char* name, int namespaceId);
int socket_local_client_connect(int fd, const char *name, int namespaceId,
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index bbba853..55ece54 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-/* This file is used to define the properties of the filesystem
-** images generated by build tools (mkbootfs and mkyaffs2image) and
-** by the device side of adb.
-*/
-
/*
* This file is consumed by build/tools/fs_config and is used
* for generating various files. Anything #define AID_<name>
@@ -49,18 +44,12 @@
#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
#define _ANDROID_FILESYSTEM_CONFIG_H_
-#include <stdint.h>
-#include <sys/cdefs.h>
#include <sys/types.h>
-#if defined(__ANDROID__)
-#include <linux/capability.h>
-#else
-#include "android_filesystem_capability.h"
+#if !defined(__ANDROID_VNDK__) && !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
+#include <private/fs_config.h>
#endif
-#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
-
/* This is the master Users and Groups config for the platform.
* DO NOT EVER RENUMBER
*/
@@ -130,6 +119,9 @@
#define AID_MEDIA_OBB 1059 /* GID for OBB files on internal media storage */
#define AID_ESE 1060 /* embedded secure element (eSE) subsystem */
#define AID_OTA_UPDATE 1061 /* resource tracking UID for OTA updates */
+#define AID_AUTOMOTIVE_EVS 1062 /* Automotive rear and surround view system */
+#define AID_LOWPAN 1063 /* LoWPAN subsystem */
+#define AID_HSM 1064 /* hardware security module subsystem */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
@@ -152,6 +144,7 @@
#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */
#define AID_READPROC 3009 /* Allow /proc read access */
#define AID_WAKELOCK 3010 /* Allow system wakelock read/write access */
+#define AID_UHID 3011 /* Allow read/write to /dev/uhid node */
/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
#define AID_OEM_RESERVED_2_START 5000
@@ -191,36 +184,4 @@
* Also see build/tools/fs_config for more details.
*/
-#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
-
-struct fs_path_config {
- unsigned mode;
- unsigned uid;
- unsigned gid;
- uint64_t capabilities;
- const char* prefix;
-};
-
-/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
-
-__BEGIN_DECLS
-
-/*
- * Used in:
- * build/tools/fs_config/fs_config.c
- * build/tools/fs_get_stats/fs_get_stats.c
- * system/extras/ext4_utils/make_ext4fs_main.c
- * external/squashfs-tools/squashfs-tools/android.c
- * system/core/cpio/mkbootfs.c
- * system/core/adb/file_sync_service.cpp
- * system/extras/ext4_utils/canned_fs_config.c
- */
-void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
- unsigned* mode, uint64_t* capabilities);
-
-ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc);
-
-__END_DECLS
-
-#endif
#endif
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index 7dad668..aab5042 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -14,10 +14,24 @@
* limitations under the License.
*/
+/* This file is used to define the properties of the filesystem
+** images generated by build tools (mkbootfs and mkyaffs2image) and
+** by the device side of adb.
+*/
+
#ifndef _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
#define _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
#include <stdint.h>
+#include <sys/cdefs.h>
+
+#if defined(__BIONIC__)
+#include <linux/capability.h>
+#else // defined(__BIONIC__)
+#include "android_filesystem_capability.h"
+#endif // defined(__BIONIC__)
+
+#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
/*
* binary format for the runtime <partition>/etc/fs_config_(dirs|files)
@@ -34,4 +48,33 @@
char prefix[];
} __attribute__((__aligned__(sizeof(uint64_t))));
+struct fs_path_config {
+ unsigned mode;
+ unsigned uid;
+ unsigned gid;
+ uint64_t capabilities;
+ const char* prefix;
+};
+
+/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
+
+__BEGIN_DECLS
+
+/*
+ * Used in:
+ * build/tools/fs_config/fs_config.c
+ * build/tools/fs_get_stats/fs_get_stats.c
+ * system/extras/ext4_utils/make_ext4fs_main.c
+ * external/squashfs-tools/squashfs-tools/android.c
+ * system/core/cpio/mkbootfs.c
+ * system/core/adb/file_sync_service.cpp
+ * system/extras/ext4_utils/canned_fs_config.c
+ */
+void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
+ unsigned* mode, uint64_t* capabilities);
+
+ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc);
+
+__END_DECLS
+
#endif /* _LIBS_CUTILS_PRIVATE_FS_CONFIG_H */
diff --git a/libcutils/include_vndk/cutils/android_filesystem_config.h b/libcutils/include_vndk/cutils/android_filesystem_config.h
new file mode 120000
index 0000000..13a5a08
--- /dev/null
+++ b/libcutils/include_vndk/cutils/android_filesystem_config.h
@@ -0,0 +1 @@
+../../include/private/android_filesystem_config.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/private b/libcutils/include_vndk/private
deleted file mode 120000
index 2245a85..0000000
--- a/libcutils/include_vndk/private
+++ /dev/null
@@ -1 +0,0 @@
-../include/private
\ No newline at end of file
diff --git a/libcutils/native_handle.c b/libcutils/native_handle.c
index 9f4840a..95bbc41 100644
--- a/libcutils/native_handle.c
+++ b/libcutils/native_handle.c
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "NativeHandle"
+#include <cutils/native_handle.h>
#include <errno.h>
#include <stdint.h>
@@ -22,15 +22,12 @@
#include <string.h>
#include <unistd.h>
-#include <android/log.h>
-#include <cutils/native_handle.h>
-
static const int kMaxNativeFds = 1024;
static const int kMaxNativeInts = 1024;
-native_handle_t* native_handle_init(char* storage, int numFds, int numInts)
-{
+native_handle_t* native_handle_init(char* storage, int numFds, int numInts) {
if ((uintptr_t) storage % alignof(native_handle_t)) {
+ errno = EINVAL;
return NULL;
}
@@ -38,13 +35,12 @@
handle->version = sizeof(native_handle_t);
handle->numFds = numFds;
handle->numInts = numInts;
-
return handle;
}
-native_handle_t* native_handle_create(int numFds, int numInts)
-{
+native_handle_t* native_handle_create(int numFds, int numInts) {
if (numFds < 0 || numInts < 0 || numFds > kMaxNativeFds || numInts > kMaxNativeInts) {
+ errno = EINVAL;
return NULL;
}
@@ -58,14 +54,13 @@
return h;
}
-native_handle_t* native_handle_clone(const native_handle_t* handle)
-{
+native_handle_t* native_handle_clone(const native_handle_t* handle) {
native_handle_t* clone = native_handle_create(handle->numFds, handle->numInts);
- int i;
+ if (clone == NULL) return NULL;
- for (i = 0; i < handle->numFds; i++) {
+ for (int i = 0; i < handle->numFds; i++) {
clone->data[i] = dup(handle->data[i]);
- if (clone->data[i] < 0) {
+ if (clone->data[i] == -1) {
clone->numFds = i;
native_handle_close(clone);
native_handle_delete(clone);
@@ -74,30 +69,27 @@
}
memcpy(&clone->data[handle->numFds], &handle->data[handle->numFds],
- sizeof(int) * handle->numInts);
+ sizeof(int) * handle->numInts);
return clone;
}
-int native_handle_delete(native_handle_t* h)
-{
+int native_handle_delete(native_handle_t* h) {
if (h) {
- if (h->version != sizeof(native_handle_t))
- return -EINVAL;
+ if (h->version != sizeof(native_handle_t)) return -EINVAL;
free(h);
}
return 0;
}
-int native_handle_close(const native_handle_t* h)
-{
- if (h->version != sizeof(native_handle_t))
- return -EINVAL;
+int native_handle_close(const native_handle_t* h) {
+ if (h->version != sizeof(native_handle_t)) return -EINVAL;
+ int saved_errno = errno;
const int numFds = h->numFds;
- int i;
- for (i=0 ; i<numFds ; i++) {
+ for (int i = 0; i < numFds; ++i) {
close(h->data[i]);
}
+ errno = saved_errno;
return 0;
}
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index 3837ca7..f733e90 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -28,13 +28,6 @@
#define UNUSED __attribute__((__unused__))
-#ifndef SLOGE
-#define SLOGE ALOGE
-#endif
-#ifndef SLOGW
-#define SLOGW ALOGW
-#endif
-
/* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
* Call this any place a SchedPolicy is used as an input parameter.
* Returns the possibly re-mapped policy.
@@ -123,11 +116,8 @@
on where init.rc mounts cpuset. That's why we'd better require this
configuration be set if CONFIG_CPUSETS is set.
- With runtime check using the following function, build time
- variables like ENABLE_CPUSETS (used in Android.mk) or cpusets (used
- in Android.bp) are not needed.
+ In older releases, this was controlled by build-time configuration.
*/
-
bool cpusets_enabled() {
static bool enabled = (access("/dev/cpuset/tasks", F_OK) == 0);
@@ -136,15 +126,11 @@
/*
Similar to CONFIG_CPUSETS above, but with a different configuration
- CONFIG_SCHEDTUNE that's in Android common Linux kernel and Linaro
+ CONFIG_CGROUP_SCHEDTUNE that's in Android common Linux kernel and Linaro
Stable Kernel (LSK), but not in mainline Linux as of v4.9.
- With runtime check using the following function, build time
- variables like ENABLE_SCHEDBOOST (used in Android.mk) or schedboost
- (used in Android.bp) are not needed.
-
+ In older releases, this was controlled by build-time configuration.
*/
-
bool schedboost_enabled() {
static bool enabled = (access("/dev/stune/tasks", F_OK) == 0);
@@ -263,24 +249,26 @@
char grpBuf[32];
- if (cpusets_enabled()) {
+ grpBuf[0] = '\0';
+ if (schedboost_enabled()) {
+ if (getCGroupSubsys(tid, "schedtune", grpBuf, sizeof(grpBuf)) < 0) return -1;
+ }
+ if ((grpBuf[0] == '\0') && cpusets_enabled()) {
if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0) return -1;
- if (grpBuf[0] == '\0') {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "foreground")) {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "background")) {
- *policy = SP_BACKGROUND;
- } else if (!strcmp(grpBuf, "top-app")) {
- *policy = SP_TOP_APP;
- } else {
- errno = ERANGE;
- return -1;
- }
- } else {
- // In b/34193533, we removed bg_non_interactive cgroup, so now
- // all threads are in FOREGROUND cgroup
+ }
+ if (grpBuf[0] == '\0') {
*policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "foreground")) {
+ *policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "system-background")) {
+ *policy = SP_SYSTEM;
+ } else if (!strcmp(grpBuf, "background")) {
+ *policy = SP_BACKGROUND;
+ } else if (!strcmp(grpBuf, "top-app")) {
+ *policy = SP_TOP_APP;
+ } else {
+ errno = ERANGE;
+ return -1;
}
return 0;
}
@@ -338,19 +326,28 @@
return 0;
}
-static void set_timerslack_ns(int tid, unsigned long long slack) {
+static void set_timerslack_ns(int tid, unsigned long slack) {
// v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
// TODO: once we've backported this, log if the open(2) fails.
- char buf[64];
- snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
- int fd = open(buf, O_WRONLY | O_CLOEXEC);
- if (fd != -1) {
- int len = snprintf(buf, sizeof(buf), "%llu", slack);
- if (write(fd, buf, len) != len) {
- SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
+ if (__sys_supports_timerslack) {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
+ int fd = open(buf, O_WRONLY | O_CLOEXEC);
+ if (fd != -1) {
+ int len = snprintf(buf, sizeof(buf), "%lu", slack);
+ if (write(fd, buf, len) != len) {
+ SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
+ }
+ close(fd);
+ return;
}
- close(fd);
- return;
+ }
+
+ // TODO: Remove when /proc/<tid>/timerslack_ns interface is backported.
+ if ((tid == 0) || (tid == gettid())) {
+ if (prctl(PR_SET_TIMERSLACK, slack) == -1) {
+ SLOGE("set_timerslack_ns prctl failed: %s\n", strerror(errno));
+ }
}
}
@@ -429,10 +426,7 @@
}
- if (__sys_supports_timerslack) {
- set_timerslack_ns(tid, policy == SP_BACKGROUND ?
- TIMER_SLACK_BG : TIMER_SLACK_FG);
- }
+ set_timerslack_ns(tid, policy == SP_BACKGROUND ? TIMER_SLACK_BG : TIMER_SLACK_FG);
return 0;
}
diff --git a/libcutils/socket_loopback_server_unix.c b/libcutils/socket_loopback_server_unix.c
deleted file mode 100644
index 7b92fd6..0000000
--- a/libcutils/socket_loopback_server_unix.c
+++ /dev/null
@@ -1,88 +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.
-*/
-
-#include <errno.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#define LISTEN_BACKLOG 4
-
-#if !defined(_WIN32)
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#endif
-
-#include <cutils/sockets.h>
-
-static int _socket_loopback_server(int family, int type, struct sockaddr * addr, size_t size)
-{
- int s, n;
-
- s = socket(family, type, 0);
- if(s < 0)
- return -1;
-
- n = 1;
- setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
-
- if(bind(s, addr, size) < 0) {
- close(s);
- return -1;
- }
-
- if (type == SOCK_STREAM) {
- int ret;
-
- ret = listen(s, LISTEN_BACKLOG);
-
- if (ret < 0) {
- close(s);
- return -1;
- }
- }
-
- return s;
-}
-
-/* open listen() port on loopback IPv6 interface */
-int socket_loopback_server6(int port, int type)
-{
- struct sockaddr_in6 addr;
-
- memset(&addr, 0, sizeof(addr));
- addr.sin6_family = AF_INET6;
- addr.sin6_port = htons(port);
- addr.sin6_addr = in6addr_loopback;
-
- return _socket_loopback_server(AF_INET6, type, (struct sockaddr *) &addr, sizeof(addr));
-}
-
-/* open listen() port on loopback interface */
-int socket_loopback_server(int port, int type)
-{
- struct sockaddr_in addr;
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
- return _socket_loopback_server(AF_INET, type, (struct sockaddr *) &addr, sizeof(addr));
-}
diff --git a/libcutils/socket_network_client_unix.c b/libcutils/socket_network_client_unix.c
index 37851b1..1b87c49 100644
--- a/libcutils/socket_network_client_unix.c
+++ b/libcutils/socket_network_client_unix.c
@@ -63,7 +63,7 @@
for (struct addrinfo* addr = addrs; addr != NULL; addr = addr->ai_next) {
// The Mac doesn't have SOCK_NONBLOCK.
int s = socket(addr->ai_family, type, addr->ai_protocol);
- if (s == -1 || toggle_O_NONBLOCK(s) == -1) return -1;
+ if (s == -1 || toggle_O_NONBLOCK(s) == -1) break;
int rc = connect(s, addr->ai_addr, addr->ai_addrlen);
if (rc == 0) {
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index 718d76b..7884190 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -27,7 +27,8 @@
"test_str_parms.cpp",
"android_get_control_socket_test.cpp",
"android_get_control_file_test.cpp",
- "multiuser_test.cpp"
+ "multiuser_test.cpp",
+ "fs_config.cpp",
],
},
@@ -62,6 +63,7 @@
cc_test {
name: "libcutils_test",
+ test_suites: ["device-tests"],
defaults: ["libcutils_test_default"],
host_supported: true,
shared_libs: test_libraries,
@@ -69,6 +71,7 @@
cc_test {
name: "libcutils_test_static",
+ test_suites: ["device-tests"],
defaults: ["libcutils_test_default"],
static_libs: ["libc"] + test_libraries,
stl: "libc++_static",
diff --git a/libcutils/tests/AndroidTest.xml b/libcutils/tests/AndroidTest.xml
new file mode 100644
index 0000000..dd7aca2
--- /dev/null
+++ b/libcutils/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for libcutils_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="libcutils_test->/data/local/tmp/libcutils_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="libcutils_test" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/libcutils/tests/AshmemTest.cpp b/libcutils/tests/AshmemTest.cpp
index 51c679f..a87e23e 100644
--- a/libcutils/tests/AshmemTest.cpp
+++ b/libcutils/tests/AshmemTest.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#include <sys/mman.h>
+#include <android-base/unique_fd.h>
#include <cutils/ashmem.h>
#include <gtest/gtest.h>
-#include <android-base/unique_fd.h>
+#include <linux/fs.h>
+#include <sys/mman.h>
using android::base::unique_fd;
@@ -29,8 +30,8 @@
ASSERT_EQ(0, ashmem_set_prot_region(fd, prot));
}
-void TestMmap(const unique_fd &fd, size_t size, int prot, void **region) {
- *region = mmap(nullptr, size, prot, MAP_SHARED, fd, 0);
+void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) {
+ *region = mmap(nullptr, size, prot, MAP_SHARED, fd, off);
ASSERT_NE(MAP_FAILED, *region);
}
@@ -38,6 +39,10 @@
EXPECT_EQ(MAP_FAILED, mmap(nullptr, size, prot, MAP_SHARED, fd, 0));
}
+void TestProtIs(const unique_fd& fd, int prot) {
+ EXPECT_EQ(prot, ioctl(fd, ASHMEM_GET_PROT_MASK));
+}
+
void FillData(uint8_t* data, size_t dataLen) {
for (size_t i = 0; i < dataLen; i++) {
data[i] = i & 0xFF;
@@ -101,6 +106,63 @@
EXPECT_EQ(0, munmap(region2, size));
}
+TEST(AshmemTest, FileOperationsTest) {
+ unique_fd fd;
+ void* region;
+
+ // Allocate a 4-page buffer, but leave page-sized holes on either side
+ constexpr size_t size = PAGE_SIZE * 4;
+ constexpr size_t dataSize = PAGE_SIZE * 2;
+ constexpr size_t holeSize = PAGE_SIZE;
+ ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
+ ASSERT_NO_FATAL_FAILURE(TestMmap(fd, dataSize, PROT_READ | PROT_WRITE, ®ion, holeSize));
+
+ uint8_t data[dataSize];
+ FillData(data, dataSize);
+ memcpy(region, data, dataSize);
+
+ constexpr off_t dataStart = holeSize;
+ constexpr off_t dataEnd = dataStart + dataSize;
+
+ // The sequence of seeks below looks something like this:
+ //
+ // [ ][data][data][ ]
+ // --^ lseek(99, SEEK_SET)
+ // ------^ lseek(dataStart, SEEK_CUR)
+ // ------^ lseek(0, SEEK_DATA)
+ // ------------^ lseek(dataStart, SEEK_HOLE)
+ // ^-- lseek(-99, SEEK_END)
+ // ^------ lseek(-dataStart, SEEK_CUR)
+ const struct {
+ // lseek() parameters
+ off_t offset;
+ int whence;
+ // Expected lseek() return value
+ off_t ret;
+ } seeks[] = {
+ {99, SEEK_SET, 99}, {dataStart, SEEK_CUR, dataStart + 99},
+ {0, SEEK_DATA, dataStart}, {dataStart, SEEK_HOLE, dataEnd},
+ {-99, SEEK_END, size - 99}, {-dataStart, SEEK_CUR, dataEnd - 99},
+ };
+ for (const auto& cfg : seeks) {
+ errno = 0;
+ auto off = lseek(fd, cfg.offset, cfg.whence);
+ ASSERT_EQ(cfg.ret, off) << "lseek(" << cfg.offset << ", " << cfg.whence << ") failed"
+ << (errno ? ": " : "") << (errno ? strerror(errno) : "");
+
+ if (off >= dataStart && off < dataEnd) {
+ off_t dataOff = off - dataStart;
+ ssize_t readSize = dataSize - dataOff;
+ uint8_t buf[readSize];
+
+ ASSERT_EQ(readSize, TEMP_FAILURE_RETRY(read(fd, buf, readSize)));
+ EXPECT_EQ(0, memcmp(buf, data + dataOff, readSize));
+ }
+ }
+
+ EXPECT_EQ(0, munmap(region, dataSize));
+}
+
TEST(AshmemTest, ProtTest) {
unique_fd fd;
constexpr size_t size = PAGE_SIZE;
@@ -108,13 +170,25 @@
ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ));
TestProtDenied(fd, size, PROT_WRITE);
+ TestProtIs(fd, PROT_READ);
ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, ®ion));
EXPECT_EQ(0, munmap(region, size));
ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_WRITE));
TestProtDenied(fd, size, PROT_READ);
+ TestProtIs(fd, PROT_WRITE);
ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_WRITE, ®ion));
EXPECT_EQ(0, munmap(region, size));
+
+ ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
+ TestProtIs(fd, PROT_READ | PROT_WRITE);
+ ASSERT_EQ(0, ashmem_set_prot_region(fd, PROT_READ));
+ errno = 0;
+ ASSERT_EQ(-1, ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE))
+ << "kernel shouldn't allow adding protection bits";
+ EXPECT_EQ(EINVAL, errno);
+ TestProtIs(fd, PROT_READ);
+ TestProtDenied(fd, size, PROT_WRITE);
}
TEST(AshmemTest, ForkProtTest) {
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
new file mode 100644
index 0000000..391adb6
--- /dev/null
+++ b/libcutils/tests/fs_config.cpp
@@ -0,0 +1,248 @@
+/*
+ * 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 <inttypes.h>
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
+
+extern const fs_path_config* __for_testing_only__android_dirs;
+extern const fs_path_config* __for_testing_only__android_files;
+extern bool (*__for_testing_only__fs_config_cmp)(bool, const char*, size_t, const char*, size_t);
+
+// Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we
+// hit a nullptr termination, before we declare the list is just too big or
+// could be missing the nullptr.
+static constexpr size_t max_idx = 4096;
+
+static const struct fs_config_cmp_test {
+ bool dir;
+ const char* prefix;
+ const char* path;
+ bool match;
+} fs_config_cmp_tests[] = {
+ // clang-format off
+ { true, "system/lib", "system/lib/hw", true },
+ { true, "vendor/lib", "system/vendor/lib/hw", true },
+ { true, "system/vendor/lib", "vendor/lib/hw", true },
+ { true, "system/vendor/lib", "system/vendor/lib/hw", true },
+ { false, "vendor/bin/wifi", "system/vendor/bin/w", false },
+ { false, "vendor/bin/wifi", "system/vendor/bin/wifi", true },
+ { false, "vendor/bin/wifi", "system/vendor/bin/wifi2", false },
+ { false, "system/vendor/bin/wifi", "system/vendor/bin/wifi", true, },
+ { false, "odm/bin/wifi", "system/odm/bin/wifi", true },
+ { false, "oem/bin/wifi", "system/oem/bin/wifi", true },
+ { false, "data/bin/wifi", "system/data/bin/wifi", false },
+ { false, "system/bin/*", "system/bin/wifi", true },
+ { false, "vendor/bin/*", "system/vendor/bin/wifi", true },
+ { false, "system/bin/*", "system/bin", false },
+ { false, "system/vendor/bin/*", "vendor/bin/wifi", true },
+ { false, NULL, NULL, false },
+ // clang-format on
+};
+
+static bool check_unique(std::vector<const char*>& paths, const std::string& config_name,
+ const std::string& prefix) {
+ bool retval = false;
+
+ std::string alternate = "system/" + prefix;
+
+ for (size_t idx = 0; idx < paths.size(); ++idx) {
+ size_t second;
+ std::string path(paths[idx]);
+ // check if there are multiple identical paths
+ for (second = idx + 1; second < paths.size(); ++second) {
+ if (path == paths[second]) {
+ GTEST_LOG_(ERROR) << "duplicate paths in " << config_name << ": " << paths[idx];
+ retval = true;
+ break;
+ }
+ }
+
+ // check if path is <partition>/
+ if (android::base::StartsWith(path, prefix.c_str())) {
+ // rebuild path to be system/<partition>/... to check for alias
+ path = alternate + path.substr(prefix.size());
+ for (second = 0; second < paths.size(); ++second) {
+ if (path == paths[second]) {
+ GTEST_LOG_(ERROR) << "duplicate alias paths in " << config_name << ": "
+ << paths[idx] << " and " << paths[second]
+ << " (remove latter)";
+ retval = true;
+ break;
+ }
+ }
+ continue;
+ }
+
+ // check if path is system/<partition>/
+ if (android::base::StartsWith(path, alternate.c_str())) {
+ // rebuild path to be <partition>/... to check for alias
+ path = prefix + path.substr(alternate.size());
+ for (second = 0; second < paths.size(); ++second) {
+ if (path == paths[second]) break;
+ }
+ if (second >= paths.size()) {
+ GTEST_LOG_(ERROR) << "replace path in " << config_name << ": " << paths[idx]
+ << " with " << path;
+ retval = true;
+ }
+ }
+ }
+ return retval;
+}
+
+static bool check_unique(const fs_path_config* paths, const char* type_name,
+ const std::string& prefix) {
+ std::string config("system/core/libcutils/fs_config.cpp:android_");
+ config += type_name;
+ config += "[]";
+
+ bool retval = false;
+ std::vector<const char*> paths_tmp;
+ for (size_t idx = 0; paths[idx].prefix; ++idx) {
+ if (idx > max_idx) {
+ GTEST_LOG_(WARNING) << config << ": has no end (missing null prefix)";
+ retval = true;
+ break;
+ }
+ paths_tmp.push_back(paths[idx].prefix);
+ }
+
+ return check_unique(paths_tmp, config, prefix) || retval;
+}
+
+static bool check_fs_config_cmp(const fs_config_cmp_test* tests) {
+ bool match, retval = false;
+ for (size_t idx = 0; tests[idx].prefix; ++idx) {
+ match = __for_testing_only__fs_config_cmp(tests[idx].dir, tests[idx].prefix,
+ strlen(tests[idx].prefix), tests[idx].path,
+ strlen(tests[idx].path));
+ if (match != tests[idx].match) {
+ GTEST_LOG_(ERROR) << tests[idx].path << (match ? " matched " : " didn't match ")
+ << tests[idx].prefix;
+ retval = true;
+ break;
+ }
+ }
+ return retval;
+}
+
+#define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field))
+
+static bool check_unique(const std::string& config, const std::string& prefix) {
+ int retval = false;
+
+ std::string data;
+ if (!android::base::ReadFileToString(config, &data)) return retval;
+
+ const fs_path_config_from_file* pc =
+ reinterpret_cast<const fs_path_config_from_file*>(data.c_str());
+ size_t len = data.size();
+
+ std::vector<const char*> paths_tmp;
+ size_t entry_number = 0;
+ while (len > 0) {
+ uint16_t host_len = (len >= endof(pc, len)) ? pc->len : INT16_MAX;
+ if (host_len > len) {
+ GTEST_LOG_(WARNING) << config << ": truncated at entry " << entry_number << " ("
+ << host_len << " > " << len << ")";
+ const std::string unknown("?");
+ GTEST_LOG_(WARNING)
+ << config << ": entry[" << entry_number << "]={ "
+ << "len=" << ((len >= endof(pc, len))
+ ? android::base::StringPrintf("%" PRIu16, pc->len)
+ : unknown)
+ << ", mode=" << ((len >= endof(pc, mode))
+ ? android::base::StringPrintf("0%" PRIo16, pc->mode)
+ : unknown)
+ << ", uid=" << ((len >= endof(pc, uid))
+ ? android::base::StringPrintf("%" PRIu16, pc->uid)
+ : unknown)
+ << ", gid=" << ((len >= endof(pc, gid))
+ ? android::base::StringPrintf("%" PRIu16, pc->gid)
+ : unknown)
+ << ", capabilities="
+ << ((len >= endof(pc, capabilities))
+ ? android::base::StringPrintf("0x%" PRIx64, pc->capabilities)
+ : unknown)
+ << ", prefix="
+ << ((len >= offsetof(fs_path_config_from_file, prefix))
+ ? android::base::StringPrintf(
+ "\"%.*s...", (int)(len - offsetof(fs_path_config_from_file, prefix)),
+ pc->prefix)
+ : unknown)
+ << " }";
+ retval = true;
+ break;
+ }
+ paths_tmp.push_back(pc->prefix);
+
+ pc = reinterpret_cast<const fs_path_config_from_file*>(reinterpret_cast<const char*>(pc) +
+ host_len);
+ len -= host_len;
+ ++entry_number;
+ }
+
+ return check_unique(paths_tmp, config, prefix) || retval;
+}
+
+void check_two(const fs_path_config* paths, const char* type_name, const char* prefix) {
+ ASSERT_FALSE(paths == nullptr);
+ ASSERT_FALSE(type_name == nullptr);
+ ASSERT_FALSE(prefix == nullptr);
+ bool check_internal = check_unique(paths, type_name, prefix);
+ EXPECT_FALSE(check_internal);
+ bool check_overrides =
+ check_unique(std::string("/") + prefix + "etc/fs_config_" + type_name, prefix);
+ EXPECT_FALSE(check_overrides);
+}
+
+TEST(fs_config, vendor_dirs_alias) {
+ check_two(__for_testing_only__android_dirs, "dirs", "vendor/");
+}
+
+TEST(fs_config, vendor_files_alias) {
+ check_two(__for_testing_only__android_files, "files", "vendor/");
+}
+
+TEST(fs_config, oem_dirs_alias) {
+ check_two(__for_testing_only__android_dirs, "dirs", "oem/");
+}
+
+TEST(fs_config, oem_files_alias) {
+ check_two(__for_testing_only__android_files, "files", "oem/");
+}
+
+TEST(fs_config, odm_dirs_alias) {
+ check_two(__for_testing_only__android_dirs, "dirs", "odm/");
+}
+
+TEST(fs_config, odm_files_alias) {
+ check_two(__for_testing_only__android_files, "files", "odm/");
+}
+
+TEST(fs_config, system_alias) {
+ EXPECT_FALSE(check_fs_config_cmp(fs_config_cmp_tests));
+}
diff --git a/libcutils/trace-container.c b/libcutils/trace-container.c
new file mode 100644
index 0000000..03e91b1
--- /dev/null
+++ b/libcutils/trace-container.c
@@ -0,0 +1,231 @@
+/*
+ * 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 "trace-dev.inc"
+
+#include <cutils/sockets.h>
+#include <sys/stat.h>
+#include <time.h>
+
+/**
+ * For tracing in container, tags are written into a socket
+ * instead of ftrace. Additional data is appended so we need extra space.
+ */
+#define CONTAINER_ATRACE_MESSAGE_LENGTH (ATRACE_MESSAGE_LENGTH + 512)
+
+static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
+
+// Variables used for tracing in container with socket.
+// Note that we need to manually close and reopen socket when Zygote is forking. This requires
+// writing and closing sockets on multiple threads. A rwlock is used for avoiding concurrent
+// operation on the file descriptor.
+static bool atrace_use_container_sock = false;
+static int atrace_container_sock_fd = -1;
+static pthread_mutex_t atrace_enabling_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_rwlock_t atrace_container_sock_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+static bool atrace_init_container_sock()
+{
+ pthread_rwlock_wrlock(&atrace_container_sock_rwlock);
+ atrace_container_sock_fd =
+ socket_local_client("trace", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
+ if (atrace_container_sock_fd < 0) {
+ ALOGE("Error opening container trace socket: %s (%d)", strerror(errno), errno);
+ }
+ pthread_rwlock_unlock(&atrace_container_sock_rwlock);
+ return atrace_container_sock_fd != -1;
+}
+
+static void atrace_close_container_sock()
+{
+ pthread_rwlock_wrlock(&atrace_container_sock_rwlock);
+ if (atrace_container_sock_fd != -1) close(atrace_container_sock_fd);
+ atrace_container_sock_fd = -1;
+ pthread_rwlock_unlock(&atrace_container_sock_rwlock);
+}
+
+// Set whether tracing is enabled in this process. This is used to prevent
+// the Zygote process from tracing. We need to close the socket in the container when tracing is
+// disabled, and reopen it again after Zygote forking.
+void atrace_set_tracing_enabled(bool enabled)
+{
+ pthread_mutex_lock(&atrace_enabling_mutex);
+ if (atrace_use_container_sock) {
+ bool already_enabled = atomic_load_explicit(&atrace_is_enabled, memory_order_acquire);
+ if (enabled && !already_enabled) {
+ // Trace was disabled previously. Re-initialize container socket.
+ atrace_init_container_sock();
+ } else if (!enabled && already_enabled) {
+ // Trace was enabled previously. Close container socket.
+ atrace_close_container_sock();
+ }
+ }
+ atomic_store_explicit(&atrace_is_enabled, enabled, memory_order_release);
+ pthread_mutex_unlock(&atrace_enabling_mutex);
+ atrace_update_tags();
+}
+
+static void atrace_init_once()
+{
+ atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+ if (atrace_marker_fd < 0) {
+ // We're in container, ftrace may be disabled. In such case, we use the
+ // socket to write trace event.
+
+ // Protect the initialization of container socket from
+ // atrace_set_tracing_enabled.
+ pthread_mutex_lock(&atrace_enabling_mutex);
+ atrace_use_container_sock = true;
+ bool success = false;
+ if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
+ success = atrace_init_container_sock();
+ }
+ pthread_mutex_unlock(&atrace_enabling_mutex);
+
+ if (!success) {
+ atrace_enabled_tags = 0;
+ goto done;
+ }
+ }
+ atrace_enabled_tags = atrace_get_property();
+
+done:
+ atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
+}
+
+void atrace_setup()
+{
+ pthread_once(&atrace_once_control, atrace_init_once);
+}
+
+static inline uint64_t gettime(clockid_t clk_id)
+{
+ struct timespec ts;
+ clock_gettime(clk_id, &ts);
+ return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
+}
+
+// Write trace events to container trace file. Note that we need to amend tid and time information
+// here comparing to normal ftrace, where those informations are added by kernel.
+#define WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value) { \
+ char buf[CONTAINER_ATRACE_MESSAGE_LENGTH]; \
+ int pid = getpid(); \
+ int tid = gettid(); \
+ uint64_t ts = gettime(CLOCK_MONOTONIC); \
+ uint64_t tts = gettime(CLOCK_THREAD_CPUTIME_ID); \
+ int len = snprintf( \
+ buf, sizeof(buf), \
+ ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s" value_format, \
+ pid, tid, ts, tts, name, value); \
+ if (len >= (int) sizeof(buf)) { \
+ int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
+ /* Truncate the name to make the message fit. */ \
+ if (name_len > 0) { \
+ ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
+ len = snprintf( \
+ buf, sizeof(buf), \
+ ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%.*s" value_format, \
+ pid, tid, ts, tts, name_len, name, value); \
+ } else { \
+ /* Data is still too long. Drop it. */ \
+ ALOGW("Data is too long in %s: %s\n", __FUNCTION__, name); \
+ len = 0; \
+ } \
+ } \
+ if (len > 0) { \
+ write(atrace_container_sock_fd, buf, len); \
+ } \
+}
+
+#define WRITE_MSG_IN_CONTAINER(ph, sep_before_name, value_format, name, value) { \
+ pthread_rwlock_rdlock(&atrace_container_sock_rwlock); \
+ if (atrace_container_sock_fd != -1) { \
+ WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value); \
+ } \
+ pthread_rwlock_unlock(&atrace_container_sock_rwlock); \
+}
+
+void atrace_begin_body(const char* name)
+{
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("B", "|", "%s", name, "");
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("B|%d|", "%s", name, "");
+}
+
+void atrace_end_body()
+{
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("E", "", "%s", "", "");
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("E|%d", "%s", "", "");
+}
+
+void atrace_async_begin_body(const char* name, int32_t cookie)
+{
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("S", "|", "|%d", name, cookie);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
+}
+
+void atrace_async_end_body(const char* name, int32_t cookie)
+{
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("F", "|", "|%d", name, cookie);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
+}
+
+void atrace_int_body(const char* name, int32_t value)
+{
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId32, name, value);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("C|%d|", "|%" PRId32, name, value);
+}
+
+void atrace_int64_body(const char* name, int64_t value)
+{
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId64, name, value);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("C|%d|", "|%" PRId64, name, value);
+}
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
index d45e5a9..4468e83 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.c
@@ -14,47 +14,9 @@
* limitations under the License.
*/
-#define LOG_TAG "cutils-trace"
+#include "trace-dev.inc"
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <pthread.h>
-#include <stdatomic.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <cutils/compiler.h>
-#include <cutils/properties.h>
-#include <cutils/trace.h>
-#include <log/log.h>
-#include <log/log_properties.h>
-
-/**
- * Maximum size of a message that can be logged to the trace buffer.
- * Note this message includes a tag, the pid, and the string given as the name.
- * Names should be kept short to get the most use of the trace buffer.
- */
-#define ATRACE_MESSAGE_LENGTH 1024
-
-atomic_bool atrace_is_ready = ATOMIC_VAR_INIT(false);
-int atrace_marker_fd = -1;
-uint64_t atrace_enabled_tags = ATRACE_TAG_NOT_READY;
-static bool atrace_is_debuggable = false;
-static atomic_bool atrace_is_enabled = ATOMIC_VAR_INIT(true);
-static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
-static pthread_mutex_t atrace_tags_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-// Set whether this process is debuggable, which determines whether
-// application-level tracing is allowed when the ro.debuggable system property
-// is not set to '1'.
-void atrace_set_debuggable(bool debuggable)
-{
- atrace_is_debuggable = debuggable;
- atrace_update_tags();
-}
+static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
// Set whether tracing is enabled in this process. This is used to prevent
// the Zygote process from tracing.
@@ -64,101 +26,6 @@
atrace_update_tags();
}
-// Check whether the given command line matches one of the comma-separated
-// values listed in the app_cmdlines property.
-static bool atrace_is_cmdline_match(const char* cmdline)
-{
- int count = property_get_int32("debug.atrace.app_number", 0);
-
- char buf[PROPERTY_KEY_MAX];
- char value[PROPERTY_VALUE_MAX];
-
- for (int i = 0; i < count; i++) {
- snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
- property_get(buf, value, "");
- if (strcmp(value, cmdline) == 0) {
- return true;
- }
- }
-
- return false;
-}
-
-// Determine whether application-level tracing is enabled for this process.
-static bool atrace_is_app_tracing_enabled()
-{
- bool sys_debuggable = __android_log_is_debuggable();
- bool result = false;
-
- if (sys_debuggable || atrace_is_debuggable) {
- // Check whether tracing is enabled for this process.
- FILE * file = fopen("/proc/self/cmdline", "re");
- if (file) {
- char cmdline[4096];
- if (fgets(cmdline, sizeof(cmdline), file)) {
- result = atrace_is_cmdline_match(cmdline);
- } else {
- ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
- }
- fclose(file);
- } else {
- ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
- errno);
- }
- }
-
- return result;
-}
-
-// Read the sysprop and return the value tags should be set to
-static uint64_t atrace_get_property()
-{
- char value[PROPERTY_VALUE_MAX];
- char *endptr;
- uint64_t tags;
-
- property_get("debug.atrace.tags.enableflags", value, "0");
- errno = 0;
- tags = strtoull(value, &endptr, 0);
- if (value[0] == '\0' || *endptr != '\0') {
- ALOGE("Error parsing trace property: Not a number: %s", value);
- return 0;
- } else if (errno == ERANGE || tags == ULLONG_MAX) {
- ALOGE("Error parsing trace property: Number too large: %s", value);
- return 0;
- }
-
- // Only set the "app" tag if this process was selected for app-level debug
- // tracing.
- if (atrace_is_app_tracing_enabled()) {
- tags |= ATRACE_TAG_APP;
- } else {
- tags &= ~ATRACE_TAG_APP;
- }
-
- return (tags | ATRACE_TAG_ALWAYS) & ATRACE_TAG_VALID_MASK;
-}
-
-// Update tags if tracing is ready. Useful as a sysprop change callback.
-void atrace_update_tags()
-{
- uint64_t tags;
- if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
- if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
- tags = atrace_get_property();
- pthread_mutex_lock(&atrace_tags_mutex);
- atrace_enabled_tags = tags;
- pthread_mutex_unlock(&atrace_tags_mutex);
- } else {
- // Tracing is disabled for this process, so we simply don't
- // initialize the tags.
- pthread_mutex_lock(&atrace_tags_mutex);
- atrace_enabled_tags = ATRACE_TAG_NOT_READY;
- pthread_mutex_unlock(&atrace_tags_mutex);
- }
- }
-}
-
static void atrace_init_once()
{
atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
@@ -181,54 +48,30 @@
void atrace_begin_body(const char* name)
{
- char buf[ATRACE_MESSAGE_LENGTH];
-
- int len = snprintf(buf, sizeof(buf), "B|%d|%s", getpid(), name);
- if (len >= (int) sizeof(buf)) {
- ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name);
- len = sizeof(buf) - 1;
- }
- write(atrace_marker_fd, buf, len);
+ WRITE_MSG("B|%d|", "%s", name, "");
}
void atrace_end_body()
{
- char c = 'E';
- write(atrace_marker_fd, &c, 1);
-}
-
-#define WRITE_MSG(format_begin, format_end, pid, name, value) { \
- char buf[ATRACE_MESSAGE_LENGTH]; \
- int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
- name, value); \
- if (len >= (int) sizeof(buf)) { \
- /* Given the sizeof(buf), and all of the current format buffers, \
- * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
- int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
- /* Truncate the name to make the message fit. */ \
- ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
- len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
- name_len, name, value); \
- } \
- write(atrace_marker_fd, buf, len); \
+ WRITE_MSG("E|%d", "%s", "", "");
}
void atrace_async_begin_body(const char* name, int32_t cookie)
{
- WRITE_MSG("S|%d|", "|%" PRId32, getpid(), name, cookie);
+ WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
}
void atrace_async_end_body(const char* name, int32_t cookie)
{
- WRITE_MSG("F|%d|", "|%" PRId32, getpid(), name, cookie);
+ WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
}
void atrace_int_body(const char* name, int32_t value)
{
- WRITE_MSG("C|%d|", "|%" PRId32, getpid(), name, value);
+ WRITE_MSG("C|%d|", "|%" PRId32, name, value);
}
void atrace_int64_body(const char* name, int64_t value)
{
- WRITE_MSG("C|%d|", "|%" PRId64, getpid(), name, value);
+ WRITE_MSG("C|%d|", "|%" PRId64, name, value);
}
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
new file mode 100644
index 0000000..f32330a
--- /dev/null
+++ b/libcutils/trace-dev.inc
@@ -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.
+ */
+
+#ifndef __TRACE_DEV_INC
+#define __TRACE_DEV_INC
+
+#define LOG_TAG "cutils-trace"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <cutils/trace.h>
+#include <log/log.h>
+#include <log/log_properties.h>
+
+/**
+ * Maximum size of a message that can be logged to the trace buffer.
+ * Note this message includes a tag, the pid, and the string given as the name.
+ * Names should be kept short to get the most use of the trace buffer.
+ */
+#define ATRACE_MESSAGE_LENGTH 1024
+
+atomic_bool atrace_is_ready = ATOMIC_VAR_INIT(false);
+int atrace_marker_fd = -1;
+uint64_t atrace_enabled_tags = ATRACE_TAG_NOT_READY;
+static bool atrace_is_debuggable = false;
+static atomic_bool atrace_is_enabled = ATOMIC_VAR_INIT(true);
+static pthread_mutex_t atrace_tags_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// Set whether this process is debuggable, which determines whether
+// application-level tracing is allowed when the ro.debuggable system property
+// is not set to '1'.
+void atrace_set_debuggable(bool debuggable)
+{
+ atrace_is_debuggable = debuggable;
+ atrace_update_tags();
+}
+
+// Check whether the given command line matches one of the comma-separated
+// values listed in the app_cmdlines property.
+static bool atrace_is_cmdline_match(const char* cmdline)
+{
+ int count = property_get_int32("debug.atrace.app_number", 0);
+
+ char buf[PROPERTY_KEY_MAX];
+ char value[PROPERTY_VALUE_MAX];
+
+ for (int i = 0; i < count; i++) {
+ snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
+ property_get(buf, value, "");
+ if (strcmp(value, cmdline) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Determine whether application-level tracing is enabled for this process.
+static bool atrace_is_app_tracing_enabled()
+{
+ bool sys_debuggable = __android_log_is_debuggable();
+ bool result = false;
+
+ if (sys_debuggable || atrace_is_debuggable) {
+ // Check whether tracing is enabled for this process.
+ FILE * file = fopen("/proc/self/cmdline", "re");
+ if (file) {
+ char cmdline[4096];
+ if (fgets(cmdline, sizeof(cmdline), file)) {
+ result = atrace_is_cmdline_match(cmdline);
+ } else {
+ ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
+ }
+ fclose(file);
+ } else {
+ ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
+ errno);
+ }
+ }
+
+ return result;
+}
+
+// Read the sysprop and return the value tags should be set to
+static uint64_t atrace_get_property()
+{
+ char value[PROPERTY_VALUE_MAX];
+ char *endptr;
+ uint64_t tags;
+
+ property_get("debug.atrace.tags.enableflags", value, "0");
+ errno = 0;
+ tags = strtoull(value, &endptr, 0);
+ if (value[0] == '\0' || *endptr != '\0') {
+ ALOGE("Error parsing trace property: Not a number: %s", value);
+ return 0;
+ } else if (errno == ERANGE || tags == ULLONG_MAX) {
+ ALOGE("Error parsing trace property: Number too large: %s", value);
+ return 0;
+ }
+
+ // Only set the "app" tag if this process was selected for app-level debug
+ // tracing.
+ if (atrace_is_app_tracing_enabled()) {
+ tags |= ATRACE_TAG_APP;
+ } else {
+ tags &= ~ATRACE_TAG_APP;
+ }
+
+ return (tags | ATRACE_TAG_ALWAYS) & ATRACE_TAG_VALID_MASK;
+}
+
+// Update tags if tracing is ready. Useful as a sysprop change callback.
+void atrace_update_tags()
+{
+ uint64_t tags;
+ if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
+ if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
+ tags = atrace_get_property();
+ pthread_mutex_lock(&atrace_tags_mutex);
+ atrace_enabled_tags = tags;
+ pthread_mutex_unlock(&atrace_tags_mutex);
+ } else {
+ // Tracing is disabled for this process, so we simply don't
+ // initialize the tags.
+ pthread_mutex_lock(&atrace_tags_mutex);
+ atrace_enabled_tags = ATRACE_TAG_NOT_READY;
+ pthread_mutex_unlock(&atrace_tags_mutex);
+ }
+ }
+}
+
+#define WRITE_MSG(format_begin, format_end, name, value) { \
+ char buf[ATRACE_MESSAGE_LENGTH]; \
+ int pid = getpid(); \
+ int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
+ name, value); \
+ if (len >= (int) sizeof(buf)) { \
+ /* Given the sizeof(buf), and all of the current format buffers, \
+ * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
+ int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
+ /* Truncate the name to make the message fit. */ \
+ ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
+ len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
+ name_len, name, value); \
+ } \
+ write(atrace_marker_fd, buf, len); \
+}
+
+#endif // __TRACE_DEV_INC
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index 088981a..23a5c79 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -1,6 +1,9 @@
cc_library {
name: "libdiskconfig",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"diskconfig.c",
"diskutils.c",
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index 54bfee5..cf03868 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -14,6 +14,7 @@
cc_library_static {
name: "libgrallocusage",
+ vendor_available: true,
cppflags: [
"-Weverything",
"-Werror",
@@ -26,4 +27,5 @@
srcs: ["GrallocUsageConversion.cpp"],
export_include_dirs: ["include"],
shared_libs: ["android.hardware.graphics.allocator@2.0"],
+ header_libs: ["libhardware_headers"],
}
diff --git a/libion/Android.bp b/libion/Android.bp
index 6f267e4..6d9fae0 100644
--- a/libion/Android.bp
+++ b/libion/Android.bp
@@ -1,7 +1,11 @@
cc_library {
name: "libion",
- vendor_available: true,
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
srcs: ["ion.c"],
shared_libs: ["liblog"],
local_include_dirs: [
diff --git a/libion/tests/Android.bp b/libion/tests/Android.bp
index 4428848..b3fcb3b 100644
--- a/libion/tests/Android.bp
+++ b/libion/tests/Android.bp
@@ -16,7 +16,6 @@
cc_test {
name: "ion-unit-tests",
- clang: true,
cflags: [
"-g",
"-Wall",
diff --git a/libkeyutils/.clang-format b/libkeyutils/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libkeyutils/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
new file mode 100644
index 0000000..0285259
--- /dev/null
+++ b/libkeyutils/Android.bp
@@ -0,0 +1,16 @@
+cc_library {
+ name: "libkeyutils",
+ cflags: ["-Werror"],
+ defaults: ["linux_bionic_supported"],
+ export_include_dirs: ["include/"],
+ local_include_dirs: ["include/"],
+ srcs: ["keyutils.cpp"],
+ stl: "none",
+}
+
+cc_test {
+ name: "libkeyutils-tests",
+ cflags: ["-Werror"],
+ shared_libs: ["libkeyutils"],
+ srcs: ["keyutils_test.cpp"],
+}
diff --git a/libkeyutils/include/keyutils.h b/libkeyutils/include/keyutils.h
new file mode 100644
index 0000000..585767d
--- /dev/null
+++ b/libkeyutils/include/keyutils.h
@@ -0,0 +1,56 @@
+/*
+ * 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 _KEYUTILS_H_
+#define _KEYUTILS_H_
+
+#include <linux/keyctl.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+typedef int32_t key_serial_t;
+
+key_serial_t add_key(const char* type, const char* description, const void* payload,
+ size_t payload_length, key_serial_t ring_id);
+
+key_serial_t keyctl_get_keyring_ID(key_serial_t id, int create);
+
+long keyctl_revoke(key_serial_t id); /* TODO: remove this */
+
+long keyctl_search(key_serial_t ring_id, const char* type, const char* description,
+ key_serial_t dest_ring_id);
+
+long keyctl_setperm(key_serial_t id, int permissions);
+
+long keyctl_unlink(key_serial_t key, key_serial_t keyring);
+
+__END_DECLS
+
+#endif
diff --git a/libkeyutils/keyutils.cpp b/libkeyutils/keyutils.cpp
new file mode 100644
index 0000000..58a2a17
--- /dev/null
+++ b/libkeyutils/keyutils.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#include <keyutils.h>
+
+#include <stdarg.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+// Deliberately not exposed. Callers should use the typed APIs instead.
+static long keyctl(int cmd, ...) {
+ va_list va;
+ va_start(va, cmd);
+ unsigned long arg2 = va_arg(va, unsigned long);
+ unsigned long arg3 = va_arg(va, unsigned long);
+ unsigned long arg4 = va_arg(va, unsigned long);
+ unsigned long arg5 = va_arg(va, unsigned long);
+ va_end(va);
+ return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
+}
+
+key_serial_t add_key(const char* type, const char* description, const void* payload,
+ size_t payload_length, key_serial_t ring_id) {
+ return syscall(__NR_add_key, type, description, payload, payload_length, ring_id);
+}
+
+key_serial_t keyctl_get_keyring_ID(key_serial_t id, int create) {
+ return keyctl(KEYCTL_GET_KEYRING_ID, id, create);
+}
+
+long keyctl_revoke(key_serial_t id) {
+ return keyctl(KEYCTL_REVOKE, id);
+}
+
+long keyctl_search(key_serial_t ring_id, const char* type, const char* description,
+ key_serial_t dest_ring_id) {
+ return keyctl(KEYCTL_SEARCH, ring_id, type, description, dest_ring_id);
+}
+
+long keyctl_setperm(key_serial_t id, int permissions) {
+ return keyctl(KEYCTL_SETPERM, id, permissions);
+}
+
+long keyctl_unlink(key_serial_t key, key_serial_t keyring) {
+ return keyctl(KEYCTL_UNLINK, key, keyring);
+}
diff --git a/libkeyutils/keyutils_test.cpp b/libkeyutils/keyutils_test.cpp
new file mode 100644
index 0000000..d41c91b
--- /dev/null
+++ b/libkeyutils/keyutils_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#include <keyutils.h>
+
+#include <dlfcn.h>
+
+#include <gtest/gtest.h>
+
+TEST(keyutils, smoke) {
+ // Check that the exported type is sane.
+ ASSERT_EQ(4U, sizeof(key_serial_t));
+
+ // Check that all the functions actually exist.
+ ASSERT_TRUE(dlsym(nullptr, "add_key") != nullptr);
+ ASSERT_TRUE(dlsym(nullptr, "keyctl_get_keyring_ID") != nullptr);
+ ASSERT_TRUE(dlsym(nullptr, "keyctl_revoke") != nullptr);
+ ASSERT_TRUE(dlsym(nullptr, "keyctl_search") != nullptr);
+ ASSERT_TRUE(dlsym(nullptr, "keyctl_setperm") != nullptr);
+ ASSERT_TRUE(dlsym(nullptr, "keyctl_unlink") != nullptr);
+}
diff --git a/liblog/Android.bp b/liblog/Android.bp
index e74aa82..b98d18f 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,6 +42,24 @@
"logd_writer.c",
]
+cc_library_headers {
+ name: "liblog_headers",
+ host_supported: true,
+ vendor_available: true,
+ export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ vendor: {
+ export_include_dirs: ["include_vndk"],
+ },
+ },
+}
+
// Shared and static library for host and device
// ========================================================
cc_library {
@@ -81,7 +99,8 @@
},
},
- export_include_dirs: ["include"],
+ header_libs: ["liblog_headers"],
+ export_header_lib_headers: ["liblog_headers"],
cflags: [
"-Werror",
@@ -100,7 +119,7 @@
}
ndk_headers {
- name: "liblog_headers",
+ name: "liblog_ndk_headers",
from: "include/android",
to: "android",
srcs: ["include/android/log.h"],
diff --git a/liblog/event.logtags b/liblog/event.logtags
index 301e885..0a3b650 100644
--- a/liblog/event.logtags
+++ b/liblog/event.logtags
@@ -29,6 +29,7 @@
# 4: Number of allocations
# 5: Id
# 6: Percent
+# s: Number of seconds (monotonic time)
# Default value for data of type int/long is 2 (bytes).
#
# TODO: generate ".java" and ".h" files with integer constants from this file.
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 231f4d9..83064fd 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -230,9 +230,16 @@
return it->second;
}
+// The position after the end of a valid section of the tag string,
+// caller makes sure delimited appropriately.
+static const char* endOfTag(const char* cp) {
+ while (*cp && (isalnum(*cp) || strchr("_.-@,", *cp))) ++cp;
+ return cp;
+}
+
// Scan one tag line.
//
-// "*pData" should be pointing to the first digit in the tag number. On
+// "pData" should be pointing to the first digit in the tag number. On
// successful return, it will be pointing to the last character in the
// tag line (i.e. the character before the start of the next line).
//
@@ -242,10 +249,11 @@
// data and it will outlive the call.
//
// Returns 0 on success, nonzero on failure.
-static int scanTagLine(EventTagMap* map, char** pData, int lineNum) {
- char* cp;
- unsigned long val = strtoul(*pData, &cp, 10);
- if (cp == *pData) {
+static int scanTagLine(EventTagMap* map, const char*& pData, int lineNum) {
+ char* ep;
+ unsigned long val = strtoul(pData, &ep, 10);
+ const char* cp = ep;
+ if (cp == pData) {
if (lineNum) {
fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
}
@@ -274,14 +282,13 @@
}
const char* tag = cp;
- // Determine whether "c" is a valid tag char.
- while (isalnum(*++cp) || (*cp == '_')) {
- }
+ cp = endOfTag(cp);
size_t tagLen = cp - tag;
if (!isspace(*cp)) {
if (lineNum) {
- fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n", lineNum);
+ fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp,
+ lineNum);
}
errno = EINVAL;
return -1;
@@ -290,9 +297,9 @@
while (isspace(*cp) && (*cp != '\n')) ++cp;
const char* fmt = NULL;
size_t fmtLen = 0;
- if (*cp != '#') {
+ if (*cp && (*cp != '#')) {
fmt = cp;
- while ((*cp != '\n') && (*cp != '#')) ++cp;
+ while (*cp && (*cp != '\n') && (*cp != '#')) ++cp;
while ((cp > fmt) && isspace(*(cp - 1))) --cp;
fmtLen = cp - fmt;
}
@@ -302,7 +309,7 @@
// recorded for the same uid, but recording that
// unused detail in our database is too burdensome.
bool verbose = true;
- while ((*cp != '#') && (*cp != '\n')) ++cp;
+ while (*cp && (*cp != '#') && (*cp != '\n')) ++cp;
if (*cp == '#') {
do {
++cp;
@@ -310,11 +317,11 @@
verbose = !!fastcmp<strncmp>(cp, "uid=", strlen("uid="));
}
- while (*cp != '\n') ++cp;
+ while (*cp && (*cp != '\n')) ++cp;
#ifdef DEBUG
- fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - *pData), *pData);
+ fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - pData), pData);
#endif
- *pData = cp;
+ pData = cp;
if (lineNum) {
if (map->emplaceUnique(tagIndex,
@@ -342,9 +349,9 @@
// Parse the tags out of the file.
static int parseMapLines(EventTagMap* map, size_t which) {
- char* cp = static_cast<char*>(map->mapAddr[which]);
+ const char* cp = static_cast<char*>(map->mapAddr[which]);
size_t len = map->mapLen[which];
- char* endp = cp + len;
+ const char* endp = cp + len;
// insist on EOL at EOF; simplifies parsing and null-termination
if (!len || (*(endp - 1) != '\n')) {
@@ -371,7 +378,7 @@
lineStart = false;
} else if (isdigit(*cp)) {
// looks like a tag; scan it out
- if (scanTagLine(map, &cp, lineNum) != 0) {
+ if (scanTagLine(map, cp, lineNum) != 0) {
if (!which || (errno != EMLINK)) {
return -1;
}
@@ -446,7 +453,7 @@
mmap(NULL, end[which], which ? PROT_READ : PROT_READ | PROT_WRITE,
which ? MAP_SHARED : MAP_PRIVATE, fd[which], 0);
save_errno = errno;
- close(fd[which]);
+ close(fd[which]); /* fd DONE */
fd[which] = -1;
if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
(newTagMap->mapAddr[which] != NULL)) {
@@ -466,6 +473,7 @@
delete newTagMap;
return NULL;
}
+ /* See 'fd DONE' comments above and below, no need to clean up here */
}
return newTagMap;
@@ -474,7 +482,7 @@
save_errno = EINVAL;
delete newTagMap;
fail_close:
- for (which = 0; which < NUM_MAPS; ++which) close(fd[which]);
+ for (which = 0; which < NUM_MAPS; ++which) close(fd[which]); /* fd DONE */
fail_errno:
errno = save_errno;
return NULL;
@@ -495,14 +503,13 @@
int ret = asprintf(&buf, command_template, tag);
if (ret > 0) {
// Add some buffer margin for an estimate of the full return content.
- char* cp;
size_t size =
ret - strlen(command_template) +
strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
if (size > (size_t)ret) {
- cp = static_cast<char*>(realloc(buf, size));
- if (cp) {
- buf = cp;
+ char* np = static_cast<char*>(realloc(buf, size));
+ if (np) {
+ buf = np;
} else {
size = ret;
}
@@ -512,10 +519,12 @@
// Ask event log tag service for an existing entry
if (__send_log_msg(buf, size) >= 0) {
buf[size - 1] = '\0';
- unsigned long val = strtoul(buf, &cp, 10); // return size
+ char* ep;
+ unsigned long val = strtoul(buf, &ep, 10); // return size
+ const char* cp = ep;
if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
++cp;
- if (!scanTagLine(map, &cp, 0)) {
+ if (!scanTagLine(map, cp, 0)) {
free(buf);
return map->find(tag);
}
@@ -573,8 +582,9 @@
LIBLOG_ABI_PUBLIC int android_lookupEventTagNum(EventTagMap* map,
const char* tagname,
const char* format, int prio) {
- size_t len = strlen(tagname);
- if (!len) {
+ const char* ep = endOfTag(tagname);
+ size_t len = ep - tagname;
+ if (!len || *ep) {
errno = EINVAL;
return -1;
}
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index ae7a334..1483c24 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -15,9 +15,7 @@
*/
/*
* Intercepts log messages intended for the Android log device.
- * When running in the context of the simulator, the messages are
- * passed on to the underlying (fake) log device. When not in the
- * simulator, messages are printed to stderr.
+ * Messages are printed to stderr.
*/
#include <ctype.h>
#include <errno.h>
@@ -561,7 +559,8 @@
* tag (N bytes -- null-terminated ASCII string)
* message (N bytes -- null-terminated ASCII string)
*/
-static ssize_t logWritev(int fd, const struct iovec* vector, int count) {
+LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
+ int count) {
LogState* state;
/* Make sure that no-one frees the LogState while we're using it.
@@ -626,8 +625,18 @@
/*
* Free up our state and close the fake descriptor.
+ *
+ * The logger API has no means or need to 'stop' or 'close' using the logs,
+ * and as such, there is no way for that 'stop' or 'close' to translate into
+ * a close operation to the fake log handler. fakeLogClose is provided for
+ * completeness only.
+ *
+ * We have no intention of adding a log close operation as it would complicate
+ * every user of the logging API with no gain since the only valid place to
+ * call is in the exit handler. Logging can continue in the exit handler to
+ * help debug HOST tools ...
*/
-static int logClose(int fd) {
+LIBLOG_HIDDEN int fakeLogClose(int fd) {
deleteFakeFd(fd);
return 0;
}
@@ -635,7 +644,7 @@
/*
* Open a log output device and return a fake fd.
*/
-static int logOpen(const char* pathName, int flags __unused) {
+LIBLOG_HIDDEN int fakeLogOpen(const char* pathName) {
LogState* logState;
int fd = -1;
@@ -654,66 +663,6 @@
return fd;
}
-/*
- * Runtime redirection. If this binary is running in the simulator,
- * just pass log messages to the emulated device. If it's running
- * outside of the simulator, write the log messages to stderr.
- */
-
-static int (*redirectOpen)(const char* pathName, int flags) = NULL;
-static int (*redirectClose)(int fd) = NULL;
-static ssize_t (*redirectWritev)(int fd, const struct iovec* vector,
- int count) = NULL;
-
-static void setRedirects() {
- const char* ws;
-
- /* Wrapsim sets this environment variable on children that it's
- * created using its LD_PRELOAD wrapper.
- */
- ws = getenv("ANDROID_WRAPSIM");
- if (ws != NULL && strcmp(ws, "1") == 0) {
- /* We're running inside wrapsim, so we can just write to the device. */
- redirectOpen = (int (*)(const char* pathName, int flags))open;
- redirectClose = close;
- redirectWritev = writev;
- } else {
- /* There's no device to delegate to; handle the logging ourselves. */
- redirectOpen = logOpen;
- redirectClose = logClose;
- redirectWritev = logWritev;
- }
-}
-
-LIBLOG_HIDDEN int fakeLogOpen(const char* pathName, int flags) {
- if (redirectOpen == NULL) {
- setRedirects();
- }
- return redirectOpen(pathName, flags);
-}
-
-/*
- * The logger API has no means or need to 'stop' or 'close' using the logs,
- * and as such, there is no way for that 'stop' or 'close' to translate into
- * a close operation to the fake log handler. fakeLogClose is provided for
- * completeness only.
- *
- * We have no intention of adding a log close operation as it would complicate
- * every user of the logging API with no gain since the only valid place to
- * call is in the exit handler. Logging can continue in the exit handler to
- * help debug HOST tools ...
- */
-LIBLOG_HIDDEN int fakeLogClose(int fd) {
- /* Assume that open() was called first. */
- return redirectClose(fd);
-}
-
-LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
- int count) {
- /* Assume that open() was called first. */
- return redirectWritev(fd, vector, count);
-}
-
LIBLOG_HIDDEN ssize_t __send_log_msg(char* buf __unused,
size_t buf_size __unused) {
return -ENODEV;
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index 462d026..7b0e745 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -23,7 +23,7 @@
struct iovec;
-LIBLOG_HIDDEN int fakeLogOpen(const char* pathName, int flags);
+LIBLOG_HIDDEN int fakeLogOpen(const char* pathName);
LIBLOG_HIDDEN int fakeLogClose(int fd);
LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
int count);
diff --git a/liblog/fake_writer.c b/liblog/fake_writer.c
index 969661a..403dc72 100644
--- a/liblog/fake_writer.c
+++ b/liblog/fake_writer.c
@@ -55,9 +55,9 @@
continue;
}
snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
- logFds[i] = fakeLogOpen(buf, O_WRONLY);
+ logFds[i] = fakeLogOpen(buf);
if (logFds[i] < 0) {
- fprintf(stderr, "fakeLogOpen(%s, O_WRONLY) failed\n", buf);
+ fprintf(stderr, "fakeLogOpen(%s) failed\n", buf);
}
}
return 0;
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 6758c84..d01708d 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -32,6 +32,7 @@
#include <log/log_main.h>
#include <log/log_radio.h>
#include <log/log_read.h>
+#include <log/log_safetynet.h>
#include <log/log_system.h>
#include <log/log_time.h>
#include <log/uio.h> /* helper to define iovec for portability */
@@ -160,38 +161,13 @@
#endif
#if __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
-clockid_t android_log_clockid();
+clockid_t android_log_clockid(void);
#endif
#endif /* __linux__ */
/* --------------------------------------------------------------------- */
-#ifndef _ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
-
-#define android_errorWriteLog(tag, subTag) \
- __android_log_error_write(tag, subTag, -1, NULL, 0)
-
-#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
- __android_log_error_write(tag, subTag, uid, data, dataLen)
-
-int __android_log_error_write(int tag, const char* subTag, int32_t uid,
- const char* data, uint32_t dataLen);
-
-#endif /* __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE */
-
-/* --------------------------------------------------------------------- */
-
#ifndef __ANDROID_USE_LIBLOG_CLOSE_INTERFACE
#ifndef __ANDROID_API__
#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 1
@@ -209,7 +185,7 @@
* May be used to clean up File descriptors after a Fork, the resources are
* all O_CLOEXEC so wil self clean on exec().
*/
-void __android_log_close();
+void __android_log_close(void);
#endif
#ifndef __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index 55953fc..bb1ce34 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -17,6 +17,7 @@
#ifndef _LIBS_LOG_EVENT_LIST_H
#define _LIBS_LOG_EVENT_LIST_H
+#include <errno.h>
#include <stdint.h>
#if (defined(__cplusplus) && defined(_USING_LIBCXX))
@@ -148,6 +149,7 @@
return ctx;
}
+ /* return errors or transmit status */
int status() const {
return ret;
}
@@ -175,6 +177,12 @@
return *this;
}
+ android_log_event_list& operator<<(bool value) {
+ int retval = android_log_write_int32(ctx, value ? 1 : 0);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
android_log_event_list& operator<<(int64_t value) {
int retval = android_log_write_int64(ctx, value);
if (retval < 0) ret = retval;
@@ -209,14 +217,16 @@
}
int write(log_id_t id = LOG_ID_EVENTS) {
+ /* facilitate -EBUSY retry */
+ if ((ret == -EBUSY) || (ret > 0)) ret = 0;
int retval = android_log_write_list(ctx, id);
- if (retval < 0) ret = retval;
+ /* existing errors trump transmission errors */
+ if (!ret) ret = retval;
return ret;
}
int operator<<(log_id_t id) {
- int retval = android_log_write_list(ctx, id);
- if (retval < 0) ret = retval;
+ write(id);
android_log_destroy(&ctx);
return ret;
}
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index da16158..339a06d 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -18,10 +18,9 @@
#define _LIBS_LOG_LOG_MAIN_H
#include <android/log.h>
+#include <sys/cdefs.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+__BEGIN_DECLS
/*
* Normally we strip the effects of ALOGV (VERBOSE messages),
@@ -355,11 +354,11 @@
#if LOG_NDEBUG /* Production */
#define android_testLog(prio, tag) \
- (__android_log_is_loggable_len(prio, tag, (tag && *tag) ? strlen(tag) : 0, \
+ (__android_log_is_loggable_len(prio, tag, ((tag) && *(tag)) ? strlen(tag) : 0, \
ANDROID_LOG_DEBUG) != 0)
#else
#define android_testLog(prio, tag) \
- (__android_log_is_loggable_len(prio, tag, (tag && *tag) ? strlen(tag) : 0, \
+ (__android_log_is_loggable_len(prio, tag, ((tag) && *(tag)) ? strlen(tag) : 0, \
ANDROID_LOG_VERBOSE) != 0)
#endif
@@ -385,8 +384,6 @@
#pragma clang diagnostic pop
#endif
-#ifdef __cplusplus
-}
-#endif
+__END_DECLS
#endif /* _LIBS_LOG_LOG_MAIN_H */
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
new file mode 100644
index 0000000..b8ca475
--- /dev/null
+++ b/liblog/include/log/log_safetynet.h
@@ -0,0 +1,44 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** This file is dual licensed. It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#ifndef _LIBS_LOG_SAFETYNET_H
+#define _LIBS_LOG_SAFETYNET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
+#elif __ANDROID_API__ > 22 /* > Lollipop */
+#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
+
+#define android_errorWriteLog(tag, subTag) \
+ __android_log_error_write(tag, subTag, -1, NULL, 0)
+
+#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
+ __android_log_error_write(tag, subTag, uid, data, dataLen)
+
+int __android_log_error_write(int tag, const char* subTag, int32_t uid,
+ const char* data, uint32_t dataLen);
+
+#endif /* __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_SAFETYNET_H */
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 9ece0b3..3764faf 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -22,6 +22,8 @@
/* struct log_time is a wire-format variant of struct timespec */
#define NS_PER_SEC 1000000000ULL
+#define US_PER_SEC 1000000ULL
+#define MS_PER_SEC 1000ULL
#ifndef __struct_log_time_defined
#define __struct_log_time_defined
@@ -148,6 +150,14 @@
uint64_t nsec() const {
return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
}
+ uint64_t usec() const {
+ return static_cast<uint64_t>(tv_sec) * US_PER_SEC +
+ tv_nsec / (NS_PER_SEC / US_PER_SEC);
+ }
+ uint64_t msec() const {
+ return static_cast<uint64_t>(tv_sec) * MS_PER_SEC +
+ tv_nsec / (NS_PER_SEC / MS_PER_SEC);
+ }
#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
static const char default_format[];
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
index f93b377..a79beec 100644
--- a/liblog/include_vndk/log/log.h
+++ b/liblog/include_vndk/log/log.h
@@ -8,6 +8,8 @@
#include <log/log_main.h>
#include <log/log_radio.h>
#include <log/log_read.h>
+#include <log/log_safetynet.h>
+#include <log/log_system.h>
#include <log/log_time.h>
/*
diff --git a/liblog/include_vndk/log/log_safetynet.h b/liblog/include_vndk/log/log_safetynet.h
new file mode 120000
index 0000000..a4614e7
--- /dev/null
+++ b/liblog/include_vndk/log/log_safetynet.h
@@ -0,0 +1 @@
+../../include/log/log_safetynet.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_system.h b/liblog/include_vndk/log/log_system.h
new file mode 120000
index 0000000..d0d3904
--- /dev/null
+++ b/liblog/include_vndk/log/log_system.h
@@ -0,0 +1 @@
+../../include/log/log_system.h
\ No newline at end of file
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 58fb148..3c4c1f1 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -33,6 +33,7 @@
android_logger_get_prune_list; # vndk
android_logger_set_prune_list; # vndk
android_logger_get_statistics; # vndk
+ __android_log_error_write; # vndk
__android_log_is_loggable;
};
diff --git a/liblog/local_logger.c b/liblog/local_logger.c
index 522867d..563cb3f 100644
--- a/liblog/local_logger.c
+++ b/liblog/local_logger.c
@@ -222,6 +222,7 @@
log->last[logId] = node->prev;
}
list_remove(node);
+ LOG_ALWAYS_FATAL_IF(node == log->last[logId], "corrupted list");
free(e);
}
/* add entry to list */
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
index 600f4bb..603ba24 100644
--- a/liblog/logd_reader.c
+++ b/liblog/logd_reader.c
@@ -590,20 +590,30 @@
memset(log_msg, 0, sizeof(*log_msg));
+ unsigned int new_alarm = 0;
if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ if ((logger_list->mode & ANDROID_LOG_WRAP) &&
+ (logger_list->start.tv_sec || logger_list->start.tv_nsec)) {
+ /* b/64143705 */
+ new_alarm = (ANDROID_LOG_WRAP_DEFAULT_TIMEOUT * 11) / 10 + 10;
+ logger_list->mode &= ~ANDROID_LOG_WRAP;
+ } else {
+ new_alarm = 30;
+ }
+
memset(&ignore, 0, sizeof(ignore));
ignore.sa_handler = caught_signal;
sigemptyset(&ignore.sa_mask);
/* particularily useful if tombstone is reporting for logd */
sigaction(SIGALRM, &ignore, &old_sigaction);
- old_alarm = alarm(30);
+ old_alarm = alarm(new_alarm);
}
/* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
ret = recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0);
e = errno;
- if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+ if (new_alarm) {
if ((ret == 0) || (e == EINTR)) {
e = EAGAIN;
ret = -1;
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index d322c0f..84feb20 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -410,16 +410,46 @@
if (!tag) tag = "";
/* XXX: This needs to go! */
- if ((bufID != LOG_ID_RADIO) &&
- (!strcmp(tag, "HTC_RIL") ||
- !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
- !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
- !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") ||
- !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || !strcmp(tag, "SMS"))) {
- bufID = LOG_ID_RADIO;
- /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
- snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
- tag = tmp_tag;
+ if (bufID != LOG_ID_RADIO) {
+ switch (tag[0]) {
+ case 'H':
+ if (strcmp(tag + 1, "HTC_RIL" + 1)) break;
+ goto inform;
+ case 'R':
+ /* Any log tag with "RIL" as the prefix */
+ if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break;
+ goto inform;
+ case 'Q':
+ /* Any log tag with "QC_RIL" as the prefix */
+ if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break;
+ goto inform;
+ case 'I':
+ /* Any log tag with "IMS" as the prefix */
+ if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break;
+ goto inform;
+ case 'A':
+ if (strcmp(tag + 1, "AT" + 1)) break;
+ goto inform;
+ case 'G':
+ if (strcmp(tag + 1, "GSM" + 1)) break;
+ goto inform;
+ case 'S':
+ if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break;
+ goto inform;
+ case 'C':
+ if (strcmp(tag + 1, "CDMA" + 1)) break;
+ goto inform;
+ case 'P':
+ if (strcmp(tag + 1, "PHONE" + 1)) break;
+ /* FALLTHRU */
+ inform:
+ bufID = LOG_ID_RADIO;
+ snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+ tag = tmp_tag;
+ /* FALLTHRU */
+ default:
+ break;
+ }
}
#if __BIONIC__
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 2ade7b0..a2839bf 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -250,6 +250,7 @@
while (!list_empty(&convertHead)) {
struct listnode* node = list_head(&convertHead);
list_remove(node);
+ LOG_ALWAYS_FATAL_IF(node == list_head(&convertHead), "corrupted list");
free(node);
}
}
@@ -326,6 +327,7 @@
else if (!strcmp(formatString, "uid")) format = FORMAT_MODIFIER_UID;
else if (!strcmp(formatString, "descriptive")) format = FORMAT_MODIFIER_DESCRIPT;
/* clang-format on */
+
#ifndef __MINGW32__
else {
extern char* tzname[2];
@@ -637,7 +639,8 @@
TYPE_MILLISECONDS = '3',
TYPE_ALLOCATIONS = '4',
TYPE_ID = '5',
- TYPE_PERCENT = '6'
+ TYPE_PERCENT = '6',
+ TYPE_MONOTONIC = 's'
};
static int android_log_printBinaryEvent(const unsigned char** pEventData,
@@ -651,7 +654,7 @@
size_t outBufLen = *pOutBufLen;
size_t outBufLenSave = outBufLen;
unsigned char type;
- size_t outCount;
+ size_t outCount = 0;
int result = 0;
const char* cp;
size_t len;
@@ -690,6 +693,7 @@
* 4: Number of allocations
* 5: Id
* 6: Percent
+ * s: Number of seconds (monotonic time)
* Default value for data of type int/long is 2 (bytes).
*/
if (!cp || !findChar(&cp, &len, '(')) {
@@ -921,6 +925,42 @@
outCount = snprintf(outBuf, outBufLen, "ms");
}
break;
+ case TYPE_MONOTONIC: {
+ static const uint64_t minute = 60;
+ static const uint64_t hour = 60 * minute;
+ static const uint64_t day = 24 * hour;
+
+ /* Repaint as unsigned seconds, minutes, hours ... */
+ outBuf -= outCount;
+ outBufLen += outCount;
+ uint64_t val = lval;
+ if (val >= day) {
+ outCount = snprintf(outBuf, outBufLen, "%" PRIu64 "d ", val / day);
+ if (outCount >= outBufLen) break;
+ outBuf += outCount;
+ outBufLen -= outCount;
+ val = (val % day) + day;
+ }
+ if (val >= minute) {
+ if (val >= hour) {
+ outCount = snprintf(outBuf, outBufLen, "%" PRIu64 ":",
+ (val / hour) % (day / hour));
+ if (outCount >= outBufLen) break;
+ outBuf += outCount;
+ outBufLen -= outCount;
+ }
+ outCount =
+ snprintf(outBuf, outBufLen,
+ (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+ (val / minute) % (hour / minute));
+ if (outCount >= outBufLen) break;
+ outBuf += outCount;
+ outBufLen -= outCount;
+ }
+ outCount = snprintf(outBuf, outBufLen,
+ (val >= minute) ? "%02" PRIu64 : "%" PRIu64 "s",
+ val % minute);
+ } break;
case TYPE_ALLOCATIONS:
outCount = 0;
/* outCount = snprintf(outBuf, outBufLen, " allocations"); */
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index 0e6432c..275a2d6 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -55,6 +55,7 @@
-fno-builtin \
test_src_files := \
+ libc_test.cpp \
liblog_test_default.cpp \
liblog_test_local.cpp \
liblog_test_stderr.cpp \
@@ -63,15 +64,8 @@
log_radio_test.cpp \
log_read_test.cpp \
log_system_test.cpp \
- log_time_test.cpp
-
-# to prevent breaking the build if bionic not relatively visible to us
-ifneq ($(wildcard $(LOCAL_PATH)/../../../../bionic/libc/bionic/libc_logging.cpp),)
-
-test_src_files += \
- libc_test.cpp
-
-endif
+ log_time_test.cpp \
+ log_wrap_test.cpp
# Build tests for the device (with .so). Run with:
# adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 3f79552..c4bf959 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -527,11 +527,13 @@
/*
* Measure the time it takes to submit the android event logging call
* using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
- * under light load. Expect this to be a dozen or so syscall periods (40us)
+ * under light load. Expect this to be a long path to logger to convert the
+ * unknown tag (0) into a tagname (less than 200us).
*/
static void BM_log_event_overhead(int iters) {
for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
StartBenchmarkTiming();
+ // log tag number 0 is not known, nor shall it ever be known
__android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
StopBenchmarkTiming();
logd_yield();
@@ -539,6 +541,28 @@
}
BENCHMARK(BM_log_event_overhead);
+/*
+ * Measure the time it takes to submit the android event logging call
+ * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
+ * under light load with a known logtag. Expect this to be a dozen or so
+ * syscall periods (less than 40us)
+ */
+static void BM_log_event_overhead_42(int iters) {
+ for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
+ StartBenchmarkTiming();
+ // In system/core/logcat/event.logtags:
+ // # These are used for testing, do not modify without updating
+ // # tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+ // # system/core/liblog/tests/liblog_benchmark.cpp
+ // # system/core/liblog/tests/liblog_test.cpp
+ // 42 answer (to life the universe etc|3)
+ __android_log_btwrite(42, EVENT_TYPE_LONG, &i, sizeof(i));
+ StopBenchmarkTiming();
+ logd_yield();
+ }
+}
+BENCHMARK(BM_log_event_overhead_42);
+
static void BM_log_event_overhead_null(int iters) {
set_log_null();
BM_log_event_overhead(iters);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 71f74ab..e2d5aeb 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -105,7 +105,7 @@
}
#if (defined(__ANDROID__) && defined(USING_LOGGER_DEFAULT))
-static std::string popenToString(std::string command) {
+static std::string popenToString(const std::string& command) {
std::string ret;
FILE* fp = popen(command.c_str(), "r");
@@ -129,17 +129,17 @@
static bool isLogdwActive() {
std::string logdwSignature =
popenToString("grep /dev/socket/logdw /proc/net/unix");
- size_t beginning = logdwSignature.find(" ");
+ size_t beginning = logdwSignature.find(' ');
if (beginning == std::string::npos) return true;
- beginning = logdwSignature.find(" ", beginning + 1);
+ beginning = logdwSignature.find(' ', beginning + 1);
if (beginning == std::string::npos) return true;
- size_t end = logdwSignature.find(" ", beginning + 1);
+ size_t end = logdwSignature.find(' ', beginning + 1);
if (end == std::string::npos) return true;
- end = logdwSignature.find(" ", end + 1);
+ end = logdwSignature.find(' ', end + 1);
if (end == std::string::npos) return true;
- end = logdwSignature.find(" ", end + 1);
+ end = logdwSignature.find(' ', end + 1);
if (end == std::string::npos) return true;
- end = logdwSignature.find(" ", end + 1);
+ end = logdwSignature.find(' ', end + 1);
if (end == std::string::npos) return true;
std::string allLogdwEndpoints = popenToString(
"grep ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
@@ -159,7 +159,7 @@
// NB: fgrep with multiple strings is broken in Android
for (beginning = 0;
- (end = allLogdwEndpoints.find("\n", beginning)) != std::string::npos;
+ (end = allLogdwEndpoints.find('\n', beginning)) != std::string::npos;
beginning = end + 1) {
if (myPidFds.find(allLogdwEndpoints.substr(beginning, end - beginning)) !=
std::string::npos)
@@ -1720,6 +1720,7 @@
// Kills logd and toss all collected data, equivalent to logcat -b all -c,
// except we also return errors to the logging callers.
#ifdef USING_LOGGER_DEFAULT
+#ifdef __ANDROID__
#ifdef TEST_PREFIX
// helper to liblog.enoent to count end-to-end matching logging messages.
static int count_matching_ts(log_time ts) {
@@ -1832,7 +1833,8 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
-#endif // USING_LOCAL_LOGD
+#endif // __ANDROID__
+#endif // USING_LOGGER_DEFAULT
// Below this point we run risks of setuid(AID_SYSTEM) which may affect others.
@@ -3168,7 +3170,7 @@
return (offset != std::string::npos) &&
((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
std::string::npos) &&
- (content.find_first_not_of("0", offset) != offset);
+ (content.find_first_not_of('0', offset) != offset);
}
// must not be: '<needle:> 0 kB'
@@ -3237,7 +3239,7 @@
filename = android::base::StringPrintf("/proc/%d/comm", pid);
android::base::ReadFileToString(filename, &content);
content = android::base::StringPrintf(
- "%d:%s", pid, content.substr(0, content.find("\n")).c_str());
+ "%d:%s", pid, content.substr(0, content.find('\n')).c_str());
EXPECT_TRUE(IsOk(shared_ok, content));
EXPECT_TRUE(IsOk(private_ok, content));
diff --git a/liblog/tests/log_id_test.cpp b/liblog/tests/log_id_test.cpp
index c56fa8b..9fb5a2c 100644
--- a/liblog/tests/log_id_test.cpp
+++ b/liblog/tests/log_id_test.cpp
@@ -89,12 +89,12 @@
ASSERT_EQ(0, pthread_create(&t[i], NULL, ConcurrentPrintFn,
reinterpret_cast<void*>(i)));
}
- int ret = 0;
+ int ret = 1;
for (i = 0; i < NUM_CONCURRENT; i++) {
void* result;
ASSERT_EQ(0, pthread_join(t[i], &result));
int this_result = reinterpret_cast<uintptr_t>(result);
- if ((0 == ret) && (0 != this_result)) {
+ if ((0 < ret) && (ret != this_result)) {
ret = this_result;
}
}
diff --git a/liblog/tests/log_wrap_test.cpp b/liblog/tests/log_wrap_test.cpp
new file mode 100644
index 0000000..ebf0b15
--- /dev/null
+++ b/liblog/tests/log_wrap_test.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013-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 <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/stringprintf.h>
+#include <android/log.h> // minimal logging API
+#include <gtest/gtest.h>
+#include <log/log_properties.h>
+#include <log/log_read.h>
+#include <log/log_time.h>
+#include <log/log_transport.h>
+
+#ifdef __ANDROID__
+static void read_with_wrap() {
+ android_set_log_transport(LOGGER_LOGD);
+
+ // Read the last line in the log to get a starting timestamp. We're assuming
+ // the log is not empty.
+ const int mode = ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
+ struct logger_list* logger_list =
+ android_logger_list_open(LOG_ID_MAIN, mode, 1000, 0);
+
+ ASSERT_NE(logger_list, nullptr);
+
+ log_msg log_msg;
+ int ret = android_logger_list_read(logger_list, &log_msg);
+ android_logger_list_close(logger_list);
+ ASSERT_GT(ret, 0);
+
+ log_time start(log_msg.entry.sec, log_msg.entry.nsec);
+ ASSERT_NE(start, log_time());
+
+ logger_list =
+ android_logger_list_alloc_time(mode | ANDROID_LOG_WRAP, start, 0);
+ ASSERT_NE(logger_list, nullptr);
+
+ struct logger* logger = android_logger_open(logger_list, LOG_ID_MAIN);
+ EXPECT_NE(logger, nullptr);
+ if (logger) {
+ android_logger_list_read(logger_list, &log_msg);
+ }
+
+ android_logger_list_close(logger_list);
+}
+
+static void caught_signal(int /* signum */) {
+}
+#endif
+
+// b/64143705 confirm fixed
+TEST(liblog, wrap_mode_blocks) {
+#ifdef __ANDROID__
+
+ android::base::Timer timer;
+
+ // The read call is expected to take up to 2 hours in the happy case.
+ // We only want to make sure it waits for longer than 30s, but we can't
+ // use an alarm as the implementation uses it. So we run the test in
+ // a separate process.
+ pid_t pid = fork();
+
+ if (pid == 0) {
+ // child
+ read_with_wrap();
+ _exit(0);
+ }
+
+ struct sigaction ignore, old_sigaction;
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ alarm(45);
+
+ bool killed = false;
+ for (;;) {
+ siginfo_t info = {};
+ // This wait will succeed if the child exits, or fail with EINTR if the
+ // alarm goes off first - a loose approximation to a timed wait.
+ int ret = waitid(P_PID, pid, &info, WEXITED);
+ if (ret >= 0 || errno != EINTR) {
+ EXPECT_EQ(ret, 0);
+ if (!killed) {
+ EXPECT_EQ(info.si_status, 0);
+ }
+ break;
+ }
+ unsigned int alarm_left = alarm(0);
+ if (alarm_left > 0) {
+ alarm(alarm_left);
+ } else {
+ kill(pid, SIGTERM);
+ killed = true;
+ }
+ }
+
+ alarm(0);
+ EXPECT_GT(timer.duration(), std::chrono::seconds(40));
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 68c580a..0955633 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -2,6 +2,10 @@
cc_library_shared {
name: "libmemtrack",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["memtrack.cpp"],
export_include_dirs: ["include"],
local_include_dirs: ["include"],
diff --git a/libmemunreachable/.clang-format b/libmemunreachable/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libmemunreachable/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
index 6fe67a4..213be17 100644
--- a/libmemunreachable/Allocator.cpp
+++ b/libmemunreachable/Allocator.cpp
@@ -33,9 +33,11 @@
#include "android-base/macros.h"
-#include "anon_vma_naming.h"
#include "Allocator.h"
#include "LinkedList.h"
+#include "anon_vma_naming.h"
+
+namespace android {
// runtime interfaces used:
// abort
@@ -57,10 +59,9 @@
static constexpr size_t kUsableChunkSize = kChunkSize - kPageSize;
static constexpr size_t kMaxBucketAllocationSize = kChunkSize / 4;
static constexpr size_t kMinBucketAllocationSize = 8;
-static constexpr unsigned int kNumBuckets = const_log2(kMaxBucketAllocationSize)
- - const_log2(kMinBucketAllocationSize) + 1;
-static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize
- / kPageSize;
+static constexpr unsigned int kNumBuckets =
+ const_log2(kMaxBucketAllocationSize) - const_log2(kMinBucketAllocationSize) + 1;
+static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize / kPageSize;
std::atomic<int> heap_count;
@@ -93,7 +94,7 @@
void FreeLocked(void* ptr);
struct MapAllocation {
- void *ptr;
+ void* ptr;
size_t size;
MapAllocation* next;
};
@@ -107,8 +108,7 @@
}
static inline unsigned int size_to_bucket(size_t size) {
- if (size < kMinBucketAllocationSize)
- return kMinBucketAllocationSize;
+ if (size < kMinBucketAllocationSize) return kMinBucketAllocationSize;
return log2(size - 1) + 1 - const_log2(kMinBucketAllocationSize);
}
@@ -140,8 +140,7 @@
// Trim beginning
if (aligned_ptr != ptr) {
- ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr)
- - reinterpret_cast<uintptr_t>(ptr);
+ ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr) - reinterpret_cast<uintptr_t>(ptr);
munmap(ptr, extra);
map_size -= extra;
ptr = aligned_ptr;
@@ -151,14 +150,13 @@
if (map_size != size) {
assert(map_size > size);
assert(ptr != NULL);
- munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size),
- map_size - size);
+ munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size), map_size - size);
}
-#define PR_SET_VMA 0x53564d41
-#define PR_SET_VMA_ANON_NAME 0
- prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
- reinterpret_cast<uintptr_t>(ptr), size, "leak_detector_malloc");
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(ptr), size,
+ "leak_detector_malloc");
return ptr;
}
@@ -170,36 +168,31 @@
Chunk(HeapImpl* heap, int bucket);
~Chunk() {}
- void *Alloc();
+ void* Alloc();
void Free(void* ptr);
void Purge();
bool Empty();
static Chunk* ptr_to_chunk(void* ptr) {
- return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr)
- & ~(kChunkSize - 1));
+ return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr) & ~(kChunkSize - 1));
}
static bool is_chunk(void* ptr) {
return (reinterpret_cast<uintptr_t>(ptr) & (kChunkSize - 1)) != 0;
}
- unsigned int free_count() {
- return free_count_;
- }
- HeapImpl* heap() {
- return heap_;
- }
- LinkedList<Chunk*> node_; // linked list sorted by minimum free count
+ unsigned int free_count() { return free_count_; }
+ HeapImpl* heap() { return heap_; }
+ LinkedList<Chunk*> node_; // linked list sorted by minimum free count
private:
DISALLOW_COPY_AND_ASSIGN(Chunk);
HeapImpl* heap_;
unsigned int bucket_;
- unsigned int allocation_size_; // size of allocations in chunk, min 8 bytes
- unsigned int max_allocations_; // maximum number of allocations in the chunk
- unsigned int first_free_bitmap_; // index into bitmap for first non-full entry
- unsigned int free_count_; // number of available allocations
- unsigned int frees_since_purge_; // number of calls to Free since last Purge
+ unsigned int allocation_size_; // size of allocations in chunk, min 8 bytes
+ unsigned int max_allocations_; // maximum number of allocations in the chunk
+ unsigned int first_free_bitmap_; // index into bitmap for first non-full entry
+ unsigned int free_count_; // number of available allocations
+ unsigned int frees_since_purge_; // number of calls to Free since last Purge
// bitmap of pages that have been dirtied
uint32_t dirty_pages_[div_round_up(kUsablePagesPerChunk, 32)];
@@ -210,13 +203,10 @@
char data_[0];
unsigned int ptr_to_n(void* ptr) {
- ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr)
- - reinterpret_cast<uintptr_t>(data_);
+ ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(data_);
return offset / allocation_size_;
}
- void* n_to_ptr(unsigned int n) {
- return data_ + n * allocation_size_;
- }
+ void* n_to_ptr(unsigned int n) { return data_ + n * allocation_size_; }
};
static_assert(sizeof(Chunk) <= kPageSize, "header must fit in page");
@@ -225,23 +215,27 @@
assert(count == sizeof(Chunk));
void* mem = MapAligned(kChunkSize, kChunkSize);
if (!mem) {
- abort(); //throw std::bad_alloc;
+ abort(); // throw std::bad_alloc;
}
return mem;
}
// Override new operator on chunk to use mmap to allocate kChunkSize
-void Chunk::operator delete(void *ptr) {
+void Chunk::operator delete(void* ptr) {
assert(reinterpret_cast<Chunk*>(ptr) == ptr_to_chunk(ptr));
munmap(ptr, kChunkSize);
}
-Chunk::Chunk(HeapImpl* heap, int bucket) :
- node_(this), heap_(heap), bucket_(bucket), allocation_size_(
- bucket_to_size(bucket)), max_allocations_(
- kUsableChunkSize / allocation_size_), first_free_bitmap_(0), free_count_(
- max_allocations_), frees_since_purge_(0) {
+Chunk::Chunk(HeapImpl* heap, int bucket)
+ : node_(this),
+ heap_(heap),
+ bucket_(bucket),
+ allocation_size_(bucket_to_size(bucket)),
+ max_allocations_(kUsableChunkSize / allocation_size_),
+ first_free_bitmap_(0),
+ free_count_(max_allocations_),
+ frees_since_purge_(0) {
memset(dirty_pages_, 0, sizeof(dirty_pages_));
memset(free_bitmap_, 0xff, sizeof(free_bitmap_));
}
@@ -254,8 +248,7 @@
assert(free_count_ > 0);
unsigned int i = first_free_bitmap_;
- while (free_bitmap_[i] == 0)
- i++;
+ while (free_bitmap_[i] == 0) i++;
assert(i < arraysize(free_bitmap_));
unsigned int bit = __builtin_ffs(free_bitmap_[i]) - 1;
assert(free_bitmap_[i] & (1U << bit));
@@ -306,38 +299,35 @@
void Chunk::Purge() {
frees_since_purge_ = 0;
- //unsigned int allocsPerPage = kPageSize / allocation_size_;
+ // unsigned int allocsPerPage = kPageSize / allocation_size_;
}
// Override new operator on HeapImpl to use mmap to allocate a page
-void* HeapImpl::operator new(std::size_t count __attribute__((unused)))
- noexcept {
+void* HeapImpl::operator new(std::size_t count __attribute__((unused))) noexcept {
assert(count == sizeof(HeapImpl));
void* mem = MapAligned(kPageSize, kPageSize);
if (!mem) {
- abort(); //throw std::bad_alloc;
+ abort(); // throw std::bad_alloc;
}
heap_count++;
return mem;
}
-void HeapImpl::operator delete(void *ptr) {
+void HeapImpl::operator delete(void* ptr) {
munmap(ptr, kPageSize);
}
-HeapImpl::HeapImpl() :
- free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {
-}
+HeapImpl::HeapImpl() : free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {}
bool HeapImpl::Empty() {
for (unsigned int i = 0; i < kNumBuckets; i++) {
- for (LinkedList<Chunk*> *it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+ for (LinkedList<Chunk*>* it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) {
if (!it->data()->Empty()) {
return false;
}
}
- for (LinkedList<Chunk*> *it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+ for (LinkedList<Chunk*>* it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) {
if (!it->data()->Empty()) {
return false;
}
@@ -350,12 +340,12 @@
HeapImpl::~HeapImpl() {
for (unsigned int i = 0; i < kNumBuckets; i++) {
while (!free_chunks_[i].empty()) {
- Chunk *chunk = free_chunks_[i].next()->data();
+ Chunk* chunk = free_chunks_[i].next()->data();
chunk->node_.remove();
delete chunk;
}
while (!full_chunks_[i].empty()) {
- Chunk *chunk = full_chunks_[i].next()->data();
+ Chunk* chunk = full_chunks_[i].next()->data();
chunk->node_.remove();
delete chunk;
}
@@ -373,18 +363,18 @@
}
int bucket = size_to_bucket(size);
if (free_chunks_[bucket].empty()) {
- Chunk *chunk = new Chunk(this, bucket);
+ Chunk* chunk = new Chunk(this, bucket);
free_chunks_[bucket].insert(chunk->node_);
}
return free_chunks_[bucket].next()->data()->Alloc();
}
-void HeapImpl::Free(void *ptr) {
+void HeapImpl::Free(void* ptr) {
std::lock_guard<std::mutex> lk(m_);
FreeLocked(ptr);
}
-void HeapImpl::FreeLocked(void *ptr) {
+void HeapImpl::FreeLocked(void* ptr) {
if (!Chunk::is_chunk(ptr)) {
HeapImpl::MapFree(ptr);
} else {
@@ -397,12 +387,11 @@
void* HeapImpl::MapAlloc(size_t size) {
size = (size + kPageSize - 1) & ~(kPageSize - 1);
- MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(
- sizeof(MapAllocation)));
+ MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(sizeof(MapAllocation)));
void* ptr = MapAligned(size, kChunkSize);
if (!ptr) {
FreeLocked(allocation);
- abort(); //throw std::bad_alloc;
+ abort(); // throw std::bad_alloc;
}
allocation->ptr = ptr;
allocation->size = size;
@@ -412,10 +401,9 @@
return ptr;
}
-void HeapImpl::MapFree(void *ptr) {
- MapAllocation **allocation = &map_allocation_list_;
- while (*allocation && (*allocation)->ptr != ptr)
- allocation = &(*allocation)->next;
+void HeapImpl::MapFree(void* ptr) {
+ MapAllocation** allocation = &map_allocation_list_;
+ while (*allocation && (*allocation)->ptr != ptr) allocation = &(*allocation)->next;
assert(*allocation != nullptr);
@@ -425,22 +413,22 @@
*allocation = (*allocation)->next;
}
-void HeapImpl::MoveToFreeList(Chunk *chunk, int bucket) {
+void HeapImpl::MoveToFreeList(Chunk* chunk, int bucket) {
MoveToList(chunk, &free_chunks_[bucket]);
}
-void HeapImpl::MoveToFullList(Chunk *chunk, int bucket) {
+void HeapImpl::MoveToFullList(Chunk* chunk, int bucket) {
MoveToList(chunk, &full_chunks_[bucket]);
}
-void HeapImpl::MoveToList(Chunk *chunk, LinkedList<Chunk*>* head) {
+void HeapImpl::MoveToList(Chunk* chunk, LinkedList<Chunk*>* head) {
// Remove from old list
chunk->node_.remove();
- LinkedList<Chunk*> *node = head;
+ LinkedList<Chunk*>* node = head;
// Insert into new list, sorted by lowest free count
- while (node->next() != head && node->data() != nullptr
- && node->data()->free_count() < chunk->free_count())
+ while (node->next() != head && node->data() != nullptr &&
+ node->data()->free_count() < chunk->free_count())
node = node->next();
node->insert(chunk->node_);
@@ -469,10 +457,12 @@
impl_->Free(ptr);
}
-void Heap::deallocate(HeapImpl*impl, void* ptr) {
+void Heap::deallocate(HeapImpl* impl, void* ptr) {
impl->Free(ptr);
}
bool Heap::empty() {
return impl_->Empty();
}
+
+} // namespace android
diff --git a/libmemunreachable/Allocator.h b/libmemunreachable/Allocator.h
index 5390739..837a12b 100644
--- a/libmemunreachable/Allocator.h
+++ b/libmemunreachable/Allocator.h
@@ -27,18 +27,20 @@
#include <unordered_map>
#include <unordered_set>
#include <vector>
+
+namespace android {
+
extern std::atomic<int> heap_count;
class HeapImpl;
-template<typename T>
+template <typename T>
class Allocator;
-
// Non-templated class that implements wraps HeapImpl to keep
// implementation out of the header file
class Heap {
-public:
+ public:
Heap();
~Heap();
@@ -59,110 +61,99 @@
static void deallocate(HeapImpl* impl, void* ptr);
// Allocate a class of type T
- template<class T>
+ template <class T>
T* allocate() {
return reinterpret_cast<T*>(allocate(sizeof(T)));
}
// Comparators, copied objects will be equal
- bool operator ==(const Heap& other) const {
- return impl_ == other.impl_;
- }
- bool operator !=(const Heap& other) const {
- return !(*this == other);
- }
+ bool operator==(const Heap& other) const { return impl_ == other.impl_; }
+ bool operator!=(const Heap& other) const { return !(*this == other); }
// std::unique_ptr wrapper that allocates using allocate and deletes using
// deallocate
- template<class T>
+ template <class T>
using unique_ptr = std::unique_ptr<T, std::function<void(void*)>>;
- template<class T, class... Args>
+ template <class T, class... Args>
unique_ptr<T> make_unique(Args&&... args) {
HeapImpl* impl = impl_;
- return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...),
- [impl](void* ptr) {
- reinterpret_cast<T*>(ptr)->~T();
- deallocate(impl, ptr);
- });
+ return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...), [impl](void* ptr) {
+ reinterpret_cast<T*>(ptr)->~T();
+ deallocate(impl, ptr);
+ });
}
// std::unique_ptr wrapper that allocates using allocate and deletes using
// deallocate
- template<class T>
+ template <class T>
using shared_ptr = std::shared_ptr<T>;
- template<class T, class... Args>
+ template <class T, class... Args>
shared_ptr<T> make_shared(Args&&... args);
-protected:
+ protected:
HeapImpl* impl_;
bool owns_impl_;
};
// STLAllocator implements the std allocator interface on top of a Heap
-template<typename T>
+template <typename T>
class STLAllocator {
-public:
+ public:
using value_type = T;
- ~STLAllocator() {
- }
+ ~STLAllocator() {}
// Construct an STLAllocator on top of a Heap
- STLAllocator(const Heap& heap) : // NOLINT, implicit
- heap_(heap) {
- }
+ STLAllocator(const Heap& heap)
+ : // NOLINT, implicit
+ heap_(heap) {}
// Rebind an STLAllocator from an another STLAllocator
- template<typename U>
- STLAllocator(const STLAllocator<U>& other) : // NOLINT, implicit
- heap_(other.heap_) {
- }
+ template <typename U>
+ STLAllocator(const STLAllocator<U>& other)
+ : // NOLINT, implicit
+ heap_(other.heap_) {}
STLAllocator(const STLAllocator&) = default;
STLAllocator<T>& operator=(const STLAllocator<T>&) = default;
- T* allocate(std::size_t n) {
- return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T)));
- }
+ T* allocate(std::size_t n) { return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T))); }
- void deallocate(T* ptr, std::size_t) {
- heap_.deallocate(ptr);
- }
+ void deallocate(T* ptr, std::size_t) { heap_.deallocate(ptr); }
- template<typename U>
- bool operator ==(const STLAllocator<U>& other) const {
+ template <typename U>
+ bool operator==(const STLAllocator<U>& other) const {
return heap_ == other.heap_;
}
- template<typename U>
- inline bool operator !=(const STLAllocator<U>& other) const {
+ template <typename U>
+ inline bool operator!=(const STLAllocator<U>& other) const {
return !(this == other);
}
- template<typename U>
+ template <typename U>
friend class STLAllocator;
-protected:
+ protected:
Heap heap_;
};
-
// Allocator extends STLAllocator with some convenience methods for allocating
// a single object and for constructing unique_ptr and shared_ptr objects with
// appropriate deleters.
-template<class T>
+template <class T>
class Allocator : public STLAllocator<T> {
public:
~Allocator() {}
- Allocator(const Heap& other) : // NOLINT, implicit
- STLAllocator<T>(other) {
- }
+ Allocator(const Heap& other)
+ : // NOLINT, implicit
+ STLAllocator<T>(other) {}
- template<typename U>
- Allocator(const STLAllocator<U>& other) : // NOLINT, implicit
- STLAllocator<T>(other) {
- }
+ template <typename U>
+ Allocator(const STLAllocator<U>& other)
+ : // NOLINT, implicit
+ STLAllocator<T>(other) {}
Allocator(const Allocator&) = default;
Allocator<T>& operator=(const Allocator<T>&) = default;
@@ -171,24 +162,20 @@
using STLAllocator<T>::deallocate;
using STLAllocator<T>::heap_;
- T* allocate() {
- return STLAllocator<T>::allocate(1);
- }
- void deallocate(void* ptr) {
- heap_.deallocate(ptr);
- }
+ T* allocate() { return STLAllocator<T>::allocate(1); }
+ void deallocate(void* ptr) { heap_.deallocate(ptr); }
using shared_ptr = Heap::shared_ptr<T>;
- template<class... Args>
- shared_ptr make_shared(Args&& ...args) {
+ template <class... Args>
+ shared_ptr make_shared(Args&&... args) {
return heap_.template make_shared<T>(std::forward<Args>(args)...);
}
using unique_ptr = Heap::unique_ptr<T>;
- template<class... Args>
- unique_ptr make_unique(Args&& ...args) {
+ template <class... Args>
+ unique_ptr make_unique(Args&&... args) {
return heap_.template make_unique<T>(std::forward<Args>(args)...);
}
};
@@ -196,33 +183,36 @@
// std::unique_ptr wrapper that allocates using allocate and deletes using
// deallocate. Implemented outside class definition in order to pass
// Allocator<T> to shared_ptr.
-template<class T, class... Args>
+template <class T, class... Args>
inline Heap::shared_ptr<T> Heap::make_shared(Args&&... args) {
return std::allocate_shared<T, Allocator<T>, Args...>(Allocator<T>(*this),
- std::forward<Args>(args)...);
+ std::forward<Args>(args)...);
}
namespace allocator {
-template<class T>
+template <class T>
using vector = std::vector<T, Allocator<T>>;
-template<class T>
+template <class T>
using list = std::list<T, Allocator<T>>;
-template<class Key, class T, class Compare = std::less<Key>>
+template <class Key, class T, class Compare = std::less<Key>>
using map = std::map<Key, T, Compare, Allocator<std::pair<const Key, T>>>;
-template<class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
-using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>;
+template <class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+using unordered_map =
+ std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>;
-template<class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+template <class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
using unordered_set = std::unordered_set<Key, Hash, KeyEqual, Allocator<Key>>;
-template<class Key, class Compare = std::less<Key>>
+template <class Key, class Compare = std::less<Key>>
using set = std::set<Key, Compare, Allocator<Key>>;
using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
}
+} // namespace android
+
#endif
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 1b8830a..8b76a65 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -6,19 +6,26 @@
"-Wextra",
"-Werror",
],
- clang: true,
shared_libs: [
"libbase",
- "liblog",
],
+
+ target: {
+ android: {
+ static_libs: ["libasync_safe"],
+ },
+ host: {
+ shared_libs: ["liblog"],
+ }
+ },
}
cc_library_shared {
name: "libmemunreachable",
- vendor_available: true,
defaults: ["libmemunreachable_defaults"],
srcs: [
"Allocator.cpp",
+ "Binder.cpp",
"HeapWalker.cpp",
"LeakFolding.cpp",
"LeakPipe.cpp",
@@ -31,7 +38,6 @@
static_libs: [
"libc_malloc_debug_backtrace",
- "libc_logging",
],
// Only need this for arm since libc++ uses its own unwind code that
// doesn't mix with the other default unwind code.
@@ -78,3 +84,18 @@
},
},
}
+
+cc_test {
+ name: "memunreachable_binder_test",
+ defaults: ["libmemunreachable_defaults"],
+ srcs: [
+ "tests/Binder_test.cpp",
+ "tests/MemUnreachable_test.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libhwbinder",
+ "libmemunreachable",
+ "libutils",
+ ],
+}
diff --git a/libmemunreachable/Binder.cpp b/libmemunreachable/Binder.cpp
new file mode 100644
index 0000000..60512a3
--- /dev/null
+++ b/libmemunreachable/Binder.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 <sys/cdefs.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include "Binder.h"
+#include "log.h"
+
+__BEGIN_DECLS
+
+// Weak undefined references to the symbols in libbinder and libhwbinder
+// so that libmemunreachable can call them in processes that have them
+// loaded without requiring libmemunreachable to have dependencies on them.
+ssize_t __attribute__((weak)) getBinderKernelReferences(size_t, uintptr_t*);
+ssize_t __attribute__((weak)) getHWBinderKernelReferences(size_t, uintptr_t*);
+
+__END_DECLS
+
+namespace android {
+
+static bool BinderReferencesToVector(allocator::vector<uintptr_t>& refs,
+ std::function<ssize_t(size_t, uintptr_t*)> fn) {
+ if (fn == nullptr) {
+ return true;
+ }
+
+ size_t size = refs.size();
+
+ do {
+ refs.resize(size);
+
+ ssize_t ret = fn(refs.size(), refs.data());
+ if (ret < 0) {
+ return false;
+ }
+
+ size = ret;
+ } while (size > refs.size());
+
+ refs.resize(size);
+ return true;
+}
+
+bool BinderReferences(allocator::vector<uintptr_t>& refs) {
+ refs.clear();
+
+ allocator::vector<uintptr_t> binder_refs{refs.get_allocator()};
+ if (BinderReferencesToVector(refs, getBinderKernelReferences)) {
+ refs.insert(refs.end(), binder_refs.begin(), binder_refs.end());
+ } else {
+ MEM_ALOGE("getBinderKernelReferences failed");
+ }
+
+ allocator::vector<uintptr_t> hwbinder_refs{refs.get_allocator()};
+ if (BinderReferencesToVector(hwbinder_refs, getHWBinderKernelReferences)) {
+ refs.insert(refs.end(), hwbinder_refs.begin(), hwbinder_refs.end());
+ } else {
+ MEM_ALOGE("getHWBinderKernelReferences failed");
+ }
+
+ return true;
+}
+
+} // namespace android
diff --git a/libunwindstack/Log.h b/libmemunreachable/Binder.h
similarity index 65%
copy from libunwindstack/Log.h
copy to libmemunreachable/Binder.h
index 2d01aa8..bf4fd3e 100644
--- a/libunwindstack/Log.h
+++ b/libmemunreachable/Binder.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,12 +14,15 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
+#ifndef LIBMEMUNREACHABLE_BINDER_H_
+#define LIBMEMUNREACHABLE_BINDER_H_
-#include <stdint.h>
+#include "Allocator.h"
-void log_to_stdout(bool enable);
-void log(uint8_t indent, const char* format, ...);
+namespace android {
-#endif // _LIBUNWINDSTACK_LOG_H
+bool BinderReferences(allocator::vector<uintptr_t>& refs);
+
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_BINDER_H_
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 62366f2..2403ad0 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -28,6 +28,8 @@
#include "ScopedSignalHandler.h"
#include "log.h"
+namespace android {
+
bool HeapWalker::Allocation(uintptr_t begin, uintptr_t end) {
if (end == begin) {
end = begin + 1;
@@ -42,11 +44,9 @@
} else {
Range overlap = inserted.first->first;
if (overlap != range) {
- ALOGE("range %p-%p overlaps with existing range %p-%p",
- reinterpret_cast<void*>(begin),
- reinterpret_cast<void*>(end),
- reinterpret_cast<void*>(overlap.begin),
- reinterpret_cast<void*>(overlap.end));
+ MEM_ALOGE("range %p-%p overlaps with existing range %p-%p", reinterpret_cast<void*>(begin),
+ reinterpret_cast<void*>(end), reinterpret_cast<void*>(overlap.begin),
+ reinterpret_cast<void*>(overlap.end));
}
return false;
}
@@ -116,8 +116,8 @@
return true;
}
-bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit,
- size_t* num_leaks_out, size_t* leak_bytes_out) {
+bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit, size_t* num_leaks_out,
+ size_t* leak_bytes_out) {
leaked.clear();
size_t num_leaks = 0;
@@ -150,27 +150,30 @@
static bool MapOverPage(void* addr) {
const size_t page_size = sysconf(_SC_PAGE_SIZE);
- void *page = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & ~(page_size-1));
+ void* page = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & ~(page_size - 1));
- void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
+ void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
if (ret == MAP_FAILED) {
- ALOGE("failed to map page at %p: %s", page, strerror(errno));
+ MEM_ALOGE("failed to map page at %p: %s", page, strerror(errno));
return false;
}
return true;
}
-void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si, void* /*uctx*/) {
+void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si,
+ void* /*uctx*/) {
uintptr_t addr = reinterpret_cast<uintptr_t>(si->si_addr);
if (addr != walking_ptr_) {
handler.reset();
return;
}
- ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
+ MEM_ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
if (!MapOverPage(si->si_addr)) {
handler.reset();
}
}
ScopedSignalHandler::SignalFn ScopedSignalHandler::handler_;
+
+} // namespace android
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index b25696f..5c7ec13 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -25,6 +25,8 @@
#include "ScopedSignalHandler.h"
#include "Tarjan.h"
+namespace android {
+
// A range [begin, end)
struct Range {
uintptr_t begin;
@@ -34,31 +36,31 @@
bool operator==(const Range& other) const {
return this->begin == other.begin && this->end == other.end;
}
- bool operator!=(const Range& other) const {
- return !(*this == other);
- }
+ bool operator!=(const Range& other) const { return !(*this == other); }
};
// Comparator for Ranges that returns equivalence for overlapping ranges
struct compare_range {
- bool operator()(const Range& a, const Range& b) const {
- return a.end <= b.begin;
- }
+ bool operator()(const Range& a, const Range& b) const { return a.end <= b.begin; }
};
class HeapWalker {
public:
- explicit HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator),
- allocations_(allocator), allocation_bytes_(0),
- roots_(allocator), root_vals_(allocator),
- segv_handler_(allocator), walking_ptr_(0) {
+ explicit HeapWalker(Allocator<HeapWalker> allocator)
+ : allocator_(allocator),
+ allocations_(allocator),
+ allocation_bytes_(0),
+ roots_(allocator),
+ root_vals_(allocator),
+ segv_handler_(allocator),
+ walking_ptr_(0) {
valid_allocations_range_.end = 0;
valid_allocations_range_.begin = ~valid_allocations_range_.end;
- segv_handler_.install(SIGSEGV,
- [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
+ segv_handler_.install(
+ SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
this->HandleSegFault(handler, signal, siginfo, uctx);
- });
+ });
}
~HeapWalker() {}
@@ -68,15 +70,14 @@
bool DetectLeaks();
- bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks,
- size_t* leak_bytes);
+ bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks, size_t* leak_bytes);
size_t Allocations();
size_t AllocationBytes();
- template<class F>
+ template <class F>
void ForEachPtrInRange(const Range& range, F&& f);
- template<class F>
+ template <class F>
void ForEachAllocation(F&& f);
struct AllocationInfo {
@@ -84,7 +85,6 @@
};
private:
-
void RecurseRoot(const Range& root);
bool WordContainsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info);
void HandleSegFault(ScopedSignalHandler&, int, siginfo_t*, void*);
@@ -103,7 +103,7 @@
uintptr_t walking_ptr_;
};
-template<class F>
+template <class F>
inline void HeapWalker::ForEachPtrInRange(const Range& range, F&& f) {
uintptr_t begin = (range.begin + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1);
// TODO(ccross): we might need to consider a pointer to the end of a buffer
@@ -118,7 +118,7 @@
}
}
-template<class F>
+template <class F>
inline void HeapWalker::ForEachAllocation(F&& f) {
for (auto& it : allocations_) {
const Range& range = it.first;
@@ -127,4 +127,6 @@
}
}
+} // namespace android
+
#endif
diff --git a/libmemunreachable/Leak.h b/libmemunreachable/Leak.h
index eaeeea7..de64b64 100644
--- a/libmemunreachable/Leak.h
+++ b/libmemunreachable/Leak.h
@@ -26,9 +26,9 @@
// as a key in std::unordered_map.
namespace std {
-template<>
-struct hash<Leak::Backtrace> {
- std::size_t operator()(const Leak::Backtrace& key) const {
+template <>
+struct hash<android::Leak::Backtrace> {
+ std::size_t operator()(const android::Leak::Backtrace& key) const {
std::size_t seed = 0;
hash_combine(seed, key.num_frames);
@@ -40,7 +40,7 @@
}
private:
- template<typename T>
+ template <typename T>
inline void hash_combine(std::size_t& seed, const T& v) const {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
@@ -49,9 +49,12 @@
} // namespace std
+namespace android {
+
static bool operator==(const Leak::Backtrace& lhs, const Leak::Backtrace& rhs) {
return (lhs.num_frames == rhs.num_frames) &&
- memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0;
+ memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0;
+}
}
#endif
diff --git a/libmemunreachable/LeakFolding.cpp b/libmemunreachable/LeakFolding.cpp
index be4d20c..69f320c 100644
--- a/libmemunreachable/LeakFolding.cpp
+++ b/libmemunreachable/LeakFolding.cpp
@@ -22,6 +22,8 @@
#include "Tarjan.h"
#include "log.h"
+namespace android {
+
// Converts possibly cyclic graph of leaks to a DAG by combining
// strongly-connected components into a object, stored in the scc pointer
// of each node in the component.
@@ -31,11 +33,11 @@
Allocator<SCCInfo> scc_allocator = allocator_;
- for (auto& scc_nodes: scc_list) {
+ for (auto& scc_nodes : scc_list) {
Allocator<SCCInfo>::unique_ptr leak_scc;
leak_scc = scc_allocator.make_unique(scc_allocator);
- for (auto& node: scc_nodes) {
+ for (auto& node : scc_nodes) {
node->ptr->scc = leak_scc.get();
leak_scc->count++;
leak_scc->size += node->ptr->range.size();
@@ -46,7 +48,7 @@
for (auto& it : leak_map_) {
LeakInfo& leak = it.second;
- for (auto& ref: leak.node.references_out) {
+ for (auto& ref : leak.node.references_out) {
if (leak.scc != ref->ptr->scc) {
leak.scc->node.Edge(&ref->ptr->scc->node);
}
@@ -55,17 +57,14 @@
}
void LeakFolding::AccumulateLeaks(SCCInfo* dominator) {
- std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_,
- [&](SCCInfo* scc) {
- if (scc->accumulator != dominator) {
- scc->accumulator = dominator;
- dominator->cuumulative_size += scc->size;
- dominator->cuumulative_count += scc->count;
- scc->node.Foreach([&](SCCInfo* ref) {
- walk(ref);
- });
- }
- });
+ std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, [&](SCCInfo* scc) {
+ if (scc->accumulator != dominator) {
+ scc->accumulator = dominator;
+ dominator->cuumulative_size += scc->size;
+ dominator->cuumulative_count += scc->count;
+ scc->node.Foreach([&](SCCInfo* ref) { walk(ref); });
+ }
+ });
walk(dominator);
}
@@ -73,27 +72,25 @@
Allocator<LeakInfo> leak_allocator = allocator_;
// Find all leaked allocations insert them into leak_map_ and leak_graph_
- heap_walker_.ForEachAllocation(
- [&](const Range& range, HeapWalker::AllocationInfo& allocation) {
- if (!allocation.referenced_from_root) {
- auto it = leak_map_.emplace(std::piecewise_construct,
- std::forward_as_tuple(range),
- std::forward_as_tuple(range, allocator_));
- LeakInfo& leak = it.first->second;
- leak_graph_.push_back(&leak.node);
- }
- });
+ heap_walker_.ForEachAllocation([&](const Range& range, HeapWalker::AllocationInfo& allocation) {
+ if (!allocation.referenced_from_root) {
+ auto it = leak_map_.emplace(std::piecewise_construct, std::forward_as_tuple(range),
+ std::forward_as_tuple(range, allocator_));
+ LeakInfo& leak = it.first->second;
+ leak_graph_.push_back(&leak.node);
+ }
+ });
// Find references between leaked allocations and connect them in leak_graph_
for (auto& it : leak_map_) {
LeakInfo& leak = it.second;
heap_walker_.ForEachPtrInRange(leak.range,
- [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) {
- if (!ptr_info->referenced_from_root) {
- LeakInfo* ptr_leak = &leak_map_.at(ptr_range);
- leak.node.Edge(&ptr_leak->node);
- }
- });
+ [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) {
+ if (!ptr_info->referenced_from_root) {
+ LeakInfo* ptr_leak = &leak_map_.at(ptr_range);
+ leak.node.Edge(&ptr_leak->node);
+ }
+ });
}
// Convert the cyclic graph to a DAG by grouping strongly connected components
@@ -110,8 +107,8 @@
return true;
}
-bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked,
- size_t* num_leaks_out, size_t* leak_bytes_out) {
+bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked, size_t* num_leaks_out,
+ size_t* leak_bytes_out) {
size_t num_leaks = 0;
size_t leak_bytes = 0;
for (auto& it : leak_map_) {
@@ -123,9 +120,8 @@
for (auto& it : leak_map_) {
const LeakInfo& leak = it.second;
if (leak.scc->dominator) {
- leaked.emplace_back(Leak{leak.range,
- leak.scc->cuumulative_count - 1,
- leak.scc->cuumulative_size - leak.range.size()});
+ leaked.emplace_back(Leak{leak.range, leak.scc->cuumulative_count - 1,
+ leak.scc->cuumulative_size - leak.range.size()});
}
}
@@ -138,3 +134,5 @@
return true;
}
+
+} // namespace android
diff --git a/libmemunreachable/LeakFolding.h b/libmemunreachable/LeakFolding.h
index 9c6a525..09affac 100644
--- a/libmemunreachable/LeakFolding.h
+++ b/libmemunreachable/LeakFolding.h
@@ -19,11 +19,16 @@
#include "HeapWalker.h"
+namespace android {
+
class LeakFolding {
public:
LeakFolding(Allocator<void> allocator, HeapWalker& heap_walker)
- : allocator_(allocator), heap_walker_(heap_walker),
- leak_map_(allocator), leak_graph_(allocator), leak_scc_(allocator) {}
+ : allocator_(allocator),
+ heap_walker_(heap_walker),
+ leak_map_(allocator),
+ leak_graph_(allocator),
+ leak_scc_(allocator) {}
bool FoldLeaks();
@@ -33,8 +38,7 @@
size_t referenced_size;
};
- bool Leaked(allocator::vector<Leak>& leaked,
- size_t* num_leaks_out, size_t* leak_bytes_out);
+ bool Leaked(allocator::vector<Leak>& leaked, size_t* num_leaks_out, size_t* leak_bytes_out);
private:
DISALLOW_COPY_AND_ASSIGN(LeakFolding);
@@ -54,9 +58,15 @@
bool dominator;
SCCInfo* accumulator;
- explicit SCCInfo(Allocator<SCCInfo> allocator) : node(this, allocator),
- count(0), size(0), cuumulative_count(0), cuumulative_size(0),
- dominator(false), accumulator(nullptr) {}
+ explicit SCCInfo(Allocator<SCCInfo> allocator)
+ : node(this, allocator),
+ count(0),
+ size(0),
+ cuumulative_count(0),
+ cuumulative_size(0),
+ dominator(false),
+ accumulator(nullptr) {}
+
private:
SCCInfo(SCCInfo&&) = delete;
DISALLOW_COPY_AND_ASSIGN(SCCInfo);
@@ -71,8 +81,7 @@
SCCInfo* scc;
LeakInfo(const Range& range, Allocator<LeakInfo> allocator)
- : node(this, allocator), range(range),
- scc(nullptr) {}
+ : node(this, allocator), range(range), scc(nullptr) {}
private:
DISALLOW_COPY_AND_ASSIGN(LeakInfo);
@@ -86,4 +95,6 @@
allocator::vector<Allocator<SCCInfo>::unique_ptr> leak_scc_;
};
-#endif // LIBMEMUNREACHABLE_LEAK_FOLDING_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_LEAK_FOLDING_H_
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp
index 080f8a7..8ea9ad6 100644
--- a/libmemunreachable/LeakPipe.cpp
+++ b/libmemunreachable/LeakPipe.cpp
@@ -21,9 +21,11 @@
#include "log.h"
+namespace android {
+
bool LeakPipe::SendFd(int sock, int fd) {
- struct msghdr hdr{};
- struct iovec iov{};
+ struct msghdr hdr {};
+ struct iovec iov {};
unsigned int data = 0xfdfdfdfd;
alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
@@ -44,11 +46,11 @@
int ret = sendmsg(sock, &hdr, 0);
if (ret < 0) {
- ALOGE("failed to send fd: %s", strerror(errno));
+ MEM_ALOGE("failed to send fd: %s", strerror(errno));
return false;
}
if (ret == 0) {
- ALOGE("eof when sending fd");
+ MEM_ALOGE("eof when sending fd");
return false;
}
@@ -56,8 +58,8 @@
}
int LeakPipe::ReceiveFd(int sock) {
- struct msghdr hdr{};
- struct iovec iov{};
+ struct msghdr hdr {};
+ struct iovec iov {};
unsigned int data;
alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
@@ -71,19 +73,21 @@
int ret = recvmsg(sock, &hdr, 0);
if (ret < 0) {
- ALOGE("failed to receive fd: %s", strerror(errno));
+ MEM_ALOGE("failed to receive fd: %s", strerror(errno));
return -1;
}
if (ret == 0) {
- ALOGE("eof when receiving fd");
+ MEM_ALOGE("eof when receiving fd");
return -1;
}
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
- ALOGE("missing fd while receiving fd");
+ MEM_ALOGE("missing fd while receiving fd");
return -1;
}
return *(int*)CMSG_DATA(cmsg);
}
+
+} // namespace android
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h
index 3f4e0b7..94d4aa4 100644
--- a/libmemunreachable/LeakPipe.h
+++ b/libmemunreachable/LeakPipe.h
@@ -26,6 +26,8 @@
#include "ScopedPipe.h"
#include "log.h"
+namespace android {
+
// LeakPipe implements a pipe that can transfer vectors of simple objects
// between processes. The pipe is created in the sending process and
// transferred over a socketpair that was created before forking. This ensures
@@ -34,15 +36,13 @@
class LeakPipe {
public:
LeakPipe() {
- int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_);
+ int ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv_);
if (ret < 0) {
- LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
+ MEM_LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
}
}
- ~LeakPipe() {
- Close();
- }
+ ~LeakPipe() { Close(); }
void Close() {
close(sv_[0]);
@@ -77,13 +77,9 @@
public:
LeakPipeBase() : fd_(-1) {}
- ~LeakPipeBase() {
- Close();
- }
+ ~LeakPipeBase() { Close(); }
- void SetFd(int fd) {
- fd_ = fd;
- }
+ void SetFd(int fd) { fd_ = fd; }
void Close() {
close(fd_);
@@ -101,21 +97,21 @@
public:
using LeakPipeBase::LeakPipeBase;
- template<typename T>
+ template <typename T>
bool Send(const T& value) {
ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T)));
if (ret < 0) {
- ALOGE("failed to send value: %s", strerror(errno));
+ MEM_ALOGE("failed to send value: %s", strerror(errno));
return false;
} else if (static_cast<size_t>(ret) != sizeof(T)) {
- ALOGE("eof while writing value");
+ MEM_ALOGE("eof while writing value");
return false;
}
return true;
}
- template<class T, class Alloc = std::allocator<T>>
+ template <class T, class Alloc = std::allocator<T>>
bool SendVector(const std::vector<T, Alloc>& vector) {
size_t size = vector.size() * sizeof(T);
if (!Send(size)) {
@@ -124,10 +120,10 @@
ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, vector.data(), size));
if (ret < 0) {
- ALOGE("failed to send vector: %s", strerror(errno));
+ MEM_ALOGE("failed to send vector: %s", strerror(errno));
return false;
} else if (static_cast<size_t>(ret) != size) {
- ALOGE("eof while writing vector");
+ MEM_ALOGE("eof while writing vector");
return false;
}
@@ -139,21 +135,21 @@
public:
using LeakPipeBase::LeakPipeBase;
- template<typename T>
+ template <typename T>
bool Receive(T* value) {
ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T)));
if (ret < 0) {
- ALOGE("failed to receive value: %s", strerror(errno));
+ MEM_ALOGE("failed to receive value: %s", strerror(errno));
return false;
} else if (static_cast<size_t>(ret) != sizeof(T)) {
- ALOGE("eof while receiving value");
+ MEM_ALOGE("eof while receiving value");
return false;
}
return true;
}
- template<class T, class Alloc = std::allocator<T>>
+ template <class T, class Alloc = std::allocator<T>>
bool ReceiveVector(std::vector<T, Alloc>& vector) {
size_t size = 0;
if (!Receive(&size)) {
@@ -166,10 +162,10 @@
while (size > 0) {
ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, ptr, size));
if (ret < 0) {
- ALOGE("failed to send vector: %s", strerror(errno));
+ MEM_ALOGE("failed to send vector: %s", strerror(errno));
return false;
} else if (ret == 0) {
- ALOGE("eof while reading vector");
+ MEM_ALOGE("eof while reading vector");
return false;
}
size -= ret;
@@ -178,16 +174,11 @@
return true;
}
-
};
- LeakPipeReceiver& Receiver() {
- return receiver_;
- }
+ LeakPipeReceiver& Receiver() { return receiver_; }
- LeakPipeSender& Sender() {
- return sender_;
- }
+ LeakPipeSender& Sender() { return sender_; }
private:
LeakPipeReceiver receiver_;
@@ -198,4 +189,6 @@
int sv_[2];
};
-#endif // LIBMEMUNREACHABLE_LEAK_PIPE_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_LEAK_PIPE_H_
diff --git a/libmemunreachable/LineBuffer.cpp b/libmemunreachable/LineBuffer.cpp
index d3580c0..4ea0542 100644
--- a/libmemunreachable/LineBuffer.cpp
+++ b/libmemunreachable/LineBuffer.cpp
@@ -23,8 +23,10 @@
#include "LineBuffer.h"
-LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len) : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {
-}
+namespace android {
+
+LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len)
+ : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {}
bool LineBuffer::GetLine(char** line, size_t* line_len) {
while (true) {
@@ -60,3 +62,5 @@
bytes_ += bytes;
}
}
+
+} // namespace android
diff --git a/libmemunreachable/LineBuffer.h b/libmemunreachable/LineBuffer.h
index a015c46..cc6cd0c 100644
--- a/libmemunreachable/LineBuffer.h
+++ b/libmemunreachable/LineBuffer.h
@@ -19,6 +19,8 @@
#include <stdint.h>
+namespace android {
+
class LineBuffer {
public:
LineBuffer(int fd, char* buffer, size_t buffer_len);
@@ -33,4 +35,6 @@
size_t bytes_ = 0;
};
-#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H
+} // namespace android
+
+#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H
diff --git a/libmemunreachable/LinkedList.h b/libmemunreachable/LinkedList.h
index 132842d..36fe9fd 100644
--- a/libmemunreachable/LinkedList.h
+++ b/libmemunreachable/LinkedList.h
@@ -17,44 +17,47 @@
#ifndef LIBMEMUNREACHABLE_LINKED_LIST_H_
#define LIBMEMUNREACHABLE_LINKED_LIST_H_
-template<class T>
+namespace android {
+
+template <class T>
class LinkedList {
-public:
- LinkedList() : next_(this), prev_(this), data_() {}
- explicit LinkedList(T data) : LinkedList() {
- data_ = data;
- }
- ~LinkedList() {}
- void insert(LinkedList<T>& node) {
- assert(node.empty());
- node.next_ = this->next_;
- node.next_->prev_ = &node;
- this->next_ = &node;
- node.prev_ = this;
- }
- void remove() {
- this->next_->prev_ = this->prev_;
- this->prev_->next_ = this->next_;
- this->next_ = this;
- this->prev_ = this;
- }
- T data() { return data_; }
- bool empty() { return next_ == this && prev_ == this; }
- LinkedList<T> *next() { return next_; }
-private:
- LinkedList<T> *next_;
- LinkedList<T> *prev_;
- T data_;
+ public:
+ LinkedList() : next_(this), prev_(this), data_() {}
+ explicit LinkedList(T data) : LinkedList() { data_ = data; }
+ ~LinkedList() {}
+ void insert(LinkedList<T>& node) {
+ assert(node.empty());
+ node.next_ = this->next_;
+ node.next_->prev_ = &node;
+ this->next_ = &node;
+ node.prev_ = this;
+ }
+ void remove() {
+ this->next_->prev_ = this->prev_;
+ this->prev_->next_ = this->next_;
+ this->next_ = this;
+ this->prev_ = this;
+ }
+ T data() { return data_; }
+ bool empty() { return next_ == this && prev_ == this; }
+ LinkedList<T>* next() { return next_; }
+
+ private:
+ LinkedList<T>* next_;
+ LinkedList<T>* prev_;
+ T data_;
};
-template<class T>
+template <class T>
class LinkedListHead {
-public:
- LinkedListHead() : node_() {}
- ~LinkedListHead() {}
+ public:
+ LinkedListHead() : node_() {}
+ ~LinkedListHead() {}
-private:
- LinkedList<T> node_;
+ private:
+ LinkedList<T> node_;
};
+} // namespace android
+
#endif
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index ac19a66..24fdc7f 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -15,18 +15,20 @@
*/
#include <inttypes.h>
+#include <string.h>
#include <functional>
#include <iomanip>
#include <mutex>
-#include <string>
#include <sstream>
+#include <string>
#include <unordered_map>
-#include <backtrace.h>
#include <android-base/macros.h>
+#include <backtrace.h>
#include "Allocator.h"
+#include "Binder.h"
#include "HeapWalker.h"
#include "Leak.h"
#include "LeakFolding.h"
@@ -37,30 +39,34 @@
#include "Semaphore.h"
#include "ThreadCapture.h"
-#include "memunreachable/memunreachable.h"
#include "bionic.h"
#include "log.h"
-
-const size_t Leak::contents_length;
+#include "memunreachable/memunreachable.h"
using namespace std::chrono_literals;
+namespace android {
+
+const size_t Leak::contents_length;
+
class MemUnreachable {
public:
- MemUnreachable(pid_t pid, Allocator<void> allocator) : pid_(pid), allocator_(allocator),
- heap_walker_(allocator_) {}
+ MemUnreachable(pid_t pid, Allocator<void> allocator)
+ : pid_(pid), allocator_(allocator), heap_walker_(allocator_) {}
bool CollectAllocations(const allocator::vector<ThreadInfo>& threads,
- const allocator::vector<Mapping>& mappings);
- bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
- size_t* num_leaks, size_t* leak_bytes);
+ const allocator::vector<Mapping>& mappings,
+ const allocator::vector<uintptr_t>& refs);
+ bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit, size_t* num_leaks,
+ size_t* leak_bytes);
size_t Allocations() { return heap_walker_.Allocations(); }
size_t AllocationBytes() { return heap_walker_.AllocationBytes(); }
+
private:
bool ClassifyMappings(const allocator::vector<Mapping>& mappings,
- allocator::vector<Mapping>& heap_mappings,
- allocator::vector<Mapping>& anon_mappings,
- allocator::vector<Mapping>& globals_mappings,
- allocator::vector<Mapping>& stack_mappings);
+ allocator::vector<Mapping>& heap_mappings,
+ allocator::vector<Mapping>& anon_mappings,
+ allocator::vector<Mapping>& globals_mappings,
+ allocator::vector<Mapping>& stack_mappings);
DISALLOW_COPY_AND_ASSIGN(MemUnreachable);
pid_t pid_;
Allocator<void> allocator_;
@@ -68,74 +74,75 @@
};
static void HeapIterate(const Mapping& heap_mapping,
- const std::function<void(uintptr_t, size_t)>& func) {
+ const std::function<void(uintptr_t, size_t)>& func) {
malloc_iterate(heap_mapping.begin, heap_mapping.end - heap_mapping.begin,
- [](uintptr_t base, size_t size, void* arg) {
- auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg);
- (*f)(base, size);
- }, const_cast<void*>(reinterpret_cast<const void*>(&func)));
+ [](uintptr_t base, size_t size, void* arg) {
+ auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg);
+ (*f)(base, size);
+ },
+ const_cast<void*>(reinterpret_cast<const void*>(&func)));
}
bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
- const allocator::vector<Mapping>& mappings) {
- ALOGI("searching process %d for allocations", pid_);
+ const allocator::vector<Mapping>& mappings,
+ const allocator::vector<uintptr_t>& refs) {
+ MEM_ALOGI("searching process %d for allocations", pid_);
allocator::vector<Mapping> heap_mappings{mappings};
allocator::vector<Mapping> anon_mappings{mappings};
allocator::vector<Mapping> globals_mappings{mappings};
allocator::vector<Mapping> stack_mappings{mappings};
- if (!ClassifyMappings(mappings, heap_mappings, anon_mappings,
- globals_mappings, stack_mappings)) {
+ if (!ClassifyMappings(mappings, heap_mappings, anon_mappings, globals_mappings, stack_mappings)) {
return false;
}
for (auto it = heap_mappings.begin(); it != heap_mappings.end(); it++) {
- ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
- HeapIterate(*it, [&](uintptr_t base, size_t size) {
- heap_walker_.Allocation(base, base + size);
- });
+ MEM_ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ HeapIterate(*it,
+ [&](uintptr_t base, size_t size) { heap_walker_.Allocation(base, base + size); });
}
for (auto it = anon_mappings.begin(); it != anon_mappings.end(); it++) {
- ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ MEM_ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
heap_walker_.Allocation(it->begin, it->end);
}
for (auto it = globals_mappings.begin(); it != globals_mappings.end(); it++) {
- ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ MEM_ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
heap_walker_.Root(it->begin, it->end);
}
for (auto thread_it = threads.begin(); thread_it != threads.end(); thread_it++) {
for (auto it = stack_mappings.begin(); it != stack_mappings.end(); it++) {
if (thread_it->stack.first >= it->begin && thread_it->stack.first <= it->end) {
- ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
+ MEM_ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
heap_walker_.Root(thread_it->stack.first, it->end);
}
}
heap_walker_.Root(thread_it->regs);
}
- ALOGI("searching done");
+ heap_walker_.Root(refs);
+
+ MEM_ALOGI("searching done");
return true;
}
-bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks,
- size_t limit, size_t* num_leaks, size_t* leak_bytes) {
- ALOGI("sweeping process %d for unreachable memory", pid_);
+bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
+ size_t* num_leaks, size_t* leak_bytes) {
+ MEM_ALOGI("sweeping process %d for unreachable memory", pid_);
leaks.clear();
if (!heap_walker_.DetectLeaks()) {
return false;
}
-
allocator::vector<Range> leaked1{allocator_};
heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
- ALOGI("sweeping done");
+ MEM_ALOGI("sweeping done");
- ALOGI("folding related leaks");
+ MEM_ALOGI("folding related leaks");
LeakFolding folding(allocator_, heap_walker_);
if (!folding.FoldLeaks()) {
@@ -154,12 +161,12 @@
// in backtrace_map.
leaks.reserve(leaked.size());
- for (auto& it: leaked) {
+ for (auto& it : leaked) {
leaks.emplace_back();
Leak* leak = &leaks.back();
- ssize_t num_backtrace_frames = malloc_backtrace(reinterpret_cast<void*>(it.range.begin),
- leak->backtrace.frames, leak->backtrace.max_frames);
+ ssize_t num_backtrace_frames = malloc_backtrace(
+ reinterpret_cast<void*>(it.range.begin), leak->backtrace.frames, leak->backtrace.max_frames);
if (num_backtrace_frames > 0) {
leak->backtrace.num_frames = num_backtrace_frames;
@@ -185,14 +192,13 @@
leak->referenced_size = it.referenced_size;
leak->total_size = leak->size + leak->referenced_size;
memcpy(leak->contents, reinterpret_cast<void*>(it.range.begin),
- std::min(leak->size, Leak::contents_length));
+ std::min(leak->size, Leak::contents_length));
}
- ALOGI("folding done");
+ MEM_ALOGI("folding done");
- std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) {
- return a.total_size > b.total_size;
- });
+ std::sort(leaks.begin(), leaks.end(),
+ [](const Leak& a, const Leak& b) { return a.total_size > b.total_size; });
if (leaks.size() > limit) {
leaks.resize(limit);
@@ -207,11 +213,10 @@
}
bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings,
- allocator::vector<Mapping>& heap_mappings,
- allocator::vector<Mapping>& anon_mappings,
- allocator::vector<Mapping>& globals_mappings,
- allocator::vector<Mapping>& stack_mappings)
-{
+ allocator::vector<Mapping>& heap_mappings,
+ allocator::vector<Mapping>& anon_mappings,
+ allocator::vector<Mapping>& globals_mappings,
+ allocator::vector<Mapping>& stack_mappings) {
heap_mappings.clear();
anon_mappings.clear();
globals_mappings.clear();
@@ -247,7 +252,8 @@
stack_mappings.emplace_back(*it);
} else if (mapping_name.size() == 0) {
globals_mappings.emplace_back(*it);
- } else if (has_prefix(mapping_name, "[anon:") && mapping_name != "[anon:leak_detector_malloc]") {
+ } else if (has_prefix(mapping_name, "[anon:") &&
+ mapping_name != "[anon:leak_detector_malloc]") {
// TODO(ccross): it would be nice to treat named anonymous mappings as
// possible leaks, but naming something in a .bss or .data section makes
// it impossible to distinguish them from mmaped and then named mappings.
@@ -258,7 +264,7 @@
return true;
}
-template<typename T>
+template <typename T>
static inline const char* plural(T val) {
return (val == 1) ? "" : "s";
}
@@ -276,11 +282,12 @@
/////////////////////////////////////////////
// Collection thread
/////////////////////////////////////////////
- ALOGI("collecting thread info for process %d...", parent_pid);
+ MEM_ALOGI("collecting thread info for process %d...", parent_pid);
ThreadCapture thread_capture(parent_pid, heap);
allocator::vector<ThreadInfo> thread_info(heap);
allocator::vector<Mapping> mappings(heap);
+ allocator::vector<uintptr_t> refs(heap);
// ptrace all the threads
if (!thread_capture.CaptureThreads()) {
@@ -300,6 +307,11 @@
return 1;
}
+ if (!BinderReferences(refs)) {
+ continue_parent_sem.Post();
+ return 1;
+ }
+
// malloc must be enabled to call fork, at_fork handlers take the same
// locks as ScopedDisableMalloc. All threads are paused in ptrace, so
// memory state is still consistent. Unfreeze the original thread so it
@@ -325,7 +337,7 @@
MemUnreachable unreachable{parent_pid, heap};
- if (!unreachable.CollectAllocations(thread_info, mappings)) {
+ if (!unreachable.CollectAllocations(thread_info, mappings, refs)) {
_exit(2);
}
size_t num_allocations = unreachable.Allocations();
@@ -351,7 +363,7 @@
} else {
// Nothing left to do in the collection thread, return immediately,
// releasing all the captured threads.
- ALOGI("collection thread done");
+ MEM_ALOGI("collection thread done");
return 0;
}
}};
@@ -397,15 +409,14 @@
return false;
}
- ALOGI("unreachable memory detection done");
- ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
- info.leak_bytes, info.num_leaks, plural(info.num_leaks),
- info.allocation_bytes, info.num_allocations, plural(info.num_allocations));
+ MEM_ALOGI("unreachable memory detection done");
+ MEM_ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
+ info.leak_bytes, info.num_leaks, plural(info.num_leaks), info.allocation_bytes,
+ info.num_allocations, plural(info.num_allocations));
return true;
}
std::string Leak::ToString(bool log_contents) const {
-
std::ostringstream oss;
oss << " " << std::dec << size;
@@ -468,23 +479,6 @@
return oss.str();
}
-// Figure out the abi based on defined macros.
-#if defined(__arm__)
-#define ABI_STRING "arm"
-#elif defined(__aarch64__)
-#define ABI_STRING "arm64"
-#elif defined(__mips__) && !defined(__LP64__)
-#define ABI_STRING "mips"
-#elif defined(__mips__) && defined(__LP64__)
-#define ABI_STRING "mips64"
-#elif defined(__i386__)
-#define ABI_STRING "x86"
-#elif defined(__x86_64__)
-#define ABI_STRING "x86_64"
-#else
-#error "Unsupported ABI"
-#endif
-
std::string UnreachableMemoryInfo::ToString(bool log_contents) const {
std::ostringstream oss;
oss << " " << leak_bytes << " bytes in ";
@@ -494,8 +488,8 @@
oss << std::endl;
for (auto it = leaks.begin(); it != leaks.end(); it++) {
- oss << it->ToString(log_contents);
- oss << std::endl;
+ oss << it->ToString(log_contents);
+ oss << std::endl;
}
return oss.str();
@@ -504,28 +498,32 @@
std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
UnreachableMemoryInfo info;
if (!GetUnreachableMemory(info, limit)) {
- return "Failed to get unreachable memory\n";
+ return "Failed to get unreachable memory\n"
+ "If you are trying to get unreachable memory from a system app\n"
+ "(like com.android.systemui), disable selinux first using\n"
+ "setenforce 0\n";
}
return info.ToString(log_contents);
}
+} // namespace android
+
bool LogUnreachableMemory(bool log_contents, size_t limit) {
- UnreachableMemoryInfo info;
- if (!GetUnreachableMemory(info, limit)) {
+ android::UnreachableMemoryInfo info;
+ if (!android::GetUnreachableMemory(info, limit)) {
return false;
}
for (auto it = info.leaks.begin(); it != info.leaks.end(); it++) {
- ALOGE("%s", it->ToString(log_contents).c_str());
+ MEM_ALOGE("%s", it->ToString(log_contents).c_str());
}
return true;
}
-
bool NoLeaks() {
- UnreachableMemoryInfo info;
- if (!GetUnreachableMemory(info, 0)) {
+ android::UnreachableMemoryInfo info;
+ if (!android::GetUnreachableMemory(info, 0)) {
return false;
}
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
index 57b2321..9a06870 100644
--- a/libmemunreachable/ProcessMappings.cpp
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include <inttypes.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <string.h>
#include <unistd.h>
@@ -25,6 +25,8 @@
#include "ProcessMappings.h"
#include "log.h"
+namespace android {
+
// This function is not re-entrant since it uses a static buffer for
// the line data.
bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) {
@@ -42,8 +44,8 @@
int name_pos;
char perms[5];
Mapping mapping{};
- if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n",
- &mapping.begin, &mapping.end, perms, &name_pos) == 3) {
+ if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n", &mapping.begin,
+ &mapping.end, perms, &name_pos) == 3) {
if (perms[0] == 'r') {
mapping.read = true;
}
@@ -64,3 +66,5 @@
}
return true;
}
+
+} // namespace android
diff --git a/libmemunreachable/ProcessMappings.h b/libmemunreachable/ProcessMappings.h
index d3b7496..a0e97e9 100644
--- a/libmemunreachable/ProcessMappings.h
+++ b/libmemunreachable/ProcessMappings.h
@@ -19,6 +19,8 @@
#include "Allocator.h"
+namespace android {
+
struct Mapping {
uintptr_t begin;
uintptr_t end;
@@ -33,4 +35,6 @@
// the line data.
bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings);
-#endif // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
index 4e3c41e..aca2a82 100644
--- a/libmemunreachable/PtracerThread.cpp
+++ b/libmemunreachable/PtracerThread.cpp
@@ -23,17 +23,19 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <unistd.h>
#include "android-base/macros.h"
+#include "PtracerThread.h"
#include "anon_vma_naming.h"
#include "log.h"
-#include "PtracerThread.h"
+
+namespace android {
class Stack {
public:
@@ -41,7 +43,7 @@
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
page_size_ = sysconf(_SC_PAGE_SIZE);
- size_ += page_size_*2; // guard pages
+ size_ += page_size_ * 2; // guard pages
base_ = mmap(NULL, size_, prot, flags, -1, 0);
if (base_ == MAP_FAILED) {
base_ = NULL;
@@ -52,25 +54,23 @@
mprotect(base_, page_size_, PROT_NONE);
mprotect(top(), page_size_, PROT_NONE);
};
- ~Stack() {
- munmap(base_, size_);
- };
+ ~Stack() { munmap(base_, size_); };
void* top() {
return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_) + size_ - page_size_);
};
+
private:
DISALLOW_COPY_AND_ASSIGN(Stack);
- void *base_;
+ void* base_;
size_t size_;
size_t page_size_;
};
-PtracerThread::PtracerThread(const std::function<int()>& func) :
- child_pid_(0) {
+PtracerThread::PtracerThread(const std::function<int()>& func) : child_pid_(0) {
stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN);
if (stack_->top() == nullptr) {
- LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
+ MEM_LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
}
func_ = std::function<int()>{[&, func]() -> int {
@@ -93,16 +93,15 @@
std::unique_lock<std::mutex> lk(m_);
// Convert from void(*)(void*) to lambda with captures
- auto proxy = [](void *arg) -> int {
+ auto proxy = [](void* arg) -> int {
prctl(PR_SET_NAME, "libmemunreachable ptrace thread");
return (*reinterpret_cast<std::function<int()>*>(arg))();
};
- child_pid_ = clone(proxy, stack_->top(),
- CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/,
- reinterpret_cast<void*>(&func_));
+ child_pid_ = clone(proxy, stack_->top(), CLONE_VM | CLONE_FS | CLONE_FILES /*|CLONE_UNTRACED*/,
+ reinterpret_cast<void*>(&func_));
if (child_pid_ < 0) {
- ALOGE("failed to clone child: %s", strerror(errno));
+ MEM_ALOGE("failed to clone child: %s", strerror(errno));
return false;
}
@@ -120,7 +119,7 @@
int status;
int ret = TEMP_FAILURE_RETRY(waitpid(child_pid_, &status, __WALL));
if (ret < 0) {
- ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
+ MEM_ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
return -1;
}
@@ -131,7 +130,7 @@
} else if (WIFSIGNALED(status)) {
return -WTERMSIG(status);
} else {
- ALOGE("unexpected status %x", status);
+ MEM_ALOGE("unexpected status %x", status);
return -1;
}
}
@@ -151,3 +150,5 @@
void PtracerThread::ClearTracer() {
prctl(PR_SET_PTRACER, 0);
}
+
+} // namespace android
diff --git a/libmemunreachable/PtracerThread.h b/libmemunreachable/PtracerThread.h
index f88b599..4f9c420 100644
--- a/libmemunreachable/PtracerThread.h
+++ b/libmemunreachable/PtracerThread.h
@@ -24,6 +24,8 @@
#include "Allocator.h"
+namespace android {
+
class Stack;
// PtracerThread is similar to std::thread, except that it creates a "thread"
@@ -36,6 +38,7 @@
~PtracerThread();
bool Start();
int Join();
+
private:
void SetTracer(pid_t);
void ClearTracer();
@@ -47,4 +50,6 @@
pid_t child_pid_;
};
-#endif // LIBMEMUNREACHABLE_PTRACER_THREAD_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_PTRACER_THREAD_H_
diff --git a/libmemunreachable/ScopedAlarm.h b/libmemunreachable/ScopedAlarm.h
index 287f479..bb50b9e 100644
--- a/libmemunreachable/ScopedAlarm.h
+++ b/libmemunreachable/ScopedAlarm.h
@@ -23,15 +23,15 @@
#include <chrono>
#include <functional>
+namespace android {
+
class ScopedAlarm {
public:
ScopedAlarm(std::chrono::microseconds us, std::function<void()> func) {
func_ = func;
- struct sigaction oldact{};
- struct sigaction act{};
- act.sa_handler = [](int) {
- ScopedAlarm::func_();
- };
+ struct sigaction oldact {};
+ struct sigaction act {};
+ act.sa_handler = [](int) { ScopedAlarm::func_(); };
sigaction(SIGALRM, &act, &oldact);
std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
@@ -43,11 +43,15 @@
~ScopedAlarm() {
itimerval t = itimerval{};
setitimer(ITIMER_REAL, &t, NULL);
- struct sigaction act{};
+ struct sigaction act {};
act.sa_handler = SIG_DFL;
sigaction(SIGALRM, &act, NULL);
}
+
private:
static std::function<void()> func_;
};
+
+} // namespace android
+
#endif
diff --git a/libmemunreachable/ScopedDisableMalloc.h b/libmemunreachable/ScopedDisableMalloc.h
index 758d317..655e826 100644
--- a/libmemunreachable/ScopedDisableMalloc.h
+++ b/libmemunreachable/ScopedDisableMalloc.h
@@ -21,16 +21,16 @@
#include "android-base/macros.h"
+#include "ScopedAlarm.h"
#include "bionic.h"
#include "log.h"
-#include "ScopedAlarm.h"
-class DisableMallocGuard{
+namespace android {
+
+class DisableMallocGuard {
public:
- DisableMallocGuard() : disabled_(false){}
- ~DisableMallocGuard() {
- Enable();
- }
+ DisableMallocGuard() : disabled_(false) {}
+ ~DisableMallocGuard() { Enable(); }
void Disable() {
if (!disabled_) {
@@ -45,6 +45,7 @@
disabled_ = false;
}
}
+
private:
DISALLOW_COPY_AND_ASSIGN(DisableMallocGuard);
bool disabled_;
@@ -59,13 +60,9 @@
// here.
class ScopedDisableMalloc {
public:
- ScopedDisableMalloc() {
- disable_malloc_.Disable();
- }
+ ScopedDisableMalloc() { disable_malloc_.Disable(); }
- ~ScopedDisableMalloc() {
- disable_malloc_.Enable();
- }
+ ~ScopedDisableMalloc() { disable_malloc_.Enable(); }
private:
DISALLOW_COPY_AND_ASSIGN(ScopedDisableMalloc);
@@ -74,18 +71,15 @@
class ScopedDisableMallocTimeout {
public:
- explicit ScopedDisableMallocTimeout(std::chrono::milliseconds timeout = std::chrono::milliseconds(2000)) :
- timeout_(timeout), timed_out_(false), disable_malloc_() {
+ explicit ScopedDisableMallocTimeout(
+ std::chrono::milliseconds timeout = std::chrono::milliseconds(2000))
+ : timeout_(timeout), timed_out_(false), disable_malloc_() {
Disable();
}
- ~ScopedDisableMallocTimeout() {
- Enable();
- }
+ ~ScopedDisableMallocTimeout() { Enable(); }
- bool timed_out() {
- return timed_out_;
- }
+ bool timed_out() { return timed_out_; }
void Enable() {
disable_malloc_.Enable();
@@ -110,4 +104,6 @@
DisableMallocGuard disable_malloc_;
};
-#endif // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index 9beef9a..adabfd8 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -21,36 +21,32 @@
#include "log.h"
+namespace android {
+
class ScopedPipe {
public:
ScopedPipe() : pipefd_{-1, -1} {
int ret = pipe2(pipefd_, O_CLOEXEC);
if (ret < 0) {
- LOG_ALWAYS_FATAL("failed to open pipe");
+ MEM_LOG_ALWAYS_FATAL("failed to open pipe");
}
}
- ~ScopedPipe() {
- Close();
- }
+ ~ScopedPipe() { Close(); }
ScopedPipe(ScopedPipe&& other) {
SetReceiver(other.ReleaseReceiver());
SetSender(other.ReleaseSender());
}
- ScopedPipe& operator = (ScopedPipe&& other) {
+ ScopedPipe& operator=(ScopedPipe&& other) {
SetReceiver(other.ReleaseReceiver());
SetSender(other.ReleaseSender());
return *this;
}
- void CloseReceiver() {
- close(ReleaseReceiver());
- }
+ void CloseReceiver() { close(ReleaseReceiver()); }
- void CloseSender() {
- close(ReleaseSender());
- }
+ void CloseSender() { close(ReleaseSender()); }
void Close() {
CloseReceiver();
@@ -78,4 +74,7 @@
int pipefd_[2];
};
+
+} // namespace android
+
#endif
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index 1fd9d4d..ff53fad 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -26,33 +26,29 @@
#include "log.h"
+namespace android {
+
class ScopedSignalHandler {
public:
using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>;
explicit ScopedSignalHandler(Allocator<Fn> allocator) : allocator_(allocator), signal_(-1) {}
- ~ScopedSignalHandler() {
- reset();
- }
+ ~ScopedSignalHandler() { reset(); }
template <class F>
void install(int signal, F&& f) {
- LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
+ if (signal_ != -1) MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed");
handler_ = SignalFn(std::allocator_arg, allocator_,
- [=](int signal, siginfo_t* si, void* uctx) {
- f(*this, signal, si, uctx);
- });
+ [=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
- struct sigaction act{};
- act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) {
- handler_(signal, si, uctx);
- };
+ struct sigaction act {};
+ act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) { handler_(signal, si, uctx); };
act.sa_flags = SA_SIGINFO;
int ret = sigaction(signal, &act, &old_act_);
if (ret < 0) {
- LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
+ MEM_LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
}
signal_ = signal;
@@ -62,14 +58,13 @@
if (signal_ != -1) {
int ret = sigaction(signal_, &old_act_, NULL);
if (ret < 0) {
- ALOGE("failed to uninstall segfault handler");
+ MEM_ALOGE("failed to uninstall segfault handler");
}
handler_ = SignalFn{};
signal_ = -1;
}
}
-
private:
using SignalFn = std::function<void(int, siginfo_t*, void*)>;
DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler);
@@ -81,4 +76,6 @@
static SignalFn handler_;
};
-#endif // LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
diff --git a/libmemunreachable/Semaphore.h b/libmemunreachable/Semaphore.h
index 6bcf4ea..cd73972 100644
--- a/libmemunreachable/Semaphore.h
+++ b/libmemunreachable/Semaphore.h
@@ -22,6 +22,8 @@
#include "android-base/macros.h"
+namespace android {
+
class Semaphore {
public:
explicit Semaphore(int count = 0) : count_(count) {}
@@ -29,7 +31,7 @@
void Wait(std::chrono::milliseconds ms) {
std::unique_lock<std::mutex> lk(m_);
- cv_.wait_for(lk, ms, [&]{
+ cv_.wait_for(lk, ms, [&] {
if (count_ > 0) {
count_--;
return true;
@@ -44,6 +46,7 @@
}
cv_.notify_one();
}
+
private:
DISALLOW_COPY_AND_ASSIGN(Semaphore);
@@ -52,5 +55,6 @@
std::condition_variable cv_;
};
+} // namespace android
-#endif // LIBMEMUNREACHABLE_SEMAPHORE_H_
+#endif // LIBMEMUNREACHABLE_SEMAPHORE_H_
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h
index 2546341..355679f 100644
--- a/libmemunreachable/Tarjan.h
+++ b/libmemunreachable/Tarjan.h
@@ -24,7 +24,9 @@
#include "Allocator.h"
-template<class T>
+namespace android {
+
+template <class T>
class Node {
public:
allocator::set<Node<T>*> references_in;
@@ -34,39 +36,41 @@
T* ptr;
- Node(T* ptr, Allocator<Node> allocator) : references_in(allocator), references_out(allocator),
- ptr(ptr) {};
+ Node(T* ptr, Allocator<Node> allocator)
+ : references_in(allocator), references_out(allocator), ptr(ptr){};
Node(Node&& rhs) = default;
void Edge(Node<T>* ref) {
references_out.emplace(ref);
ref->references_in.emplace(this);
}
- template<class F>
+ template <class F>
void Foreach(F&& f) {
- for (auto& node: references_out) {
+ for (auto& node : references_out) {
f(node->ptr);
}
}
+
private:
DISALLOW_COPY_AND_ASSIGN(Node<T>);
};
-template<class T>
+template <class T>
using Graph = allocator::vector<Node<T>*>;
-template<class T>
+template <class T>
using SCC = allocator::vector<Node<T>*>;
-template<class T>
+template <class T>
using SCCList = allocator::vector<SCC<T>>;
-template<class T>
+template <class T>
class TarjanAlgorithm {
public:
- explicit TarjanAlgorithm(Allocator<void> allocator) : index_(0),
- stack_(allocator), components_(allocator) {}
+ explicit TarjanAlgorithm(Allocator<void> allocator)
+ : index_(0), stack_(allocator), components_(allocator) {}
void Execute(Graph<T>& graph, SCCList<T>& out);
+
private:
static constexpr size_t UNDEFINED_INDEX = static_cast<size_t>(-1);
void Tarjan(Node<T>* vertex, Graph<T>& graph);
@@ -76,17 +80,17 @@
SCCList<T> components_;
};
-template<class T>
+template <class T>
void TarjanAlgorithm<T>::Execute(Graph<T>& graph, SCCList<T>& out) {
stack_.clear();
components_.clear();
index_ = 0;
- for (auto& it: graph) {
+ for (auto& it : graph) {
it->index = UNDEFINED_INDEX;
it->lowlink = UNDEFINED_INDEX;
}
- for (auto& it: graph) {
+ for (auto& it : graph) {
if (it->index == UNDEFINED_INDEX) {
Tarjan(it, graph);
}
@@ -94,14 +98,14 @@
out.swap(components_);
}
-template<class T>
+template <class T>
void TarjanAlgorithm<T>::Tarjan(Node<T>* vertex, Graph<T>& graph) {
assert(vertex->index == UNDEFINED_INDEX);
vertex->index = index_;
vertex->lowlink = index_;
index_++;
stack_.push_back(vertex);
- for (auto& it: vertex->references_out) {
+ for (auto& it : vertex->references_out) {
Node<T>* vertex_next = it;
if (vertex_next->index == UNDEFINED_INDEX) {
Tarjan(vertex_next, graph);
@@ -123,10 +127,12 @@
}
}
-template<class T>
+template <class T>
void Tarjan(Graph<T>& graph, SCCList<T>& out) {
TarjanAlgorithm<T> tarjan{graph.get_allocator()};
tarjan.Execute(graph, out);
}
-#endif // LIBMEMUNREACHABLE_TARJAN_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_TARJAN_H_
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
index 9155c29..45eb55d 100644
--- a/libmemunreachable/ThreadCapture.cpp
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -21,13 +21,13 @@
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
-#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/wait.h>
+#include <unistd.h>
#include <map>
#include <memory>
@@ -39,6 +39,8 @@
#include "Allocator.h"
#include "log.h"
+namespace android {
+
// bionic interfaces used:
// atoi
// strlcat
@@ -50,12 +52,12 @@
// Convert a pid > 0 to a string. sprintf might allocate, so we can't use it.
// Returns a pointer somewhere in buf to a null terminated string, or NULL
// on error.
-static char *pid_to_str(char *buf, size_t len, pid_t pid) {
+static char* pid_to_str(char* buf, size_t len, pid_t pid) {
if (pid <= 0) {
return nullptr;
}
- char *ptr = buf + len - 1;
+ char* ptr = buf + len - 1;
*ptr = 0;
while (pid > 0) {
ptr--;
@@ -79,6 +81,7 @@
bool ReleaseThread(pid_t tid);
bool CapturedThreadInfo(ThreadInfoList& threads);
void InjectTestFunc(std::function<void(pid_t)>&& f) { inject_test_func_ = f; }
+
private:
int CaptureThread(pid_t tid);
bool ReleaseThread(pid_t tid, unsigned int signal);
@@ -92,9 +95,8 @@
std::function<void(pid_t)> inject_test_func_;
};
-ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator) :
- captured_threads_(allocator), allocator_(allocator), pid_(pid) {
-}
+ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator)
+ : captured_threads_(allocator), allocator_(allocator), pid_(pid) {}
bool ThreadCaptureImpl::ListThreads(TidList& tids) {
tids.clear();
@@ -110,23 +112,23 @@
android::base::unique_fd fd(open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
if (fd == -1) {
- ALOGE("failed to open %s: %s", path, strerror(errno));
+ MEM_ALOGE("failed to open %s: %s", path, strerror(errno));
return false;
}
struct linux_dirent64 {
- uint64_t d_ino;
- int64_t d_off;
- uint16_t d_reclen;
- char d_type;
- char d_name[];
+ uint64_t d_ino;
+ int64_t d_off;
+ uint16_t d_reclen;
+ char d_type;
+ char d_name[];
} __attribute((packed));
char dirent_buf[4096];
ssize_t nread;
do {
nread = syscall(SYS_getdents64, fd.get(), dirent_buf, sizeof(dirent_buf));
if (nread < 0) {
- ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
+ MEM_ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
return false;
} else if (nread > 0) {
ssize_t off = 0;
@@ -177,8 +179,7 @@
void ThreadCaptureImpl::PtraceDetach(pid_t tid, unsigned int signal) {
void* sig_ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(signal));
if (ptrace(PTRACE_DETACH, tid, NULL, sig_ptr) < 0 && errno != ESRCH) {
- ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_,
- strerror(errno));
+ MEM_ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_, strerror(errno));
}
}
@@ -187,8 +188,7 @@
int ThreadCaptureImpl::PtraceAttach(pid_t tid) {
int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL);
if (ret < 0) {
- ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_,
- strerror(errno));
+ MEM_ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_, strerror(errno));
return -1;
}
@@ -200,8 +200,7 @@
if (errno == ESRCH) {
return 0;
} else {
- ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_,
- strerror(errno));
+ MEM_ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_, strerror(errno));
PtraceDetach(tid, 0);
return -1;
}
@@ -212,15 +211,14 @@
bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) {
thread_info.tid = tid;
- const unsigned int max_num_regs = 128; // larger than number of registers on any device
+ const unsigned int max_num_regs = 128; // larger than number of registers on any device
uintptr_t regs[max_num_regs];
struct iovec iovec;
iovec.iov_base = ®s;
iovec.iov_len = sizeof(regs);
if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) {
- ALOGE("ptrace getregset for thread %d of process %d failed: %s",
- tid, pid_, strerror(errno));
+ MEM_ALOGE("ptrace getregset for thread %d of process %d failed: %s", tid, pid_, strerror(errno));
return false;
}
@@ -247,7 +245,7 @@
thread_info.stack = std::pair<uintptr_t, uintptr_t>(regs[sp], 0);
- return true;
+ return true;
}
int ThreadCaptureImpl::CaptureThread(pid_t tid) {
@@ -258,21 +256,19 @@
int status = 0;
if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) < 0) {
- ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_,
- strerror(errno));
+ MEM_ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_, strerror(errno));
PtraceDetach(tid, 0);
return -1;
}
if (!WIFSTOPPED(status)) {
- ALOGE("thread %d of process %d was not paused after waitpid, killed?",
- tid, pid_);
+ MEM_ALOGE("thread %d of process %d was not paused after waitpid, killed?", tid, pid_);
return 0;
}
unsigned int resume_signal = 0;
- unsigned int signal = WSTOPSIG(status);
+ unsigned int signal = WSTOPSIG(status);
if ((status >> 16) == PTRACE_EVENT_STOP) {
switch (signal) {
case SIGSTOP:
@@ -285,8 +281,8 @@
// normal ptrace interrupt stop
break;
default:
- ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d",
- signal, tid, pid_);
+ MEM_ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d", signal,
+ tid, pid_);
return -1;
}
} else {
@@ -313,7 +309,7 @@
bool ThreadCaptureImpl::ReleaseThreads() {
bool ret = true;
- for (auto it = captured_threads_.begin(); it != captured_threads_.end(); ) {
+ for (auto it = captured_threads_.begin(); it != captured_threads_.end();) {
if (ReleaseThread(it->first, it->second)) {
it = captured_threads_.erase(it);
} else {
@@ -367,3 +363,5 @@
void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) {
impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f));
}
+
+} // namespace android
diff --git a/libmemunreachable/ThreadCapture.h b/libmemunreachable/ThreadCapture.h
index 1022cad..961cb60 100644
--- a/libmemunreachable/ThreadCapture.h
+++ b/libmemunreachable/ThreadCapture.h
@@ -21,6 +21,8 @@
#include "Allocator.h"
+namespace android {
+
struct ThreadInfo {
pid_t tid;
allocator::vector<uintptr_t> regs;
@@ -33,7 +35,7 @@
class ThreadCaptureImpl;
class ThreadCapture {
-public:
+ public:
ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator);
~ThreadCapture();
@@ -44,11 +46,13 @@
bool CapturedThreadInfo(ThreadInfoList& threads);
void InjectTestFunc(std::function<void(pid_t)>&& f);
-private:
+ private:
ThreadCapture(const ThreadCapture&) = delete;
void operator=(const ThreadCapture&) = delete;
Allocator<ThreadCaptureImpl>::unique_ptr impl_;
};
+} // namespace android
+
#endif
diff --git a/libmemunreachable/anon_vma_naming.h b/libmemunreachable/anon_vma_naming.h
index 1e4ade1..fb31e41 100644
--- a/libmemunreachable/anon_vma_naming.h
+++ b/libmemunreachable/anon_vma_naming.h
@@ -19,7 +19,7 @@
#include <sys/prctl.h>
-#define PR_SET_VMA 0x53564d41
-#define PR_SET_VMA_ANON_NAME 0
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
-#endif // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+#endif // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
diff --git a/libmemunreachable/bionic.h b/libmemunreachable/bionic.h
index 83d07a8..dd1ec79 100644
--- a/libmemunreachable/bionic.h
+++ b/libmemunreachable/bionic.h
@@ -17,9 +17,9 @@
#ifndef LIBMEMUNREACHABLE_BIONIC_H_
#define LIBMEMUNREACHABLE_BIONIC_H_
-#include <sys/cdefs.h>
#include <stdint.h>
#include <stdlib.h>
+#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -27,9 +27,9 @@
extern void malloc_disable();
extern void malloc_enable();
extern int malloc_iterate(uintptr_t base, size_t size,
- void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+ void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
extern ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
__END_DECLS
-#endif // LIBMEMUNREACHABLE_BIONIC_H_
+#endif // LIBMEMUNREACHABLE_BIONIC_H_
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index 9b227fd..438fcaf 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -17,12 +17,15 @@
#ifndef LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
#define LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
+#include <string.h>
#include <sys/cdefs.h>
#ifdef __cplusplus
-#include <vector>
#include <string>
+#include <vector>
+
+namespace android {
struct Leak {
uintptr_t begin;
@@ -73,6 +76,8 @@
std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100);
+} // namespace android
+
#endif
__BEGIN_DECLS
@@ -83,4 +88,4 @@
__END_DECLS
-#endif // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
+#endif // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
index cdfbfd9..44c5f85 100644
--- a/libmemunreachable/log.h
+++ b/libmemunreachable/log.h
@@ -19,6 +19,39 @@
#define LOG_TAG "libmemunreachable"
+#if defined(__ANDROID__)
+
+#include <async_safe/log.h>
+
+#define MEM_ALOGE(...) async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGW(...) async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGI(...) async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGV_IMPL(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+
+#ifdef NDEBUG
+#define MEM_ALOGV(...) \
+ do { \
+ if (0) { \
+ MEM_ALOGV_IMPL(__VA_ARGS__); \
+ } \
+ } while (0)
+#else
+#define MEM_ALOGV(...) MEM_ALOGV_IMPL(__VA_ARGS__)
+#endif
+
+#define MEM_LOG_ALWAYS_FATAL(...) async_safe_fatal(__VA_ARGS__)
+
+#else
+
#include <log/log.h>
-#endif // LIBMEMUNREACHABLE_LOG_H_
+#define MEM_ALOGW ALOGW
+#define MEM_ALOGE ALOGE
+#define MEM_ALOGV ALOGV
+#define MEM_ALOGI ALOGI
+
+#define MEM_LOG_ALWAYS_FATAL LOG_ALWAYS_FATAL
+
+#endif
+
+#endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libmemunreachable/tests/Allocator_test.cpp b/libmemunreachable/tests/Allocator_test.cpp
index 21c8218..8991a7b 100644
--- a/libmemunreachable/tests/Allocator_test.cpp
+++ b/libmemunreachable/tests/Allocator_test.cpp
@@ -16,44 +16,44 @@
#include <Allocator.h>
-#include <gtest/gtest.h>
#include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
+namespace android {
std::function<void()> ScopedAlarm::func_;
class AllocatorTest : public testing::Test {
protected:
AllocatorTest() : heap(), disable_malloc_() {}
- virtual void SetUp() {
- heap_count = 0;
- }
+ virtual void SetUp() { heap_count = 0; }
virtual void TearDown() {
ASSERT_EQ(heap_count, 0);
ASSERT_TRUE(heap.empty());
ASSERT_FALSE(disable_malloc_.timed_out());
}
Heap heap;
+
private:
ScopedDisableMallocTimeout disable_malloc_;
};
TEST_F(AllocatorTest, simple) {
Allocator<char[100]> allocator(heap);
- void *ptr = allocator.allocate();
+ void* ptr = allocator.allocate();
ASSERT_TRUE(ptr != NULL);
allocator.deallocate(ptr);
}
TEST_F(AllocatorTest, multiple) {
Allocator<char[100]> allocator(heap);
- void *ptr1 = allocator.allocate();
+ void* ptr1 = allocator.allocate();
ASSERT_TRUE(ptr1 != NULL);
- void *ptr2 = allocator.allocate();
+ void* ptr2 = allocator.allocate();
ASSERT_TRUE(ptr2 != NULL);
ASSERT_NE(ptr1, ptr2);
allocator.deallocate(ptr1);
- void *ptr3 = allocator.allocate();
+ void* ptr3 = allocator.allocate();
ASSERT_EQ(ptr1, ptr3);
allocator.deallocate(ptr3);
allocator.deallocate(ptr2);
@@ -63,7 +63,7 @@
const int num = 4096;
const int size = 128;
Allocator<char[size]> allocator(heap);
- void *ptr[num];
+ void* ptr[num];
for (int i = 0; i < num; i++) {
ptr[i] = allocator.allocate();
memset(ptr[i], 0xaa, size);
@@ -87,7 +87,7 @@
TEST_F(AllocatorTest, large) {
const size_t size = 1024 * 1024;
Allocator<char[size]> allocator(heap);
- void *ptr = allocator.allocate();
+ void* ptr = allocator.allocate();
memset(ptr, 0xaa, size);
allocator.deallocate(ptr);
}
@@ -96,7 +96,7 @@
const int num = 128;
const int size = 1024 * 1024;
Allocator<char[size]> allocator(heap);
- void *ptr[num];
+ void* ptr[num];
for (int i = 0; i < num; i++) {
ptr[i] = allocator.allocate();
memset(ptr[i], 0xaa, size);
@@ -172,3 +172,5 @@
ASSERT_NE(ptr, nullptr);
}
+
+} // namespace android
diff --git a/libmemunreachable/tests/AndroidTest.xml b/libmemunreachable/tests/AndroidTest.xml
new file mode 100644
index 0000000..604c0ec
--- /dev/null
+++ b/libmemunreachable/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for memunreachable_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="memunreachable_test->/data/local/tmp/memunreachable_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="memunreachable_test" />
+ </test>
+</configuration>
diff --git a/libmemunreachable/tests/Binder_test.cpp b/libmemunreachable/tests/Binder_test.cpp
new file mode 100644
index 0000000..6e85d5a
--- /dev/null
+++ b/libmemunreachable/tests/Binder_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 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 <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <gtest/gtest.h>
+
+#include "Allocator.h"
+#include "Binder.h"
+
+namespace android {
+
+static const String16 service_name("test.libmemunreachable_binder");
+
+class BinderService : public BBinder {
+ public:
+ BinderService() = default;
+ virtual ~BinderService() = default;
+
+ virtual status_t onTransact(uint32_t /*code*/, const Parcel& data, Parcel* reply,
+ uint32_t /*flags*/ = 0) {
+ reply->writeStrongBinder(ref);
+ ref = data.readStrongBinder();
+ return 0;
+ }
+
+ private:
+ sp<IBinder> ref;
+};
+
+class BinderObject : public BBinder {
+ public:
+ BinderObject() = default;
+ ~BinderObject() = default;
+};
+
+class ServiceProcess {
+ public:
+ ServiceProcess() : child_(0) {}
+ ~ServiceProcess() { Stop(); }
+
+ bool Run() {
+ pid_t ret = fork();
+ if (ret < 0) {
+ return false;
+ } else if (ret == 0) {
+ // child
+ _exit(Service());
+ } else {
+ // parent
+ child_ = ret;
+ return true;
+ }
+ }
+
+ bool Stop() {
+ if (child_ > 0) {
+ if (kill(child_, SIGTERM)) {
+ return false;
+ }
+ int status = 0;
+ if (TEMP_FAILURE_RETRY(waitpid(child_, &status, 0)) != child_) {
+ return false;
+ }
+ child_ = 0;
+ return WIFEXITED(status) && WEXITSTATUS(status) == 0;
+ }
+
+ return true;
+ }
+
+ int Service() {
+ sp<ProcessState> proc{ProcessState::self()};
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (sm == nullptr) {
+ fprintf(stderr, "Failed to get service manager\n");
+ return 1;
+ }
+ if (sm->addService(service_name, new BinderService()) != OK) {
+ fprintf(stderr, "Failed to add test service\n");
+ return 1;
+ }
+ proc->startThreadPool();
+ pause();
+ return 0;
+ }
+
+ private:
+ pid_t child_;
+};
+
+class BinderTest : public ::testing::Test {
+ protected:
+ ServiceProcess service_process_;
+};
+
+TEST_F(BinderTest, binder) {
+ ServiceProcess service_process;
+ ASSERT_TRUE(service_process.Run());
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ ASSERT_TRUE(sm != nullptr);
+
+ // A small sleep allows the service to start, which
+ // prevents a longer sleep in getService.
+ usleep(100000);
+
+ sp<IBinder> service = sm->getService(service_name);
+ ASSERT_TRUE(service != nullptr);
+
+ sp<IBinder> binder{new BinderObject()};
+
+ Parcel send;
+ Parcel reply;
+
+ send.writeStrongBinder(binder);
+ status_t rv = service->transact(0, send, &reply);
+ ASSERT_EQ(static_cast<status_t>(OK), rv);
+
+ Heap heap;
+ allocator::vector<uintptr_t> refs{heap};
+
+ ASSERT_TRUE(BinderReferences(refs));
+
+ bool found_ref = false;
+ for (auto ref : refs) {
+ if (ref == reinterpret_cast<uintptr_t>(binder.get())) {
+ found_ref = true;
+ }
+ }
+
+ ASSERT_TRUE(found_ref);
+}
+
+} // namespace android
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
index ea5c22c..f446719 100644
--- a/libmemunreachable/tests/DisableMalloc_test.cpp
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -19,11 +19,13 @@
#include <chrono>
#include <functional>
-#include <gtest/gtest.h>
#include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
using namespace std::chrono_literals;
+namespace android {
+
class DisableMallocTest : public ::testing::Test {
protected:
void alarm(std::chrono::microseconds us) {
@@ -36,73 +38,84 @@
};
TEST_F(DisableMallocTest, reenable) {
- ASSERT_EXIT({
- alarm(100ms);
- void *ptr1 = malloc(128);
- ASSERT_NE(ptr1, nullptr);
- free(ptr1);
- {
- ScopedDisableMalloc disable_malloc;
- }
- void *ptr2 = malloc(128);
- ASSERT_NE(ptr2, nullptr);
- free(ptr2);
- _exit(1);
- }, ::testing::ExitedWithCode(1), "");
+ ASSERT_EXIT(
+ {
+ alarm(100ms);
+ void* ptr1 = malloc(128);
+ ASSERT_NE(ptr1, nullptr);
+ free(ptr1);
+ { ScopedDisableMalloc disable_malloc; }
+ void* ptr2 = malloc(128);
+ ASSERT_NE(ptr2, nullptr);
+ free(ptr2);
+ _exit(1);
+ },
+ ::testing::ExitedWithCode(1), "");
}
TEST_F(DisableMallocTest, deadlock_allocate) {
- ASSERT_DEATH({
- void *ptr = malloc(128);
- ASSERT_NE(ptr, nullptr);
- free(ptr);
- {
- alarm(100ms);
- ScopedDisableMalloc disable_malloc;
- void* ptr = malloc(128);
- ASSERT_NE(ptr, nullptr);
- free(ptr);
- }
- }, "");
+ ASSERT_DEATH(
+ {
+ void* ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ free(ptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ void* ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ free(ptr);
+ }
+ },
+ "");
}
TEST_F(DisableMallocTest, deadlock_new) {
- ASSERT_DEATH({
- char* ptr = new(char);
- ASSERT_NE(ptr, nullptr);
- delete(ptr);
- {
- alarm(100ms);
- ScopedDisableMalloc disable_malloc;
- char* ptr = new(char);
- ASSERT_NE(ptr, nullptr);
- delete(ptr);
- }
- }, "");
+ ASSERT_DEATH(
+ {
+ // C++ allows `new Foo` to be replaced with a stack allocation or merged
+ // with future `new Foo` expressions, provided certain conditions are
+ // met [expr.new/10]. None of this applies to `operator new(size_t)`.
+ void* ptr = ::operator new(1);
+ ASSERT_NE(ptr, nullptr);
+ ::operator delete(ptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ void* ptr = ::operator new(1);
+ ASSERT_NE(ptr, nullptr);
+ ::operator delete(ptr);
+ }
+ },
+ "");
}
TEST_F(DisableMallocTest, deadlock_delete) {
- ASSERT_DEATH({
- char* ptr = new(char);
- ASSERT_NE(ptr, nullptr);
- {
- alarm(250ms);
- ScopedDisableMalloc disable_malloc;
- delete(ptr);
- }
- }, "");
+ ASSERT_DEATH(
+ {
+ void* ptr = ::operator new(1);
+ ASSERT_NE(ptr, nullptr);
+ {
+ alarm(250ms);
+ ScopedDisableMalloc disable_malloc;
+ ::operator delete(ptr);
+ }
+ },
+ "");
}
TEST_F(DisableMallocTest, deadlock_free) {
- ASSERT_DEATH({
- void *ptr = malloc(128);
- ASSERT_NE(ptr, nullptr);
- {
- alarm(100ms);
- ScopedDisableMalloc disable_malloc;
- free(ptr);
- }
- }, "");
+ ASSERT_DEATH(
+ {
+ void* ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ free(ptr);
+ }
+ },
+ "");
}
TEST_F(DisableMallocTest, deadlock_fork) {
@@ -111,6 +124,8 @@
alarm(100ms);
ScopedDisableMalloc disable_malloc;
fork();
- }
- }, "");
}
+}, "");
+}
+
+} // namespace android
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
index 98e4aa1..84a0ec6 100644
--- a/libmemunreachable/tests/HeapWalker_test.cpp
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -19,10 +19,12 @@
#include "HeapWalker.h"
-#include <gtest/gtest.h>
#include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
#include "Allocator.h"
+namespace android {
+
class HeapWalkerTest : public ::testing::Test {
public:
HeapWalkerTest() : disable_malloc_(), heap_() {}
@@ -172,20 +174,20 @@
ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
EXPECT_EQ(2U, num_leaks);
- EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(2U, leaked.size());
}
TEST_F(HeapWalkerTest, segv) {
const size_t page_size = sysconf(_SC_PAGE_SIZE);
- void* buffer1 = mmap(NULL, page_size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+ void* buffer1 = mmap(NULL, page_size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
ASSERT_NE(buffer1, nullptr);
void* buffer2;
buffer2 = &buffer1;
HeapWalker heap_walker(heap_);
- heap_walker.Allocation(buffer_begin(buffer1), buffer_begin(buffer1)+page_size);
+ heap_walker.Allocation(buffer_begin(buffer1), buffer_begin(buffer1) + page_size);
heap_walker.Root(buffer_begin(buffer2), buffer_end(buffer2));
ASSERT_EQ(true, heap_walker.DetectLeaks());
@@ -199,3 +201,5 @@
EXPECT_EQ(0U, leaked_bytes);
ASSERT_EQ(0U, leaked.size());
}
+
+} // namespace android
diff --git a/libmemunreachable/tests/HostMallocStub.cpp b/libmemunreachable/tests/HostMallocStub.cpp
index a7e3f07..0ef0487 100644
--- a/libmemunreachable/tests/HostMallocStub.cpp
+++ b/libmemunreachable/tests/HostMallocStub.cpp
@@ -16,8 +16,6 @@
#include "bionic.h"
-void malloc_disable() {
-}
+void malloc_disable() {}
-void malloc_enable() {
-}
+void malloc_enable() {}
diff --git a/libmemunreachable/tests/LeakFolding_test.cpp b/libmemunreachable/tests/LeakFolding_test.cpp
index e85df5f..f5b3631 100644
--- a/libmemunreachable/tests/LeakFolding_test.cpp
+++ b/libmemunreachable/tests/LeakFolding_test.cpp
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-#include "HeapWalker.h"
#include "LeakFolding.h"
+#include "HeapWalker.h"
-#include <gtest/gtest.h>
#include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
#include "Allocator.h"
+namespace android {
+
class LeakFoldingTest : public ::testing::Test {
public:
LeakFoldingTest() : disable_malloc_(), heap_() {}
@@ -84,7 +86,7 @@
ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
EXPECT_EQ(2U, num_leaks);
- EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(2U, leaked.size());
EXPECT_EQ(0U, leaked[0].referenced_count);
EXPECT_EQ(0U, leaked[0].referenced_size);
@@ -113,7 +115,7 @@
ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
EXPECT_EQ(2U, num_leaks);
- EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(1U, leaked.size());
EXPECT_EQ(1U, leaked[0].referenced_count);
EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
@@ -144,10 +146,10 @@
ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
EXPECT_EQ(3U, num_leaks);
- EXPECT_EQ(3*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(3 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(1U, leaked.size());
EXPECT_EQ(2U, leaked[0].referenced_count);
- EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(2 * sizeof(uintptr_t), leaked[0].referenced_size);
}
TEST_F(LeakFoldingTest, dominator_cycle) {
@@ -175,13 +177,13 @@
ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
EXPECT_EQ(3U, num_leaks);
- EXPECT_EQ(5*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(5 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(2U, leaked.size());
EXPECT_EQ(2U, leaked[0].referenced_count);
- EXPECT_EQ(3*sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(3 * sizeof(uintptr_t), leaked[0].referenced_size);
EXPECT_EQ(2U, leaked[1].referenced_count);
- EXPECT_EQ(3*sizeof(uintptr_t), leaked[1].referenced_size);
+ EXPECT_EQ(3 * sizeof(uintptr_t), leaked[1].referenced_size);
}
TEST_F(LeakFoldingTest, two_cycles) {
@@ -218,12 +220,12 @@
ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
EXPECT_EQ(6U, num_leaks);
- EXPECT_EQ(6*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(2U, leaked.size());
EXPECT_EQ(2U, leaked[0].referenced_count);
- EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(2 * sizeof(uintptr_t), leaked[0].referenced_size);
EXPECT_EQ(2U, leaked[1].referenced_count);
- EXPECT_EQ(2*sizeof(uintptr_t), leaked[1].referenced_size);
+ EXPECT_EQ(2 * sizeof(uintptr_t), leaked[1].referenced_size);
}
TEST_F(LeakFoldingTest, two_dominator_cycles) {
@@ -254,7 +256,7 @@
ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
EXPECT_EQ(4U, num_leaks);
- EXPECT_EQ(4*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(4 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(4U, leaked.size());
EXPECT_EQ(1U, leaked[0].referenced_count);
EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
@@ -272,13 +274,13 @@
HeapWalker heap_walker(heap_);
- for (size_t i = 0; i < n; i ++) {
+ for (size_t i = 0; i < n; i++) {
ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
- reinterpret_cast<uintptr_t>(&buffer[i+1])));
+ reinterpret_cast<uintptr_t>(&buffer[i + 1])));
}
for (size_t i = 0; i < n - 1; i++) {
- buffer[i] = &buffer[i+1];
+ buffer[i] = &buffer[i + 1];
}
buffer[n - 1] = &buffer[0];
@@ -306,15 +308,15 @@
HeapWalker heap_walker(heap_);
for (size_t i = 0; i < n - 1; i++) {
- buffer[i] = &buffer[i+1];
+ buffer[i] = &buffer[i + 1];
}
buffer[n - 1] = &buffer[0];
buffer1[0] = &buffer[0];
- for (size_t i = 0; i < n; i ++) {
+ for (size_t i = 0; i < n; i++) {
ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
- reinterpret_cast<uintptr_t>(&buffer[i+1])));
+ reinterpret_cast<uintptr_t>(&buffer[i + 1])));
}
ALLOCATION(heap_walker, buffer1);
@@ -425,3 +427,5 @@
EXPECT_EQ(3U, leaked[3].referenced_count);
EXPECT_EQ(6 * sizeof(uintptr_t), leaked[3].referenced_size);
}
+
+} // namespace android
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index 2ae3db8..ec89388 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -16,38 +16,32 @@
#include <fcntl.h>
#include <stdlib.h>
-#include <unistd.h>
#include <sys/prctl.h>
+#include <unistd.h>
#include <gtest/gtest.h>
#include <memunreachable/memunreachable.h>
-void* ptr;
+namespace android {
class HiddenPointer {
public:
- explicit HiddenPointer(size_t size = 256) {
- Set(malloc(size));
- }
- ~HiddenPointer() {
- Free();
- }
- void* Get() {
- return reinterpret_cast<void*>(~ptr_);
- }
+ explicit HiddenPointer(size_t size = 256) { Set(malloc(size)); }
+ ~HiddenPointer() { Free(); }
+ void* Get() { return reinterpret_cast<void*>(~ptr_); }
void Free() {
free(Get());
Set(nullptr);
}
+
private:
- void Set(void* ptr) {
- ptr_ = ~reinterpret_cast<uintptr_t>(ptr);
- }
+ void Set(void* ptr) { ptr_ = ~reinterpret_cast<uintptr_t>(ptr); }
volatile uintptr_t ptr_;
};
-static void Ref(void* ptr) {
+// Trick the compiler into thinking a value on the stack is still referenced.
+static void Ref(void** ptr) {
write(0, ptr, 0);
}
@@ -65,14 +59,14 @@
{
void* ptr = hidden_ptr.Get();
- Ref(ptr);
+ Ref(&ptr);
UnreachableMemoryInfo info;
ASSERT_TRUE(GetUnreachableMemory(info));
ASSERT_EQ(0U, info.leaks.size());
- Ref(ptr);
+ ptr = nullptr;
}
{
@@ -92,10 +86,12 @@
}
}
+void* g_ptr;
+
TEST(MemunreachableTest, global) {
HiddenPointer hidden_ptr;
- ptr = hidden_ptr.Get();
+ g_ptr = hidden_ptr.Get();
{
UnreachableMemoryInfo info;
@@ -104,7 +100,7 @@
ASSERT_EQ(0U, info.leaks.size());
}
- ptr = NULL;
+ g_ptr = nullptr;
{
UnreachableMemoryInfo info;
@@ -126,7 +122,7 @@
TEST(MemunreachableTest, tls) {
HiddenPointer hidden_ptr;
pthread_key_t key;
- pthread_key_create(&key, NULL);
+ pthread_key_create(&key, nullptr);
pthread_setspecific(key, hidden_ptr.Get());
@@ -216,3 +212,5 @@
ASSERT_TRUE(LogUnreachableMemory(true, 100));
}
+
+} // namespace android
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
index 41ed84e..4fbf729 100644
--- a/libmemunreachable/tests/ThreadCapture_test.cpp
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -34,6 +34,8 @@
using namespace std::chrono_literals;
+namespace android {
+
class ThreadListTest : public ::testing::TestWithParam<int> {
public:
ThreadListTest() : stop_(false) {}
@@ -45,17 +47,15 @@
WaitForThreads();
}
- virtual void TearDown() {
- ASSERT_TRUE(heap.empty());
- }
+ virtual void TearDown() { ASSERT_TRUE(heap.empty()); }
protected:
- template<class Function>
+ template <class Function>
void StartThreads(unsigned int threads, Function&& func) {
threads_.reserve(threads);
tids_.reserve(threads);
for (unsigned int i = 0; i < threads; i++) {
- threads_.emplace_back([&, i, threads, this]() {
+ threads_.emplace_back([&, threads, this]() {
{
std::lock_guard<std::mutex> lk(m_);
tids_.push_back(gettid());
@@ -68,14 +68,14 @@
{
std::unique_lock<std::mutex> lk(m_);
- cv_stop_.wait(lk, [&] {return stop_;});
+ cv_stop_.wait(lk, [&] { return stop_; });
}
});
}
{
std::unique_lock<std::mutex> lk(m_);
- cv_start_.wait(lk, [&]{ return tids_.size() == threads; });
+ cv_start_.wait(lk, [&] { return tids_.size() == threads; });
}
}
@@ -93,9 +93,7 @@
tids_.clear();
}
- std::vector<pid_t>& tids() {
- return tids_;
- }
+ std::vector<pid_t>& tids() { return tids_; }
Heap heap;
@@ -143,7 +141,7 @@
TEST_P(ThreadListTest, list_some) {
const unsigned int threads = GetParam() - 1;
- StartThreads(threads, [](){});
+ StartThreads(threads, []() {});
std::vector<pid_t> expected_tids = tids();
expected_tids.push_back(getpid());
@@ -176,10 +174,8 @@
public:
ThreadCaptureTest() {}
~ThreadCaptureTest() {}
- void Fork(std::function<void()>&& child_init,
- std::function<void()>&& child_cleanup,
- std::function<void(pid_t)>&& parent) {
-
+ void Fork(std::function<void()>&& child_init, std::function<void()>&& child_cleanup,
+ std::function<void(pid_t)>&& parent) {
ScopedPipe start_pipe;
ScopedPipe stop_pipe;
@@ -211,39 +207,40 @@
TEST_P(ThreadCaptureTest, capture_some) {
const unsigned int threads = GetParam();
- Fork([&](){
- // child init
- StartThreads(threads - 1, [](){});
- },
- [&](){
- // child cleanup
- StopThreads();
- },
- [&](pid_t child){
- // parent
- ASSERT_GT(child, 0);
+ Fork(
+ [&]() {
+ // child init
+ StartThreads(threads - 1, []() {});
+ },
+ [&]() {
+ // child cleanup
+ StopThreads();
+ },
+ [&](pid_t child) {
+ // parent
+ ASSERT_GT(child, 0);
- {
- ScopedDisableMallocTimeout disable_malloc;
+ {
+ ScopedDisableMallocTimeout disable_malloc;
- ThreadCapture thread_capture(child, heap);
- auto list_tids = allocator::vector<pid_t>(heap);
+ ThreadCapture thread_capture(child, heap);
+ auto list_tids = allocator::vector<pid_t>(heap);
- ASSERT_TRUE(thread_capture.ListThreads(list_tids));
- ASSERT_EQ(threads, list_tids.size());
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+ ASSERT_EQ(threads, list_tids.size());
- ASSERT_TRUE(thread_capture.CaptureThreads());
+ ASSERT_TRUE(thread_capture.CaptureThreads());
- auto thread_info = allocator::vector<ThreadInfo>(heap);
- ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
- ASSERT_EQ(threads, thread_info.size());
- ASSERT_TRUE(thread_capture.ReleaseThreads());
+ auto thread_info = allocator::vector<ThreadInfo>(heap);
+ ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+ ASSERT_EQ(threads, thread_info.size());
+ ASSERT_TRUE(thread_capture.ReleaseThreads());
- if (!HasFailure()) {
- ASSERT_FALSE(disable_malloc.timed_out());
- }
-}
- });
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+ }
+ });
}
INSTANTIATE_TEST_CASE_P(ThreadCaptureTest, ThreadCaptureTest, ::testing::Values(1, 2, 10, 1024));
@@ -262,7 +259,7 @@
ScopedDisableMallocTimeout disable_malloc;
ThreadCapture thread_capture(ret, heap);
- thread_capture.InjectTestFunc([&](pid_t tid){
+ thread_capture.InjectTestFunc([&](pid_t tid) {
syscall(SYS_tgkill, ret, tid, SIGKILL);
usleep(10000);
});
@@ -288,62 +285,65 @@
// For signal handler
static ScopedPipe* g_pipe;
- Fork([&](){
- // child init
- pipe.CloseReceiver();
+ Fork(
+ [&]() {
+ // child init
+ pipe.CloseReceiver();
- g_pipe = &pipe;
+ g_pipe = &pipe;
- struct sigaction act{};
- act.sa_handler = [](int){
- char buf = '+';
- write(g_pipe->Sender(), &buf, 1);
- g_pipe->CloseSender();
- };
- sigaction(sig, &act, NULL);
- sigset_t set;
- sigemptyset(&set);
- sigaddset(&set, sig);
- pthread_sigmask(SIG_UNBLOCK, &set, NULL);
- },
- [&](){
- // child cleanup
- g_pipe = nullptr;
- pipe.Close();
- },
- [&](pid_t child){
- // parent
- ASSERT_GT(child, 0);
- pipe.CloseSender();
+ struct sigaction act {};
+ act.sa_handler = [](int) {
+ char buf = '+';
+ write(g_pipe->Sender(), &buf, 1);
+ g_pipe->CloseSender();
+ };
+ sigaction(sig, &act, NULL);
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, sig);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+ },
+ [&]() {
+ // child cleanup
+ g_pipe = nullptr;
+ pipe.Close();
+ },
+ [&](pid_t child) {
+ // parent
+ ASSERT_GT(child, 0);
+ pipe.CloseSender();
- {
- ScopedDisableMallocTimeout disable_malloc;
+ {
+ ScopedDisableMallocTimeout disable_malloc;
- ThreadCapture thread_capture(child, heap);
- thread_capture.InjectTestFunc([&](pid_t tid){
- syscall(SYS_tgkill, child, tid, sig);
- usleep(10000);
+ ThreadCapture thread_capture(child, heap);
+ thread_capture.InjectTestFunc([&](pid_t tid) {
+ syscall(SYS_tgkill, child, tid, sig);
+ usleep(10000);
+ });
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+ ASSERT_EQ(1U, list_tids.size());
+
+ ASSERT_TRUE(thread_capture.CaptureThreads());
+
+ auto thread_info = allocator::vector<ThreadInfo>(heap);
+ ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+ ASSERT_EQ(1U, thread_info.size());
+ ASSERT_TRUE(thread_capture.ReleaseThreads());
+
+ usleep(100000);
+ char buf;
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1)));
+ ASSERT_EQ(buf, '+');
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+ }
});
- auto list_tids = allocator::vector<pid_t>(heap);
-
- ASSERT_TRUE(thread_capture.ListThreads(list_tids));
- ASSERT_EQ(1U, list_tids.size());
-
- ASSERT_TRUE(thread_capture.CaptureThreads());
-
- auto thread_info = allocator::vector<ThreadInfo>(heap);
- ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
- ASSERT_EQ(1U, thread_info.size());
- ASSERT_TRUE(thread_capture.ReleaseThreads());
-
- usleep(100000);
- char buf;
- ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1)));
- ASSERT_EQ(buf, '+');
-
- if (!HasFailure()) {
- ASSERT_FALSE(disable_malloc.timed_out());
- }
- }
- });
}
+
+} // namespace android
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 79d6c3f..c692d1f 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -7,7 +7,6 @@
cc_defaults {
name: "metricslogger_defaults",
- clang: true,
host_supported: true,
export_include_dirs: ["include"],
@@ -23,6 +22,7 @@
// 524291 corresponds to sysui_histogram, from
// frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
"-DHISTOGRAM_LOG_TAG=524292",
+ "-DCOUNT_LOG_TAG=524290",
],
}
@@ -30,7 +30,6 @@
// -----------------------------------------------------------------------------
cc_library_shared {
name: "libmetricslogger",
- vendor_available: true,
srcs: metricslogger_lib_src_files,
defaults: ["metricslogger_defaults"],
}
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 26aa189..36e124d 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -24,13 +24,18 @@
// buffer.
void LogHistogram(const std::string& event, int32_t data);
+// Logs a Tron counter metric named |name| containing |val| count to the Tron
+// log buffer.
+void LogCounter(const std::string& name, int32_t val);
+
// TODO: replace these with the metric_logger.proto definitions
enum {
- LOGBUILDER_CATEGORY = 757,
- LOGBUILDER_NAME = 799,
- LOGBUILDER_BUCKET = 801,
- LOGBUILDER_VALUE = 802,
- LOGBUILDER_HISTOGRAM = 804,
+ LOGBUILDER_CATEGORY = 757,
+ LOGBUILDER_NAME = 799,
+ LOGBUILDER_BUCKET = 801,
+ LOGBUILDER_VALUE = 802,
+ LOGBUILDER_COUNTER = 803,
+ LOGBUILDER_HISTOGRAM = 804,
};
} // namespace metricslogger
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index 0d08f5c..6f65e10 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -25,12 +25,16 @@
// Mirror com.android.internal.logging.MetricsLogger#histogram().
void LogHistogram(const std::string& event, int32_t data) {
- android_log_event_list log(HISTOGRAM_LOG_TAG);
- log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM
- << LOGBUILDER_NAME << event
- << LOGBUILDER_BUCKET << data
- << LOGBUILDER_VALUE << 1
- << LOG_ID_EVENTS;
+ android_log_event_list log(HISTOGRAM_LOG_TAG);
+ log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event
+ << LOGBUILDER_BUCKET << data << LOGBUILDER_VALUE << 1 << LOG_ID_EVENTS;
+}
+
+// Mirror com.android.internal.logging.MetricsLogger#count().
+void LogCounter(const std::string& name, int32_t val) {
+ android_log_event_list log(COUNT_LOG_TAG);
+ log << LOGBUILDER_CATEGORY << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE
+ << val << LOG_ID_EVENTS;
}
} // namespace metricslogger
diff --git a/libmetricslogger/metrics_logger_test.cpp b/libmetricslogger/metrics_logger_test.cpp
index 5a30ad7..440645c 100644
--- a/libmetricslogger/metrics_logger_test.cpp
+++ b/libmetricslogger/metrics_logger_test.cpp
@@ -19,6 +19,10 @@
#include <gtest/gtest.h>
TEST(MetricsLoggerTest, AddSingleBootEvent) {
- android::metricslogger::LogHistogram("test_event", 42);
- // TODO(jhawkins): Verify the EventLog is updated.
+ android::metricslogger::LogHistogram("test_event", 42);
+ // TODO(jhawkins): Verify the EventLog is updated.
+}
+
+TEST(MetricsLoggerTest, AddCounterVal) {
+ android::metricslogger::LogCounter("test_count", 10);
}
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 5fb56f2..b3c42f0 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -1,11 +1,19 @@
+cc_library_headers {
+ name: "libnativebridge-dummy-headers",
+
+ host_supported: true,
+ export_include_dirs=["include"],
+}
+
cc_library {
name: "libnativebridge",
host_supported: true,
srcs: ["native_bridge.cc"],
- shared_libs: ["liblog"],
- clang: true,
+ shared_libs: ["liblog", "libbase"],
+
+ export_include_dirs=["include"],
cflags: [
"-Werror",
@@ -22,3 +30,5 @@
},
},
}
+
+subdirs = ["tests"]
diff --git a/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
similarity index 100%
rename from include/nativebridge/native_bridge.h
rename to libnativebridge/include/nativebridge/native_bridge.h
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index cc30aaf..e24307a 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -28,6 +28,7 @@
#include <cstring>
+#include <android-base/macros.h>
#include <log/log.h>
namespace android {
@@ -243,29 +244,12 @@
}
}
-#if defined(__arm__)
-static const char* kRuntimeISA = "arm";
-#elif defined(__aarch64__)
-static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__) && !defined(__LP64__)
-static const char* kRuntimeISA = "mips";
-#elif defined(__mips__) && defined(__LP64__)
-static const char* kRuntimeISA = "mips64";
-#elif defined(__i386__)
-static const char* kRuntimeISA = "x86";
-#elif defined(__x86_64__)
-static const char* kRuntimeISA = "x86_64";
-#else
-static const char* kRuntimeISA = "unknown";
-#endif
-
-
bool NeedsNativeBridge(const char* instruction_set) {
if (instruction_set == nullptr) {
ALOGE("Null instruction set in NeedsNativeBridge.");
return false;
}
- return strncmp(instruction_set, kRuntimeISA, strlen(kRuntimeISA) + 1) != 0;
+ return strncmp(instruction_set, ABI_STRING, strlen(ABI_STRING) + 1) != 0;
}
#ifdef __APPLE__
@@ -577,15 +561,15 @@
bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
const char* anon_ns_library_path) {
- if (NativeBridgeInitialized()) {
- if (isCompatibleWith(NAMESPACE_VERSION)) {
- return callbacks->initAnonymousNamespace(public_ns_sonames, anon_ns_library_path);
- } else {
- ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
- }
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
+ return callbacks->initAnonymousNamespace(public_ns_sonames, anon_ns_library_path);
+ } else {
+ ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
}
+ }
- return false;
+ return false;
}
native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
@@ -612,15 +596,15 @@
bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
const char* shared_libs_sonames) {
- if (NativeBridgeInitialized()) {
- if (isCompatibleWith(NAMESPACE_VERSION)) {
- return callbacks->linkNamespaces(from, to, shared_libs_sonames);
- } else {
- ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
- }
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
+ return callbacks->linkNamespaces(from, to, shared_libs_sonames);
+ } else {
+ ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
}
+ }
- return false;
+ return false;
}
native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
new file mode 100644
index 0000000..e31dae0
--- /dev/null
+++ b/libnativebridge/tests/Android.bp
@@ -0,0 +1,54 @@
+//
+// 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.
+//
+
+cc_defaults {
+ name: "libnativebridge-dummy-defaults",
+
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ header_libs: ["libnativebridge-dummy-headers"],
+ cppflags: ["-fvisibility=protected"],
+ target: {
+ android: {
+ shared_libs: ["libdl"],
+ },
+ host: {
+ host_ldlibs: ["-ldl"],
+ },
+ },
+}
+
+cc_library_shared {
+ name: "libnativebridge-dummy",
+ srcs: ["DummyNativeBridge.cpp"],
+ defaults: ["libnativebridge-dummy-defaults"],
+}
+
+cc_library_shared {
+ name: "libnativebridge2-dummy",
+ srcs: ["DummyNativeBridge2.cpp"],
+ defaults: ["libnativebridge-dummy-defaults"],
+}
+
+cc_library_shared {
+ name: "libnativebridge3-dummy",
+ srcs: ["DummyNativeBridge3.cpp"],
+ defaults: ["libnativebridge-dummy-defaults"],
+}
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index c9468f0..b3861e0 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -1,8 +1,6 @@
# Build the unit tests.
LOCAL_PATH := $(call my-dir)
-include $(LOCAL_PATH)/Android.nativebridge-dummy.mk
-
include $(CLEAR_VARS)
# Build the unit tests.
@@ -31,12 +29,12 @@
shared_libraries := \
liblog \
+ libbase \
libnativebridge \
libnativebridge-dummy
$(foreach file,$(test_src_files), \
$(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_CLANG := true) \
$(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
$(eval LOCAL_SRC_FILES := $(file)) \
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
@@ -45,7 +43,6 @@
$(foreach file,$(test_src_files), \
$(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_CLANG := true) \
$(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
$(eval LOCAL_SRC_FILES := $(file)) \
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
diff --git a/libnativebridge/tests/Android.nativebridge-dummy.mk b/libnativebridge/tests/Android.nativebridge-dummy.mk
deleted file mode 100644
index 2d78be0..0000000
--- a/libnativebridge/tests/Android.nativebridge-dummy.mk
+++ /dev/null
@@ -1,108 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-NATIVE_BRIDGE_COMMON_SRC_FILES := \
- DummyNativeBridge.cpp
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_SHARED_LIBRARIES := libdl
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
-# v2.
-
-NATIVE_BRIDGE2_COMMON_SRC_FILES := \
- DummyNativeBridge2.cpp
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge2-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_SHARED_LIBRARIES := libdl
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge2-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
-# v3.
-
-NATIVE_BRIDGE3_COMMON_SRC_FILES := \
- DummyNativeBridge3.cpp
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge3-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge3-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
diff --git a/libnativebridge/tests/DummyNativeBridge3.cpp b/libnativebridge/tests/DummyNativeBridge3.cpp
index b0dd6d0..4ef1c82 100644
--- a/libnativebridge/tests/DummyNativeBridge3.cpp
+++ b/libnativebridge/tests/DummyNativeBridge3.cpp
@@ -78,7 +78,7 @@
extern "C" bool native_bridge3_initAnonymousNamespace(const char* /* public_ns_sonames */,
const char* /* anon_ns_library_path */) {
- return true;
+ return true;
}
extern "C" android::native_bridge_namespace_t*
@@ -94,7 +94,7 @@
extern "C" bool native_bridge3_linkNamespaces(android::native_bridge_namespace_t* /* from */,
android::native_bridge_namespace_t* /* to */,
const char* /* shared_libs_soname */) {
- return true;
+ return true;
}
extern "C" void* native_bridge3_loadLibraryExt(const char* /* libpath */,
diff --git a/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
index 989a819..b0d6b09 100644
--- a/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
+++ b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
@@ -21,19 +21,19 @@
constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
TEST_F(NativeBridgeTest, V3_InitAnonymousNamespace) {
- // Init
- ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
- ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
- ASSERT_TRUE(NativeBridgeAvailable());
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
- ASSERT_EQ(3U, NativeBridgeGetVersion());
- ASSERT_EQ(true, NativeBridgeInitAnonymousNamespace(nullptr, nullptr));
+ ASSERT_EQ(3U, NativeBridgeGetVersion());
+ ASSERT_EQ(true, NativeBridgeInitAnonymousNamespace(nullptr, nullptr));
- // Clean-up code_cache
- ASSERT_EQ(0, rmdir(kCodeCache));
+ // Clean-up code_cache
+ ASSERT_EQ(0, rmdir(kCodeCache));
}
} // namespace android
diff --git a/libnativebridge/tests/NeedsNativeBridge_test.cpp b/libnativebridge/tests/NeedsNativeBridge_test.cpp
index 2067ed2..c8ff743 100644
--- a/libnativebridge/tests/NeedsNativeBridge_test.cpp
+++ b/libnativebridge/tests/NeedsNativeBridge_test.cpp
@@ -16,34 +16,20 @@
#include "NativeBridgeTest.h"
+#include <android-base/macros.h>
+
namespace android {
static const char* kISAs[] = { "arm", "arm64", "mips", "mips64", "x86", "x86_64", "random", "64arm",
"64_x86", "64_x86_64", "", "reallylongstringabcd", nullptr };
-#if defined(__arm__)
-static const char* kRuntimeISA = "arm";
-#elif defined(__aarch64__)
-static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__) && !defined(__LP64__)
-static const char* kRuntimeISA = "mips";
-#elif defined(__mips__) && defined(__LP64__)
-static const char* kRuntimeISA = "mips64";
-#elif defined(__i386__)
-static const char* kRuntimeISA = "x86";
-#elif defined(__x86_64__)
-static const char* kRuntimeISA = "x86_64";
-#else
-static const char* kRuntimeISA = "unknown";
-#endif
-
TEST_F(NativeBridgeTest, NeedsNativeBridge) {
- EXPECT_EQ(false, NeedsNativeBridge(kRuntimeISA));
+ EXPECT_EQ(false, NeedsNativeBridge(ABI_STRING));
- const size_t kISACount = sizeof(kISAs)/sizeof(kISAs[0]);
- for (size_t i = 0; i < kISACount; i++) {
- EXPECT_EQ(kISAs[i] == nullptr ? false : strcmp(kISAs[i], kRuntimeISA) != 0,
- NeedsNativeBridge(kISAs[i]));
+ const size_t kISACount = sizeof(kISAs) / sizeof(kISAs[0]);
+ for (size_t i = 0; i < kISACount; i++) {
+ EXPECT_EQ(kISAs[i] == nullptr ? false : strcmp(kISAs[i], ABI_STRING) != 0,
+ NeedsNativeBridge(kISAs[i]));
}
}
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index c1133fb..13f9744 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -19,7 +19,6 @@
host_ldlibs: ["-ldl"],
},
},
- clang: true,
cflags: [
"-Werror",
"-Wall",
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 36a2e44..7ccd7db 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -15,7 +15,7 @@
*/
#include "nativeloader/native_loader.h"
-#include "ScopedUtfChars.h"
+#include <nativehelper/ScopedUtfChars.h>
#include <dlfcn.h>
#ifdef __ANDROID__
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index d63d619..1d43775 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -1,6 +1,9 @@
cc_library_shared {
name: "libnetutils",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"dhcpclient.c",
@@ -18,3 +21,17 @@
export_include_dirs: ["include"],
}
+
+cc_binary {
+ name: "dhcpdbg",
+
+ srcs: [
+ "dhcptool.c",
+ ],
+
+ shared_libs: [
+ "libnetutils",
+ ],
+
+ cflags: ["-Werror"],
+}
diff --git a/libnetutils/dhcptool.c b/libnetutils/dhcptool.c
new file mode 100644
index 0000000..280b977
--- /dev/null
+++ b/libnetutils/dhcptool.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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 <err.h>
+#include <errno.h>
+#include <error.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <netutils/ifc.h>
+
+extern int do_dhcp(char*);
+
+int main(int argc, char* argv[]) {
+ if (argc != 2) {
+ error(EXIT_FAILURE, 0, "usage: %s INTERFACE", argv[0]);
+ }
+
+ char* interface = argv[1];
+ if (ifc_init()) {
+ err(errno, "dhcptool %s: ifc_init failed", interface);
+ ifc_close();
+ return EXIT_FAILURE;
+ }
+
+ int rc = do_dhcp(interface);
+ if (rc) {
+ err(errno, "dhcptool %s: do_dhcp failed", interface);
+ }
+ warn("IP assignment is for debug purposes ONLY");
+ ifc_close();
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index 70ff528..a9fec7d 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -6,7 +6,6 @@
local_include_dirs: ["include"],
export_include_dirs: ["include"],
- clang: true,
sanitize: {
misc_undefined: ["integer"],
},
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c
index f28d726..1856e5c 100644
--- a/libpixelflinger/codeflinger/mips64_disassem.c
+++ b/libpixelflinger/codeflinger/mips64_disassem.c
@@ -555,6 +555,7 @@
} else {
vprintf(fmt, argp);
}
+ va_end(argp);
}
/*
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c
index 3007b15..83a9740 100644
--- a/libpixelflinger/codeflinger/mips_disassem.c
+++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -562,6 +562,7 @@
} else {
vprintf(fmt, argp);
}
+ va_end(argp);
}
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
new file mode 100644
index 0000000..1974f2c
--- /dev/null
+++ b/libprocessgroup/Android.bp
@@ -0,0 +1,11 @@
+cc_library {
+ srcs: ["processgroup.cpp"],
+ name: "libprocessgroup",
+ defaults: ["linux_bionic_supported"],
+ shared_libs: ["libbase"],
+ export_include_dirs: ["include"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libprocessgroup/Android.mk b/libprocessgroup/Android.mk
deleted file mode 100644
index 0bfc391..0000000
--- a/libprocessgroup/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := processgroup.cpp
-LOCAL_MODULE := libprocessgroup
-LOCAL_STATIC_LIBRARIES := libbase
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := processgroup.cpp
-LOCAL_MODULE := libprocessgroup
-LOCAL_SHARED_LIBRARIES := libbase
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 47f6ff3..9fa4154 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -22,12 +22,21 @@
__BEGIN_DECLS
+// Return 0 and removes the cgroup if there are no longer any processes in it.
+// Returns -1 in the case of an error occurring or if there are processes still running
+// even after retrying for up to 200ms.
int killProcessGroup(uid_t uid, int initialPid, int signal);
+// Returns the same as killProcessGroup(), however it does not retry, which means
+// that it only returns 0 in the case that the cgroup exists and it contains no processes.
int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
int createProcessGroup(uid_t uid, int initialPid);
+bool setProcessGroupSwappiness(uid_t uid, int initialPid, int swappiness);
+bool setProcessGroupSoftLimit(uid_t uid, int initialPid, int64_t softLimitInBytes);
+bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
+
void removeAllProcessGroups(void);
__END_DECLS
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 1572cb3..8526b3a 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -27,21 +27,24 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <unistd.h>
#include <chrono>
#include <memory>
#include <mutex>
+#include <set>
#include <thread>
+#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
#include <private/android_filesystem_config.h>
#include <processgroup/processgroup.h>
-using namespace std::chrono_literals;
+using android::base::WriteStringToFile;
-// Uncomment line below use memory cgroups for keeping track of (forked) PIDs
-// #define USE_MEMCG 1
+using namespace std::chrono_literals;
#define MEM_CGROUP_PATH "/dev/memcg/apps"
#define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
@@ -64,16 +67,30 @@
std::once_flag init_path_flag;
-struct ctx {
- bool initialized;
- int fd;
- char buf[128];
- char *buf_ptr;
- size_t buf_len;
+class ProcessGroup {
+ public:
+ ProcessGroup() : buf_ptr_(buf_), buf_len_(0) {}
+
+ bool Open(uid_t uid, int pid);
+
+ // Return positive number and sets *pid = next pid in process cgroup on success
+ // Returns 0 if there are no pids left in the process cgroup
+ // Returns -errno if an error was encountered
+ int GetOneAppProcess(pid_t* pid);
+
+ private:
+ // Returns positive number of bytes filled on success
+ // Returns 0 if there was nothing to read
+ // Returns -errno if an error was encountered
+ int RefillBuffer();
+
+ android::base::unique_fd fd_;
+ char buf_[128];
+ char* buf_ptr_;
+ size_t buf_len_;
};
static const char* getCgroupRootPath() {
-#ifdef USE_MEMCG
static const char* cgroup_root_path = NULL;
std::call_once(init_path_flag, [&]() {
// Check if mem cgroup is mounted, only then check for write-access to avoid
@@ -82,9 +99,6 @@
ACCT_CGROUP_PATH : MEM_CGROUP_PATH;
});
return cgroup_root_path;
-#else
- return ACCT_CGROUP_PATH;
-#endif
}
static int convertUidToPath(char *path, size_t size, uid_t uid)
@@ -105,87 +119,67 @@
pid);
}
-static int initCtx(uid_t uid, int pid, struct ctx *ctx)
-{
- int ret;
+bool ProcessGroup::Open(uid_t uid, int pid) {
char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
convertUidPidToPath(path, sizeof(path), uid, pid);
strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
int fd = open(path, O_RDONLY);
- if (fd < 0) {
- ret = -errno;
- PLOG(WARNING) << "failed to open " << path;
- return ret;
- }
+ if (fd < 0) return false;
- ctx->fd = fd;
- ctx->buf_ptr = ctx->buf;
- ctx->buf_len = 0;
- ctx->initialized = true;
+ fd_.reset(fd);
LOG(VERBOSE) << "Initialized context for " << path;
- return 0;
+ return true;
}
-static int refillBuffer(struct ctx *ctx)
-{
- memmove(ctx->buf, ctx->buf_ptr, ctx->buf_len);
- ctx->buf_ptr = ctx->buf;
+int ProcessGroup::RefillBuffer() {
+ memmove(buf_, buf_ptr_, buf_len_);
+ buf_ptr_ = buf_;
- ssize_t ret = read(ctx->fd, ctx->buf_ptr + ctx->buf_len,
- sizeof(ctx->buf) - ctx->buf_len - 1);
+ ssize_t ret = read(fd_, buf_ptr_ + buf_len_, sizeof(buf_) - buf_len_ - 1);
if (ret < 0) {
return -errno;
} else if (ret == 0) {
return 0;
}
- ctx->buf_len += ret;
- ctx->buf[ctx->buf_len] = 0;
- LOG(VERBOSE) << "Read " << ret << " to buffer: " << ctx->buf;
+ buf_len_ += ret;
+ buf_[buf_len_] = 0;
+ LOG(VERBOSE) << "Read " << ret << " to buffer: " << buf_;
- assert(ctx->buf_len <= sizeof(ctx->buf));
+ assert(buf_len_ <= sizeof(buf_));
return ret;
}
-static pid_t getOneAppProcess(uid_t uid, int appProcessPid, struct ctx *ctx)
-{
- if (!ctx->initialized) {
- int ret = initCtx(uid, appProcessPid, ctx);
- if (ret < 0) {
- return ret;
- }
- }
+int ProcessGroup::GetOneAppProcess(pid_t* out_pid) {
+ *out_pid = 0;
- char *eptr;
- while ((eptr = (char *)memchr(ctx->buf_ptr, '\n', ctx->buf_len)) == NULL) {
- int ret = refillBuffer(ctx);
- if (ret == 0) {
- return -ERANGE;
- }
- if (ret < 0) {
- return ret;
- }
+ char* eptr;
+ while ((eptr = static_cast<char*>(memchr(buf_ptr_, '\n', buf_len_))) == nullptr) {
+ int ret = RefillBuffer();
+ if (ret <= 0) return ret;
}
*eptr = '\0';
- char *pid_eptr = NULL;
+ char* pid_eptr = nullptr;
errno = 0;
- long pid = strtol(ctx->buf_ptr, &pid_eptr, 10);
+ long pid = strtol(buf_ptr_, &pid_eptr, 10);
if (errno != 0) {
return -errno;
}
if (pid_eptr != eptr) {
- return -EINVAL;
+ errno = EINVAL;
+ return -errno;
}
- ctx->buf_len -= (eptr - ctx->buf_ptr) + 1;
- ctx->buf_ptr = eptr + 1;
+ buf_len_ -= (eptr - buf_ptr_) + 1;
+ buf_ptr_ = eptr + 1;
- return (pid_t)pid;
+ *out_pid = static_cast<pid_t>(pid);
+ return 1;
}
static int removeProcessGroup(uid_t uid, int pid)
@@ -219,8 +213,8 @@
}
snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
- LOG(VERBOSE) << "removing " << path;
- if (rmdir(path) == -1) PLOG(WARNING) << "failed to remove " << path;
+ LOG(VERBOSE) << "Removing " << path;
+ if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
}
}
}
@@ -231,7 +225,7 @@
const char* cgroup_root_path = getCgroupRootPath();
std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
if (root == NULL) {
- PLOG(ERROR) << "failed to open " << cgroup_root_path;
+ PLOG(ERROR) << "Failed to open " << cgroup_root_path;
} else {
dirent* dir;
while ((dir = readdir(root.get())) != nullptr) {
@@ -246,20 +240,32 @@
snprintf(path, sizeof(path), "%s/%s", cgroup_root_path, dir->d_name);
removeUidProcessGroups(path);
- LOG(VERBOSE) << "removing " << path;
- if (rmdir(path) == -1) PLOG(WARNING) << "failed to remove " << path;
+ LOG(VERBOSE) << "Removing " << path;
+ if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
}
}
}
+// Returns number of processes killed on success
+// Returns 0 if there are no processes in the process cgroup left to kill
+// Returns -errno on error
static int doKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
- int processes = 0;
- struct ctx ctx;
+ ProcessGroup process_group;
+ if (!process_group.Open(uid, initialPid)) {
+ PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
+ return -errno;
+ }
+
+ // We separate all of the pids in the cgroup into those pids that are also the leaders of
+ // process groups (stored in the pgids set) and those that are not (stored in the pids set).
+ std::set<pid_t> pgids;
+ pgids.emplace(initialPid);
+ std::set<pid_t> pids;
+
+ int ret;
pid_t pid;
-
- ctx.initialized = false;
-
- while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) {
+ int processes = 0;
+ while ((ret = process_group.GetOneAppProcess(&pid)) > 0 && pid >= 0) {
processes++;
if (pid == 0) {
// Should never happen... but if it does, trying to kill this
@@ -267,55 +273,100 @@
LOG(WARNING) << "Yikes, we've been told to kill pid 0! How about we don't do that?";
continue;
}
- LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid
- << " as part of process group " << initialPid;
+ pid_t pgid = getpgid(pid);
+ if (pgid == -1) PLOG(ERROR) << "getpgid(" << pid << ") failed";
+ if (pgid == pid) {
+ pgids.emplace(pid);
+ } else {
+ pids.emplace(pid);
+ }
+ }
+
+ // Erase all pids that will be killed when we kill the process groups.
+ for (auto it = pids.begin(); it != pids.end();) {
+ pid_t pgid = getpgid(pid);
+ if (pgids.count(pgid) == 1) {
+ it = pids.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ // Kill all process groups.
+ for (const auto pgid : pgids) {
+ LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
+ << " as part of process cgroup " << initialPid;
+
+ if (kill(-pgid, signal) == -1) {
+ PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
+ }
+ }
+
+ // Kill remaining pids.
+ for (const auto pid : pids) {
+ LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
+ << initialPid;
+
if (kill(pid, signal) == -1) {
PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
}
}
- if (ctx.initialized) {
- close(ctx.fd);
- }
-
- return processes;
+ return ret >= 0 ? processes : ret;
}
-static int killProcessGroup(uid_t uid, int initialPid, int signal, int retry) {
+static int killProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
+ int retry = retries;
int processes;
while ((processes = doKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
- LOG(VERBOSE) << "killed " << processes << " processes for processgroup " << initialPid;
+ LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
if (retry > 0) {
std::this_thread::sleep_for(5ms);
--retry;
} else {
- LOG(ERROR) << "failed to kill " << processes << " processes for processgroup "
- << initialPid;
break;
}
}
- std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
+ if (processes < 0) {
+ PLOG(ERROR) << "Error encountered killing process cgroup uid " << uid << " pid "
+ << initialPid;
+ return -1;
+ }
+ std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
- LOG(VERBOSE) << "Killed process group uid " << uid << " pid " << initialPid << " in "
- << static_cast<int>(ms) << "ms, " << processes << " procs remain";
+
+ // We only calculate the number of 'processes' when killing the processes.
+ // In the retries == 0 case, we only kill the processes once and therefore
+ // will not have waited then recalculated how many processes are remaining
+ // after the first signals have been sent.
+ // Logging anything regarding the number of 'processes' here does not make sense.
if (processes == 0) {
+ if (retries > 0) {
+ LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
+ << " in " << static_cast<int>(ms) << "ms";
+ }
return removeProcessGroup(uid, initialPid);
} else {
+ if (retries > 0) {
+ LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
+ << " in " << static_cast<int>(ms) << "ms, " << processes
+ << " processes remain";
+ }
return -1;
}
}
int killProcessGroup(uid_t uid, int initialPid, int signal) {
- return killProcessGroup(uid, initialPid, signal, 40 /*maxRetry*/);
+ return killProcessGroup(uid, initialPid, signal, 40 /*retries*/);
}
int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
- return killProcessGroup(uid, initialPid, signal, 0 /*maxRetry*/);
+ return killProcessGroup(uid, initialPid, signal, 0 /*retries*/);
}
static bool mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
@@ -341,35 +392,53 @@
convertUidToPath(path, sizeof(path), uid);
if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
- PLOG(ERROR) << "failed to make and chown " << path;
+ PLOG(ERROR) << "Failed to make and chown " << path;
return -errno;
}
convertUidPidToPath(path, sizeof(path), uid, initialPid);
if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
- PLOG(ERROR) << "failed to make and chown " << path;
+ PLOG(ERROR) << "Failed to make and chown " << path;
return -errno;
}
strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
- int fd = open(path, O_WRONLY);
- if (fd == -1) {
- int ret = -errno;
- PLOG(ERROR) << "failed to open " << path;
- return ret;
- }
-
- char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0};
- int len = snprintf(pid, sizeof(pid), "%d", initialPid);
-
int ret = 0;
- if (write(fd, pid, len) < 0) {
+ if (!WriteStringToFile(std::to_string(initialPid), path)) {
ret = -errno;
- PLOG(ERROR) << "failed to write '" << pid << "' to " << path;
+ PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << path;
}
- close(fd);
return ret;
}
+
+static bool setProcessGroupValue(uid_t uid, int pid, const char* fileName, int64_t value) {
+ char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
+ if (strcmp(getCgroupRootPath(), MEM_CGROUP_PATH)) {
+ PLOG(ERROR) << "Memcg is not mounted." << path;
+ return false;
+ }
+
+ convertUidPidToPath(path, sizeof(path), uid, pid);
+ strlcat(path, fileName, sizeof(path));
+
+ if (!WriteStringToFile(std::to_string(value), path)) {
+ PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
+ return false;
+ }
+ return true;
+}
+
+bool setProcessGroupSwappiness(uid_t uid, int pid, int swappiness) {
+ return setProcessGroupValue(uid, pid, "/memory.swappiness", swappiness);
+}
+
+bool setProcessGroupSoftLimit(uid_t uid, int pid, int64_t soft_limit_in_bytes) {
+ return setProcessGroupValue(uid, pid, "/memory.soft_limit_in_bytes", soft_limit_in_bytes);
+}
+
+bool setProcessGroupLimit(uid_t uid, int pid, int64_t limit_in_bytes) {
+ return setProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes);
+}
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index aedaa38..b568ee5 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -23,6 +23,9 @@
cc_library {
name: "libprocinfo",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
host_supported: true,
srcs: [
"process.cpp",
diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h
index fb140ff..db56fc1 100644
--- a/libprocinfo/include/procinfo/process.h
+++ b/libprocinfo/include/procinfo/process.h
@@ -35,8 +35,18 @@
#if defined(__linux__)
+enum ProcessState {
+ kProcessStateUnknown,
+ kProcessStateRunning,
+ kProcessStateSleeping,
+ kProcessStateUninterruptibleWait,
+ kProcessStateStopped,
+ kProcessStateZombie,
+};
+
struct ProcessInfo {
std::string name;
+ ProcessState state;
pid_t tid;
pid_t pid;
pid_t ppid;
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
index c513e16..6e5be6e 100644
--- a/libprocinfo/process.cpp
+++ b/libprocinfo/process.cpp
@@ -44,6 +44,24 @@
return GetProcessInfoFromProcPidFd(dirfd.get(), process_info);
}
+static ProcessState parse_state(const char* state) {
+ switch (*state) {
+ case 'R':
+ return kProcessStateRunning;
+ case 'S':
+ return kProcessStateSleeping;
+ case 'D':
+ return kProcessStateUninterruptibleWait;
+ case 'T':
+ return kProcessStateStopped;
+ case 'Z':
+ return kProcessStateZombie;
+ default:
+ LOG(ERROR) << "unknown process state: " << *state;
+ return kProcessStateUnknown;
+ }
+}
+
bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) {
int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
@@ -60,7 +78,7 @@
}
int field_bitmap = 0;
- static constexpr int finished_bitmap = 127;
+ static constexpr int finished_bitmap = 255;
char* line = nullptr;
size_t len = 0;
@@ -98,6 +116,9 @@
} else if (header == "Gid:") {
process_info->gid = atoi(tab + 1);
field_bitmap |= 64;
+ } else if (header == "State:") {
+ process_info->state = parse_state(tab + 1);
+ field_bitmap |= 128;
}
}
diff --git a/libprocinfo/process_test.cpp b/libprocinfo/process_test.cpp
index 5ffd236..9da9278 100644
--- a/libprocinfo/process_test.cpp
+++ b/libprocinfo/process_test.cpp
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <chrono>
#include <set>
#include <thread>
#include <vector>
@@ -29,6 +30,8 @@
#include <android-base/stringprintf.h>
+using namespace std::chrono_literals;
+
#if !defined(__BIONIC__)
#include <syscall.h>
static pid_t gettid() {
@@ -82,3 +85,34 @@
}
}).join();
}
+
+TEST(process_info, process_state) {
+ int pipefd[2];
+ ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
+ pid_t forkpid = fork();
+
+ ASSERT_NE(-1, forkpid);
+ if (forkpid == 0) {
+ close(pipefd[1]);
+ char buf;
+ TEMP_FAILURE_RETRY(read(pipefd[0], &buf, 1));
+ _exit(0);
+ }
+
+ // Give the child some time to get to the read.
+ std::this_thread::sleep_for(100ms);
+
+ android::procinfo::ProcessInfo procinfo;
+ ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
+ ASSERT_EQ(android::procinfo::kProcessStateSleeping, procinfo.state);
+
+ ASSERT_EQ(0, kill(forkpid, SIGKILL));
+
+ // Give the kernel some time to kill the child.
+ std::this_thread::sleep_for(100ms);
+
+ ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
+ ASSERT_EQ(android::procinfo::kProcessStateZombie, procinfo.state);
+
+ ASSERT_EQ(forkpid, waitpid(forkpid, nullptr, 0));
+}
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index dd8b5fd..6ec0991 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -10,17 +10,23 @@
"sparse.c",
"sparse_crc32.c",
"sparse_err.c",
- "sparse_read.c",
+ "sparse_read.cpp",
],
cflags: ["-Werror"],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
target: {
host: {
- shared_libs: ["libz-host"],
+ shared_libs: [
+ "libz-host",
+ "libbase",
+ ],
},
android: {
- shared_libs: ["libz"],
+ shared_libs: [
+ "libz",
+ "libbase",
+ ],
},
windows: {
enabled: true,
@@ -38,6 +44,7 @@
static_libs: [
"libsparse",
"libz",
+ "libbase",
],
cflags: ["-Werror"],
@@ -50,6 +57,7 @@
static_libs: [
"libsparse",
"libz",
+ "libbase",
],
cflags: ["-Werror"],
@@ -61,6 +69,7 @@
static_libs: [
"libsparse",
"libz",
+ "libbase",
],
cflags: ["-Werror"],
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
index 2115998..51e60ef 100644
--- a/libsparse/output_file.c
+++ b/libsparse/output_file.c
@@ -584,7 +584,7 @@
.file_hdr_sz = SPARSE_HEADER_LEN,
.chunk_hdr_sz = CHUNK_HEADER_LEN,
.blk_sz = out->block_size,
- .total_blks = out->len / out->block_size,
+ .total_blks = DIV_ROUND_UP(out->len, out->block_size),
.total_chunks = chunks,
.image_checksum = 0
};
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 474c1fc..b67e94e 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -17,6 +17,10 @@
#ifndef _OUTPUT_FILE_H_
#define _OUTPUT_FILE_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <sparse/sparse.h>
struct output_file;
@@ -38,4 +42,8 @@
int read_all(int fd, void *buf, size_t len);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
index 91a12e6..763f43f 100644
--- a/libsparse/sparse_file.h
+++ b/libsparse/sparse_file.h
@@ -17,6 +17,10 @@
#ifndef _LIBSPARSE_SPARSE_FILE_H_
#define _LIBSPARSE_SPARSE_FILE_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <sparse/sparse.h>
struct sparse_file {
@@ -28,5 +32,8 @@
struct output_file *out;
};
+#ifdef __cplusplus
+}
+#endif
#endif /* _LIBSPARSE_SPARSE_FILE_H_ */
diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h
index c41f12a..779e038 100644
--- a/libsparse/sparse_format.h
+++ b/libsparse/sparse_format.h
@@ -18,6 +18,10 @@
#define _LIBSPARSE_SPARSE_FORMAT_H_
#include "sparse_defs.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef struct sparse_header {
__le32 magic; /* 0xed26ff3a */
__le16 major_version; /* (0x1) - reject images with higher major versions */
@@ -52,4 +56,8 @@
* For a CRC32 chunk, it's 4 bytes of CRC32
*/
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/libsparse/sparse_read.c b/libsparse/sparse_read.cpp
similarity index 88%
rename from libsparse/sparse_read.c
rename to libsparse/sparse_read.cpp
index a188202..bd66873 100644
--- a/libsparse/sparse_read.c
+++ b/libsparse/sparse_read.cpp
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
+#include <algorithm>
#include <inttypes.h>
#include <fcntl.h>
#include <stdarg.h>
@@ -25,17 +25,19 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
+#include <string>
#include <unistd.h>
#include <sparse/sparse.h>
+#include "android-base/stringprintf.h"
#include "defs.h"
#include "output_file.h"
#include "sparse_crc32.h"
#include "sparse_file.h"
#include "sparse_format.h"
+
#if defined(__APPLE__) && defined(__MACH__)
#define lseek64 lseek
#define off64_t off_t
@@ -45,57 +47,30 @@
#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
-#define COPY_BUF_SIZE (1024U*1024U)
+static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
static char *copybuf;
-#define min(a, b) \
- ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
+static std::string ErrorString(int err)
+{
+ if (err == -EOVERFLOW) return "EOF while reading file";
+ if (err == -EINVAL) return "Invalid sparse file format";
+ if (err == -ENOMEM) return "Failed allocation while reading file";
+ return android::base::StringPrintf("Unknown error %d", err);
+}
static void verbose_error(bool verbose, int err, const char *fmt, ...)
{
- char *s = "";
- char *at = "";
+ if (!verbose) return;
+
+ std::string msg = ErrorString(err);
if (fmt) {
+ msg += " at ";
va_list argp;
- int size;
-
va_start(argp, fmt);
- size = vsnprintf(NULL, 0, fmt, argp);
+ android::base::StringAppendV(&msg, fmt, argp);
va_end(argp);
-
- if (size < 0) {
- return;
- }
-
- at = malloc(size + 1);
- if (at == NULL) {
- return;
- }
-
- va_start(argp, fmt);
- vsnprintf(at, size, fmt, argp);
- va_end(argp);
- at[size] = 0;
- s = " at ";
}
- if (verbose) {
-#ifndef _WIN32
- if (err == -EOVERFLOW) {
- sparse_print_verbose("EOF while reading file%s%s\n", s, at);
- } else
-#endif
- if (err == -EINVAL) {
- sparse_print_verbose("Invalid sparse file format%s%s\n", s, at);
- } else if (err == -ENOMEM) {
- sparse_print_verbose("Failed allocation while reading file%s%s\n",
- s, at);
- } else {
- sparse_print_verbose("Unknown error %d%s%s\n", err, s, at);
- }
- }
- if (fmt) {
- free(at);
- }
+ sparse_print_verbose("%s\n", msg.c_str());
}
static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
@@ -104,7 +79,7 @@
{
int ret;
int chunk;
- unsigned int len = blocks * s->block_size;
+ int64_t len = blocks * s->block_size;
if (chunk_size % s->block_size != 0) {
return -EINVAL;
@@ -121,7 +96,7 @@
if (crc32) {
while (len) {
- chunk = min(len, COPY_BUF_SIZE);
+ chunk = std::min(len, COPY_BUF_SIZE);
ret = read_all(fd, copybuf, chunk);
if (ret < 0) {
return ret;
@@ -168,7 +143,7 @@
}
while (len) {
- chunk = min(len, COPY_BUF_SIZE);
+ chunk = std::min(len, COPY_BUF_SIZE);
*crc32 = sparse_crc32(*crc32, copybuf, chunk);
len -= chunk;
}
@@ -190,7 +165,7 @@
memset(copybuf, 0, COPY_BUF_SIZE);
while (len) {
- int chunk = min(len, COPY_BUF_SIZE);
+ int chunk = std::min(len, COPY_BUF_SIZE);
*crc32 = sparse_crc32(*crc32, copybuf, chunk);
len -= chunk;
}
@@ -284,7 +259,7 @@
off64_t offset;
if (!copybuf) {
- copybuf = malloc(COPY_BUF_SIZE);
+ copybuf = (char *)malloc(COPY_BUF_SIZE);
}
if (!copybuf) {
@@ -357,7 +332,7 @@
static int sparse_file_read_normal(struct sparse_file *s, int fd)
{
int ret;
- uint32_t *buf = malloc(s->block_size);
+ uint32_t *buf = (uint32_t *)malloc(s->block_size);
unsigned int block = 0;
int64_t remain = s->len;
int64_t offset = 0;
@@ -370,7 +345,7 @@
}
while (remain > 0) {
- to_read = min(remain, s->block_size);
+ to_read = std::min(remain, (int64_t)(s->block_size));
ret = read_all(fd, buf, to_read);
if (ret < 0) {
error("failed to read sparse file");
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index 130800e..32f1e1f 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -3,6 +3,9 @@
cc_library {
name: "libsuspend",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"autosuspend.c",
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 257d42d..3fae5e6 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -22,10 +22,15 @@
cc_library_shared {
name: "libsync",
- vendor_available: true,
defaults: ["libsync_defaults"],
}
+llndk_library {
+ name: "libsync",
+ symbol_file: "libsync.map.txt",
+ export_include_dirs: ["include"],
+}
+
// libsync_recovery is only intended for the recovery binary.
// Future versions of the kernel WILL require an updated libsync, and will break
// anything statically linked against the current libsync.
@@ -52,5 +57,4 @@
"-Wno-missing-field-initializers",
"-Wno-sign-compare",
],
- clang: true,
}
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt
index daa28ae..53bb07a 100644
--- a/libsync/libsync.map.txt
+++ b/libsync/libsync.map.txt
@@ -17,16 +17,12 @@
LIBSYNC {
global:
sync_merge; # introduced=26
- sync_get_fence_info; # introduced=26
- sync_free_fence_info; # introduced=26
+ sync_file_info; # introduced=26
+ sync_file_info_free; # introduced=26
+ sync_wait; # vndk
+ sync_fence_info; # vndk
+ sync_pt_info; # vndk
+ sync_fence_info_free; # vndk
local:
*;
};
-
-LIBSYNC_PLATFORM {
- global:
- sync_wait;
- sync_fence_info;
- sync_pt_info;
- sync_fence_info_free;
-} LIBSYNC_PLATFORM;
diff --git a/libsync/sync.c b/libsync/sync.c
index baeccda..e657658 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -275,7 +275,6 @@
info = calloc(1, sizeof(struct sync_file_info) +
num_fences * sizeof(struct sync_fence_info));
if (!info) {
- free(legacy_info);
return NULL;
}
info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 550ef42..d076a1a 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -1,6 +1,9 @@
cc_library_shared {
name: "libsysutils",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"src/SocketListener.cpp",
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index 1b6076f..87e2684 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -68,7 +68,7 @@
android_errorWriteLog(0x534e4554, "29831647");
c->sendMsg(500, "Command too large for buffer", false);
mSkipToNextNullByte = true;
- return false;
+ return true;
}
int offset = 0;
@@ -211,7 +211,6 @@
return;
overflow:
- LOG_EVENT_INT(78001, cli->getUid());
cli->sendMsg(500, "Command too long", false);
goto out;
}
diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp
index 896dad3..aad0394 100644
--- a/libsysutils/src/NetlinkListener.cpp
+++ b/libsysutils/src/NetlinkListener.cpp
@@ -57,8 +57,6 @@
count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,
mBuffer, sizeof(mBuffer), require_group, &uid));
if (count < 0) {
- if (uid > 0)
- LOG_EVENT_INT(65537, uid);
SLOGE("recvmsg failed (%s)", strerror(errno));
return false;
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ece623b..32ba692 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -30,73 +30,97 @@
enabled: false,
},
},
-
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-}
-
-cc_defaults {
- name: "libunwindstack_common",
- defaults: ["libunwindstack_flags"],
-
- srcs: [
- "ArmExidx.cpp",
- "Elf.cpp",
- "ElfInterface.cpp",
- "ElfInterfaceArm.cpp",
- "Log.cpp",
- "Regs.cpp",
- "Memory.cpp",
- ],
-
- shared_libs: [
- "libbase",
- "liblog",
- ],
}
cc_library {
name: "libunwindstack",
- defaults: ["libunwindstack_common"],
-}
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ defaults: ["libunwindstack_flags"],
+ export_include_dirs: ["include"],
-cc_library {
- name: "libunwindstack_debug",
- defaults: ["libunwindstack_common"],
+ srcs: [
+ "ArmExidx.cpp",
+ "DwarfCfa.cpp",
+ "DwarfDebugFrame.cpp",
+ "DwarfEhFrame.cpp",
+ "DwarfMemory.cpp",
+ "DwarfOp.cpp",
+ "DwarfSection.cpp",
+ "Elf.cpp",
+ "ElfInterface.cpp",
+ "ElfInterfaceArm.cpp",
+ "Log.cpp",
+ "MapInfo.cpp",
+ "Maps.cpp",
+ "Memory.cpp",
+ "Regs.cpp",
+ "Symbols.cpp",
+ ],
- cflags: [
- "-UNDEBUG",
- "-O0",
- "-g",
+ target: {
+ // Always disable optimizations for host to make it easier to debug.
+ linux: {
+ cflags: ["-O0", "-g"],
+ },
+ },
+
+ arch: {
+ x86: {
+ srcs: ["AsmGetRegsX86.S"],
+ },
+ x86_64: {
+ srcs: ["AsmGetRegsX86_64.S"],
+ },
+ },
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "liblzma",
],
}
//-------------------------------------------------------------------------
// Unit Tests
//-------------------------------------------------------------------------
-cc_defaults {
- name: "libunwindstack_test_common",
+cc_test {
+ name: "libunwindstack_test",
defaults: ["libunwindstack_flags"],
srcs: [
"tests/ArmExidxDecodeTest.cpp",
"tests/ArmExidxExtractTest.cpp",
+ "tests/DwarfCfaLogTest.cpp",
+ "tests/DwarfCfaTest.cpp",
+ "tests/DwarfDebugFrameTest.cpp",
+ "tests/DwarfEhFrameTest.cpp",
+ "tests/DwarfMemoryTest.cpp",
+ "tests/DwarfOpLogTest.cpp",
+ "tests/DwarfOpTest.cpp",
+ "tests/DwarfSectionTest.cpp",
+ "tests/DwarfSectionImplTest.cpp",
"tests/ElfInterfaceArmTest.cpp",
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
+ "tests/ElfTestUtils.cpp",
"tests/LogFake.cpp",
+ "tests/MapInfoGetElfTest.cpp",
+ "tests/MapsTest.cpp",
+ "tests/MemoryBufferTest.cpp",
"tests/MemoryFake.cpp",
"tests/MemoryFileTest.cpp",
"tests/MemoryLocalTest.cpp",
"tests/MemoryRangeTest.cpp",
"tests/MemoryRemoteTest.cpp",
+ "tests/MemoryTest.cpp",
+ "tests/RegsStepIfSignalHandlerTest.cpp",
"tests/RegsTest.cpp",
+ "tests/SymbolsTest.cpp",
+ "tests/UnwindTest.cpp",
],
cflags: [
@@ -107,6 +131,48 @@
shared_libs: [
"libbase",
"liblog",
+ "liblzma",
+ "libunwindstack",
+ ],
+
+ static_libs: [
+ "libgmock",
+ ],
+
+ target: {
+ linux: {
+ host_ldlibs: [
+ "-lrt",
+ ],
+ },
+ },
+
+ data: [
+ "tests/files/elf32.xz",
+ "tests/files/elf64.xz",
+ ],
+}
+
+//-------------------------------------------------------------------------
+// Tools
+//-------------------------------------------------------------------------
+cc_defaults {
+ name: "libunwindstack_tools",
+ defaults: ["libunwindstack_flags"],
+
+ shared_libs: [
+ "libunwindstack",
+ "libbase",
+ "liblzma",
+ ],
+}
+
+cc_binary {
+ name: "unwind",
+ defaults: ["libunwindstack_tools"],
+
+ srcs: [
+ "tools/unwind.cpp",
],
target: {
@@ -118,50 +184,31 @@
},
}
-// These unit tests run against the shared library.
-cc_test {
- name: "libunwindstack_test",
- defaults: ["libunwindstack_test_common"],
+cc_binary {
+ name: "unwind_info",
+ defaults: ["libunwindstack_tools"],
- shared_libs: [
- "libunwindstack",
+ srcs: [
+ "tools/unwind_info.cpp",
],
}
-// These unit tests run against the static debug library.
-cc_test {
- name: "libunwindstack_test_debug",
- defaults: ["libunwindstack_test_common"],
-
- static_libs: [
- "libunwindstack_debug",
- ],
-}
-
-//-------------------------------------------------------------------------
-// Utility Executables
-//-------------------------------------------------------------------------
-cc_defaults {
- name: "libunwindstack_executables",
- defaults: ["libunwindstack_flags"],
-
- shared_libs: [
- "libunwindstack",
- "libbase",
- ],
-
- static_libs: [
- "liblog",
- ],
-
- compile_multilib: "both",
-}
-
cc_binary {
- name: "unwind_info",
- defaults: ["libunwindstack_executables"],
+ name: "unwind_symbols",
+ defaults: ["libunwindstack_tools"],
srcs: [
- "unwind_info.cpp",
+ "tools/unwind_symbols.cpp",
+ ],
+}
+
+// Generates the elf data for use in the tests for .gnu_debugdata frames.
+// Once these files are generated, use the xz command to compress the data.
+cc_binary_host {
+ name: "gen_gnudebugdata",
+ defaults: ["libunwindstack_flags"],
+
+ srcs: [
+ "tests/GenGnuDebugdata.cpp",
],
}
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 12adf57..fed3e0e 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <assert.h>
#include <stdint.h>
#include <deque>
@@ -22,11 +21,15 @@
#include <android-base/stringprintf.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
#include "ArmExidx.h"
-#include "Log.h"
+#include "Check.h"
#include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
void ArmExidx::LogRawData() {
std::string log_str("Raw Data:");
@@ -173,7 +176,7 @@
}
inline bool ArmExidx::DecodePrefix_10_00(uint8_t byte) {
- assert((byte >> 4) == 0x8);
+ CHECK((byte >> 4) == 0x8);
uint16_t registers = (byte & 0xf) << 8;
if (!GetByte(&byte)) {
@@ -232,7 +235,7 @@
}
inline bool ArmExidx::DecodePrefix_10_01(uint8_t byte) {
- assert((byte >> 4) == 0x9);
+ CHECK((byte >> 4) == 0x9);
uint8_t bits = byte & 0xf;
if (bits == 13 || bits == 15) {
@@ -258,7 +261,7 @@
}
inline bool ArmExidx::DecodePrefix_10_10(uint8_t byte) {
- assert((byte >> 4) == 0xa);
+ CHECK((byte >> 4) == 0xa);
// 10100nnn: Pop r4-r[4+nnn]
// 10101nnn: Pop r4-r[4+nnn], r14
@@ -419,7 +422,7 @@
}
inline bool ArmExidx::DecodePrefix_10_11_1nnn(uint8_t byte) {
- assert((byte & ~0x07) == 0xb8);
+ CHECK((byte & ~0x07) == 0xb8);
// 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
if (log_) {
@@ -439,7 +442,7 @@
}
inline bool ArmExidx::DecodePrefix_10(uint8_t byte) {
- assert((byte >> 6) == 0x2);
+ CHECK((byte >> 6) == 0x2);
switch ((byte >> 4) & 0x3) {
case 0:
@@ -469,7 +472,7 @@
}
inline bool ArmExidx::DecodePrefix_11_000(uint8_t byte) {
- assert((byte & ~0x07) == 0xc0);
+ CHECK((byte & ~0x07) == 0xc0);
uint8_t bits = byte & 0x7;
if (bits == 6) {
@@ -550,7 +553,7 @@
}
inline bool ArmExidx::DecodePrefix_11_001(uint8_t byte) {
- assert((byte & ~0x07) == 0xc8);
+ CHECK((byte & ~0x07) == 0xc8);
uint8_t bits = byte & 0x7;
if (bits == 0) {
@@ -605,7 +608,7 @@
}
inline bool ArmExidx::DecodePrefix_11_010(uint8_t byte) {
- assert((byte & ~0x07) == 0xd0);
+ CHECK((byte & ~0x07) == 0xd0);
// 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
if (log_) {
@@ -624,7 +627,7 @@
}
inline bool ArmExidx::DecodePrefix_11(uint8_t byte) {
- assert((byte >> 6) == 0x3);
+ CHECK((byte >> 6) == 0x3);
switch ((byte >> 3) & 0x7) {
case 0:
@@ -684,3 +687,5 @@
while (Decode());
return status_ == ARM_STATUS_FINISH;
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
index 8c7f15a..f4361d4 100644
--- a/libunwindstack/ArmExidx.h
+++ b/libunwindstack/ArmExidx.h
@@ -21,6 +21,8 @@
#include <deque>
+namespace unwindstack {
+
// Forward declarations.
class Memory;
class RegsArm;
@@ -105,4 +107,6 @@
bool pc_set_ = false;
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_ARM_EXIDX_H
diff --git a/libunwindstack/AsmGetRegsX86.S b/libunwindstack/AsmGetRegsX86.S
new file mode 100644
index 0000000..14927a3
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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
+ .global AsmGetRegs
+ .balign 16
+ .type AsmGetRegs, @function
+AsmGetRegs:
+ .cfi_startproc
+ mov 4(%esp), %eax
+ movl $0, (%eax)
+ movl %ecx, 4(%eax)
+ movl %edx, 8(%eax)
+ movl %ebx, 12(%eax)
+
+ /* ESP */
+ leal 4(%esp), %ecx
+ movl %ecx, 16(%eax)
+
+ movl %ebp, 20(%eax)
+ movl %esi, 24(%eax)
+ movl %edi, 28(%eax)
+
+ /* EIP */
+ movl (%esp), %ecx
+ movl %ecx, 32(%eax)
+
+ movl %cs, 36(%eax)
+ movl %ss, 40(%eax)
+ movl %ds, 44(%eax)
+ movl %es, 48(%eax)
+ movl %fs, 52(%eax)
+ movl %gs, 56(%eax)
+ ret
+
+ .cfi_endproc
+ .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsX86_64.S b/libunwindstack/AsmGetRegsX86_64.S
new file mode 100644
index 0000000..4cd3b6f
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86_64.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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
+ .global AsmGetRegs
+ .balign 16
+ .type AsmGetRegs, @function
+AsmGetRegs:
+ .cfi_startproc
+ movq %rax, (%rdi)
+ movq %rdx, 8(%rdi)
+ movq %rcx, 16(%rdi)
+ movq %rbx, 24(%rdi)
+ movq %rsi, 32(%rdi)
+ movq %rdi, 40(%rdi)
+ movq %rbp, 48(%rdi)
+
+ /* RSP */
+ lea 8(%rsp), %rax
+ movq %rax, 56(%rdi)
+
+ movq %r8, 64(%rdi)
+ movq %r9, 72(%rdi)
+ movq %r10, 80(%rdi)
+ movq %r11, 88(%rdi)
+ movq %r12, 96(%rdi)
+ movq %r13, 104(%rdi)
+ movq %r14, 112(%rdi)
+ movq %r15, 120(%rdi)
+
+ /* RIP */
+ movq (%rsp), %rax
+ movq %rax, 128(%rdi)
+ ret
+
+ .cfi_endproc
+ .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/Check.h b/libunwindstack/Check.h
new file mode 100644
index 0000000..2d216d7
--- /dev/null
+++ b/libunwindstack/Check.h
@@ -0,0 +1,34 @@
+/*
+ * 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_ERROR_H
+#define _LIBUNWINDSTACK_ERROR_H
+
+#include <stdlib.h>
+
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
+
+#define CHECK(assertion) \
+ if (__builtin_expect(!(assertion), false)) { \
+ log(0, "%s:%d: %s\n", __FILE__, __LINE__, #assertion); \
+ abort(); \
+ }
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_ERROR_H
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
new file mode 100644
index 0000000..b1d55ba
--- /dev/null
+++ b/libunwindstack/DwarfCfa.cpp
@@ -0,0 +1,718 @@
+/*
+ * Copyright (C) 2016 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 <inttypes.h>
+#include <stdint.h>
+
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/Log.h>
+
+#include "DwarfCfa.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+#include "DwarfOp.h"
+
+namespace unwindstack {
+
+template <typename AddressType>
+constexpr typename DwarfCfa<AddressType>::process_func DwarfCfa<AddressType>::kCallbackTable[64];
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
+ dwarf_loc_regs_t* loc_regs) {
+ if (cie_loc_regs_ != nullptr) {
+ for (const auto& entry : *cie_loc_regs_) {
+ (*loc_regs)[entry.first] = entry.second;
+ }
+ }
+ last_error_ = DWARF_ERROR_NONE;
+
+ memory_->set_cur_offset(start_offset);
+ uint64_t cfa_offset;
+ cur_pc_ = fde_->pc_start;
+ while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc_ <= pc) {
+ operands_.clear();
+ // Read the cfa information.
+ uint8_t cfa_value;
+ if (!memory_->ReadBytes(&cfa_value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ uint8_t cfa_low = cfa_value & 0x3f;
+ // Check the 2 high bits.
+ switch (cfa_value >> 6) {
+ case 1:
+ cur_pc_ += cfa_low * fde_->cie->code_alignment_factor;
+ break;
+ case 2: {
+ uint64_t offset;
+ if (!memory_->ReadULEB128(&offset)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ SignedType signed_offset =
+ static_cast<SignedType>(offset) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[cfa_low] = {.type = DWARF_LOCATION_OFFSET,
+ .values = {static_cast<uint64_t>(signed_offset)}};
+ break;
+ }
+ case 3: {
+ if (cie_loc_regs_ == nullptr) {
+ log(0, "restore while processing cie");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+
+ auto reg_entry = cie_loc_regs_->find(cfa_low);
+ if (reg_entry == cie_loc_regs_->end()) {
+ loc_regs->erase(cfa_low);
+ } else {
+ (*loc_regs)[cfa_low] = reg_entry->second;
+ }
+ break;
+ }
+ case 0: {
+ const auto handle_func = DwarfCfa<AddressType>::kCallbackTable[cfa_low];
+ if (handle_func == nullptr) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ const auto cfa = &DwarfCfaInfo::kTable[cfa_low];
+ for (size_t i = 0; i < cfa->num_operands; i++) {
+ if (cfa->operands[i] == DW_EH_PE_block) {
+ uint64_t block_length;
+ if (!memory_->ReadULEB128(&block_length)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ operands_.push_back(block_length);
+ memory_->set_cur_offset(memory_->cur_offset() + block_length);
+ continue;
+ }
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ operands_.push_back(value);
+ }
+
+ if (!(this->*handle_func)(loc_regs)) {
+ return false;
+ }
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+template <typename AddressType>
+std::string DwarfCfa<AddressType>::GetOperandString(uint8_t operand, uint64_t value,
+ uint64_t* cur_pc) {
+ std::string string;
+ switch (operand) {
+ case DwarfCfaInfo::DWARF_DISPLAY_REGISTER:
+ string = " register(" + std::to_string(value) + ")";
+ break;
+ case DwarfCfaInfo::DWARF_DISPLAY_SIGNED_NUMBER:
+ string += " " + std::to_string(static_cast<SignedType>(value));
+ break;
+ case DwarfCfaInfo::DWARF_DISPLAY_ADVANCE_LOC:
+ *cur_pc += value;
+ // Fall through to log the value.
+ case DwarfCfaInfo::DWARF_DISPLAY_NUMBER:
+ string += " " + std::to_string(value);
+ break;
+ case DwarfCfaInfo::DWARF_DISPLAY_SET_LOC:
+ *cur_pc = value;
+ // Fall through to log the value.
+ case DwarfCfaInfo::DWARF_DISPLAY_ADDRESS:
+ if (std::is_same<AddressType, uint32_t>::value) {
+ string += android::base::StringPrintf(" 0x%" PRIx32, static_cast<uint32_t>(value));
+ } else {
+ string += android::base::StringPrintf(" 0x%" PRIx64, static_cast<uint64_t>(value));
+ }
+ break;
+ default:
+ string = " unknown";
+ }
+ return string;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset,
+ uint8_t reg) {
+ uint64_t offset;
+ if (!memory_->ReadULEB128(&offset)) {
+ return false;
+ }
+ uint64_t end_offset = memory_->cur_offset();
+ memory_->set_cur_offset(cfa_offset);
+
+ std::string raw_data = "Raw Data:";
+ for (uint64_t i = cfa_offset; i < end_offset; i++) {
+ uint8_t value;
+ if (!memory_->ReadBytes(&value, 1)) {
+ return false;
+ }
+ raw_data += android::base::StringPrintf(" 0x%02x", value);
+ }
+ log(indent, "DW_CFA_offset register(%d) %" PRId64, reg, offset);
+ log(indent, "%s", raw_data.c_str());
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
+ uint64_t* cur_pc) {
+ const auto* cfa = &DwarfCfaInfo::kTable[op];
+ if (cfa->name == nullptr) {
+ log(indent, "Illegal");
+ log(indent, "Raw Data: 0x%02x", op);
+ return true;
+ }
+
+ std::string log_string(cfa->name);
+ std::vector<std::string> expression_lines;
+ for (size_t i = 0; i < cfa->num_operands; i++) {
+ if (cfa->operands[i] == DW_EH_PE_block) {
+ // This is a Dwarf Expression.
+ uint64_t end_offset;
+ if (!memory_->ReadULEB128(&end_offset)) {
+ return false;
+ }
+ log_string += " " + std::to_string(end_offset);
+ end_offset += memory_->cur_offset();
+
+ DwarfOp<AddressType> op(memory_, nullptr);
+ op.GetLogInfo(memory_->cur_offset(), end_offset, &expression_lines);
+ memory_->set_cur_offset(end_offset);
+ } else {
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
+ return false;
+ }
+ log_string += GetOperandString(cfa->display_operands[i], value, cur_pc);
+ }
+ }
+ log(indent, "%s", log_string.c_str());
+
+ // Get the raw bytes of the data.
+ uint64_t end_offset = memory_->cur_offset();
+ memory_->set_cur_offset(cfa_offset);
+ std::string raw_data("Raw Data:");
+ for (uint64_t i = 0; i < end_offset - cfa_offset; i++) {
+ uint8_t value;
+ if (!memory_->ReadBytes(&value, 1)) {
+ return false;
+ }
+
+ // Only show 10 raw bytes per line.
+ if ((i % 10) == 0 && i != 0) {
+ log(indent, "%s", raw_data.c_str());
+ raw_data.clear();
+ }
+ if (raw_data.empty()) {
+ raw_data = "Raw Data:";
+ }
+ raw_data += android::base::StringPrintf(" 0x%02x", value);
+ }
+ if (!raw_data.empty()) {
+ log(indent, "%s", raw_data.c_str());
+ }
+
+ // Log any of the expression data.
+ for (const auto line : expression_lines) {
+ log(indent + 1, "%s", line.c_str());
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t load_bias,
+ uint64_t start_offset, uint64_t end_offset) {
+ memory_->set_cur_offset(start_offset);
+ uint64_t cfa_offset;
+ uint64_t cur_pc = fde_->pc_start;
+ uint64_t old_pc = cur_pc;
+ while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc <= pc) {
+ // Read the cfa information.
+ uint8_t cfa_value;
+ if (!memory_->ReadBytes(&cfa_value, 1)) {
+ return false;
+ }
+
+ // Check the 2 high bits.
+ uint8_t cfa_low = cfa_value & 0x3f;
+ switch (cfa_value >> 6) {
+ case 0:
+ if (!LogInstruction(indent, cfa_offset, cfa_low, &cur_pc)) {
+ return false;
+ }
+ break;
+ case 1:
+ log(indent, "DW_CFA_advance_loc %d", cfa_low);
+ log(indent, "Raw Data: 0x%02x", cfa_value);
+ cur_pc += cfa_low * fde_->cie->code_alignment_factor;
+ break;
+ case 2:
+ if (!LogOffsetRegisterString(indent, cfa_offset, cfa_low)) {
+ return false;
+ }
+ break;
+ case 3:
+ log(indent, "DW_CFA_restore register(%d)", cfa_low);
+ log(indent, "Raw Data: 0x%02x", cfa_value);
+ break;
+ }
+ if (cur_pc != old_pc) {
+ log(indent, "");
+ log(indent, "PC 0x%" PRIx64, cur_pc + load_bias);
+ }
+ old_pc = cur_pc;
+ }
+ return true;
+}
+
+// Static data.
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_nop(dwarf_loc_regs_t*) {
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_set_loc(dwarf_loc_regs_t*) {
+ AddressType cur_pc = cur_pc_;
+ AddressType new_pc = operands_[0];
+ if (new_pc < cur_pc) {
+ if (std::is_same<AddressType, uint32_t>::value) {
+ log(0, "Warning: PC is moving backwards: old 0x%" PRIx32 " new 0x%" PRIx32, cur_pc, new_pc);
+ } else {
+ log(0, "Warning: PC is moving backwards: old 0x%" PRIx64 " new 0x%" PRIx64, cur_pc, new_pc);
+ }
+ }
+ cur_pc_ = new_pc;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_advance_loc(dwarf_loc_regs_t*) {
+ cur_pc_ += operands_[0] * fde_->cie->code_alignment_factor;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_offset(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {operands_[1]}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_restore(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ if (cie_loc_regs_ == nullptr) {
+ log(0, "restore while processing cie");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ auto reg_entry = cie_loc_regs_->find(reg);
+ if (reg_entry == cie_loc_regs_->end()) {
+ loc_regs->erase(reg);
+ } else {
+ (*loc_regs)[reg] = reg_entry->second;
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_undefined(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_UNDEFINED};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_same_value(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ loc_regs->erase(reg);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_register(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ AddressType reg_dst = operands_[1];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_REGISTER, .values = {reg_dst}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_remember_state(dwarf_loc_regs_t* loc_regs) {
+ loc_reg_state_.push(*loc_regs);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_restore_state(dwarf_loc_regs_t* loc_regs) {
+ if (loc_reg_state_.size() == 0) {
+ log(0, "Warning: Attempt to restore without remember.");
+ return true;
+ }
+ *loc_regs = loc_reg_state_.top();
+ loc_reg_state_.pop();
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa(dwarf_loc_regs_t* loc_regs) {
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {operands_[0], operands_[1]}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_register(dwarf_loc_regs_t* loc_regs) {
+ auto cfa_location = loc_regs->find(CFA_REG);
+ if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+ log(0, "Attempt to set new register, but cfa is not already set to a register.");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+
+ cfa_location->second.values[0] = operands_[0];
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_offset(dwarf_loc_regs_t* loc_regs) {
+ // Changing the offset if this is not a register is illegal.
+ auto cfa_location = loc_regs->find(CFA_REG);
+ if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+ log(0, "Attempt to set offset, but cfa is not set to a register.");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ cfa_location->second.values[1] = operands_[0];
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_expression(dwarf_loc_regs_t* loc_regs) {
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_EXPRESSION,
+ .values = {operands_[0], memory_->cur_offset()}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_expression(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_EXPRESSION,
+ .values = {operands_[1], memory_->cur_offset()}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_offset_extended_sf(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType value = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(value)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_sf(dwarf_loc_regs_t* loc_regs) {
+ SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER,
+ .values = {operands_[0], static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_offset_sf(dwarf_loc_regs_t* loc_regs) {
+ // Changing the offset if this is not a register is illegal.
+ auto cfa_location = loc_regs->find(CFA_REG);
+ if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+ log(0, "Attempt to set offset, but cfa is not set to a register.");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ SignedType offset = static_cast<SignedType>(operands_[0]) * fde_->cie->data_alignment_factor;
+ cfa_location->second.values[1] = static_cast<uint64_t>(offset);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_offset(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_offset_sf(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_expression(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_EXPRESSION,
+ .values = {operands_[1], memory_->cur_offset()}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_gnu_negative_offset_extended(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType offset = -static_cast<SignedType>(operands_[1]);
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
+ {
+ // 0x00 DW_CFA_nop
+ "DW_CFA_nop",
+ 2,
+ 0,
+ {},
+ {},
+ },
+ {
+ "DW_CFA_set_loc", // 0x01 DW_CFA_set_loc
+ 2,
+ 1,
+ {DW_EH_PE_absptr},
+ {DWARF_DISPLAY_SET_LOC},
+ },
+ {
+ "DW_CFA_advance_loc1", // 0x02 DW_CFA_advance_loc1
+ 2,
+ 1,
+ {DW_EH_PE_udata1},
+ {DWARF_DISPLAY_ADVANCE_LOC},
+ },
+ {
+ "DW_CFA_advance_loc2", // 0x03 DW_CFA_advance_loc2
+ 2,
+ 1,
+ {DW_EH_PE_udata2},
+ {DWARF_DISPLAY_ADVANCE_LOC},
+ },
+ {
+ "DW_CFA_advance_loc4", // 0x04 DW_CFA_advance_loc4
+ 2,
+ 1,
+ {DW_EH_PE_udata4},
+ {DWARF_DISPLAY_ADVANCE_LOC},
+ },
+ {
+ "DW_CFA_offset_extended", // 0x05 DW_CFA_offset_extended
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_restore_extended", // 0x06 DW_CFA_restore_extended
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_undefined", // 0x07 DW_CFA_undefined
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_same_value", // 0x08 DW_CFA_same_value
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_register", // 0x09 DW_CFA_register
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_remember_state", // 0x0a DW_CFA_remember_state
+ 2,
+ 0,
+ {},
+ {},
+ },
+ {
+ "DW_CFA_restore_state", // 0x0b DW_CFA_restore_state
+ 2,
+ 0,
+ {},
+ {},
+ },
+ {
+ "DW_CFA_def_cfa", // 0x0c DW_CFA_def_cfa
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_register", // 0x0d DW_CFA_def_cfa_register
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_def_cfa_offset", // 0x0e DW_CFA_def_cfa_offset
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_expression", // 0x0f DW_CFA_def_cfa_expression
+ 2,
+ 1,
+ {DW_EH_PE_block},
+ {DWARF_DISPLAY_EVAL_BLOCK},
+ },
+ {
+ "DW_CFA_expression", // 0x10 DW_CFA_expression
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_block},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
+ },
+ {
+ "DW_CFA_offset_extended_sf", // 0x11 DW_CFA_offset_extend_sf
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_sf", // 0x12 DW_CFA_def_cfa_sf
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_offset_sf", // 0x13 DW_CFA_def_cfa_offset_sf
+ 2,
+ 1,
+ {DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_val_offset", // 0x14 DW_CFA_val_offset
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_val_offset_sf", // 0x15 DW_CFA_val_offset_sf
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_val_expression", // 0x16 DW_CFA_val_expression
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_block},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
+ },
+ {nullptr, 0, 0, {}, {}}, // 0x17 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x18 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x19 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1a illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1b illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1c DW_CFA_lo_user (Treat as illegal)
+ {nullptr, 0, 0, {}, {}}, // 0x1d illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1e illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1f illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x20 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x21 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x22 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x23 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x24 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x25 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x26 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x27 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x28 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x29 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x2a illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x2b illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x2c illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
+ {
+ "DW_CFA_GNU_args_size", // 0x2e DW_CFA_GNU_args_size
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_GNU_negative_offset_extended", // 0x2f DW_CFA_GNU_negative_offset_extended
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {nullptr, 0, 0, {}, {}}, // 0x31 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x32 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x33 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x34 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x35 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x36 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x37 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x38 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x39 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3a illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3b illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3c illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3d illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3e illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3f DW_CFA_hi_user (Treat as illegal)
+};
+
+// Explicitly instantiate DwarfCfa.
+template class DwarfCfa<uint32_t>;
+template class DwarfCfa<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
new file mode 100644
index 0000000..62b9b7a
--- /dev/null
+++ b/libunwindstack/DwarfCfa.h
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2016 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_DWARF_CFA_H
+#define _LIBUNWINDSTACK_DWARF_CFA_H
+
+#include <stdint.h>
+
+#include <stack>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+
+#include "DwarfError.h"
+
+namespace unwindstack {
+
+// DWARF Standard home: http://dwarfstd.org/
+// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
+// See section 6.4.2.1 for a description of the DW_CFA_xxx values.
+
+class DwarfCfaInfo {
+ public:
+ enum DisplayType : uint8_t {
+ DWARF_DISPLAY_NONE = 0,
+ DWARF_DISPLAY_REGISTER,
+ DWARF_DISPLAY_NUMBER,
+ DWARF_DISPLAY_SIGNED_NUMBER,
+ DWARF_DISPLAY_EVAL_BLOCK,
+ DWARF_DISPLAY_ADDRESS,
+ DWARF_DISPLAY_SET_LOC,
+ DWARF_DISPLAY_ADVANCE_LOC,
+ };
+
+ struct Info {
+ const char* name;
+ uint8_t supported_version;
+ uint8_t num_operands;
+ uint8_t operands[2];
+ uint8_t display_operands[2];
+ };
+
+ const static Info kTable[64];
+};
+
+template <typename AddressType>
+class DwarfCfa {
+ // Signed version of AddressType
+ typedef typename std::make_signed<AddressType>::type SignedType;
+
+ public:
+ DwarfCfa(DwarfMemory* memory, const DwarfFde* fde) : memory_(memory), fde_(fde) {}
+ virtual ~DwarfCfa() = default;
+
+ bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
+ dwarf_loc_regs_t* loc_regs);
+
+ bool Log(uint32_t indent, uint64_t pc, uint64_t load_bias, uint64_t start_offset,
+ uint64_t end_offset);
+
+ DwarfError last_error() { return last_error_; }
+
+ AddressType cur_pc() { return cur_pc_; }
+
+ void set_cie_loc_regs(const dwarf_loc_regs_t* cie_loc_regs) { cie_loc_regs_ = cie_loc_regs; }
+
+ protected:
+ std::string GetOperandString(uint8_t operand, uint64_t value, uint64_t* cur_pc);
+
+ bool LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset, uint8_t reg);
+
+ bool LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op, uint64_t* cur_pc);
+
+ private:
+ DwarfError last_error_;
+ DwarfMemory* memory_;
+ const DwarfFde* fde_;
+
+ AddressType cur_pc_;
+ const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
+ std::vector<AddressType> operands_;
+ std::stack<dwarf_loc_regs_t> loc_reg_state_;
+
+ // CFA processing functions.
+ bool cfa_nop(dwarf_loc_regs_t*);
+ bool cfa_set_loc(dwarf_loc_regs_t*);
+ bool cfa_advance_loc(dwarf_loc_regs_t*);
+ bool cfa_offset(dwarf_loc_regs_t*);
+ bool cfa_restore(dwarf_loc_regs_t*);
+ bool cfa_undefined(dwarf_loc_regs_t*);
+ bool cfa_same_value(dwarf_loc_regs_t*);
+ bool cfa_register(dwarf_loc_regs_t*);
+ bool cfa_remember_state(dwarf_loc_regs_t*);
+ bool cfa_restore_state(dwarf_loc_regs_t*);
+ bool cfa_def_cfa(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_register(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_offset(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_expression(dwarf_loc_regs_t*);
+ bool cfa_expression(dwarf_loc_regs_t*);
+ bool cfa_offset_extended_sf(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_sf(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_offset_sf(dwarf_loc_regs_t*);
+ bool cfa_val_offset(dwarf_loc_regs_t*);
+ bool cfa_val_offset_sf(dwarf_loc_regs_t*);
+ bool cfa_val_expression(dwarf_loc_regs_t*);
+ bool cfa_gnu_negative_offset_extended(dwarf_loc_regs_t*);
+
+ using process_func = bool (DwarfCfa::*)(dwarf_loc_regs_t*);
+ constexpr static process_func kCallbackTable[64] = {
+ // 0x00 DW_CFA_nop
+ &DwarfCfa::cfa_nop,
+ // 0x01 DW_CFA_set_loc
+ &DwarfCfa::cfa_set_loc,
+ // 0x02 DW_CFA_advance_loc1
+ &DwarfCfa::cfa_advance_loc,
+ // 0x03 DW_CFA_advance_loc2
+ &DwarfCfa::cfa_advance_loc,
+ // 0x04 DW_CFA_advance_loc4
+ &DwarfCfa::cfa_advance_loc,
+ // 0x05 DW_CFA_offset_extended
+ &DwarfCfa::cfa_offset,
+ // 0x06 DW_CFA_restore_extended
+ &DwarfCfa::cfa_restore,
+ // 0x07 DW_CFA_undefined
+ &DwarfCfa::cfa_undefined,
+ // 0x08 DW_CFA_same_value
+ &DwarfCfa::cfa_same_value,
+ // 0x09 DW_CFA_register
+ &DwarfCfa::cfa_register,
+ // 0x0a DW_CFA_remember_state
+ &DwarfCfa::cfa_remember_state,
+ // 0x0b DW_CFA_restore_state
+ &DwarfCfa::cfa_restore_state,
+ // 0x0c DW_CFA_def_cfa
+ &DwarfCfa::cfa_def_cfa,
+ // 0x0d DW_CFA_def_cfa_register
+ &DwarfCfa::cfa_def_cfa_register,
+ // 0x0e DW_CFA_def_cfa_offset
+ &DwarfCfa::cfa_def_cfa_offset,
+ // 0x0f DW_CFA_def_cfa_expression
+ &DwarfCfa::cfa_def_cfa_expression,
+ // 0x10 DW_CFA_expression
+ &DwarfCfa::cfa_expression,
+ // 0x11 DW_CFA_offset_extended_sf
+ &DwarfCfa::cfa_offset_extended_sf,
+ // 0x12 DW_CFA_def_cfa_sf
+ &DwarfCfa::cfa_def_cfa_sf,
+ // 0x13 DW_CFA_def_cfa_offset_sf
+ &DwarfCfa::cfa_def_cfa_offset_sf,
+ // 0x14 DW_CFA_val_offset
+ &DwarfCfa::cfa_val_offset,
+ // 0x15 DW_CFA_val_offset_sf
+ &DwarfCfa::cfa_val_offset_sf,
+ // 0x16 DW_CFA_val_expression
+ &DwarfCfa::cfa_val_expression,
+ // 0x17 illegal cfa
+ nullptr,
+ // 0x18 illegal cfa
+ nullptr,
+ // 0x19 illegal cfa
+ nullptr,
+ // 0x1a illegal cfa
+ nullptr,
+ // 0x1b illegal cfa
+ nullptr,
+ // 0x1c DW_CFA_lo_user (Treat this as illegal)
+ nullptr,
+ // 0x1d illegal cfa
+ nullptr,
+ // 0x1e illegal cfa
+ nullptr,
+ // 0x1f illegal cfa
+ nullptr,
+ // 0x20 illegal cfa
+ nullptr,
+ // 0x21 illegal cfa
+ nullptr,
+ // 0x22 illegal cfa
+ nullptr,
+ // 0x23 illegal cfa
+ nullptr,
+ // 0x24 illegal cfa
+ nullptr,
+ // 0x25 illegal cfa
+ nullptr,
+ // 0x26 illegal cfa
+ nullptr,
+ // 0x27 illegal cfa
+ nullptr,
+ // 0x28 illegal cfa
+ nullptr,
+ // 0x29 illegal cfa
+ nullptr,
+ // 0x2a illegal cfa
+ nullptr,
+ // 0x2b illegal cfa
+ nullptr,
+ // 0x2c illegal cfa
+ nullptr,
+ // 0x2d DW_CFA_GNU_window_save (Treat this as illegal)
+ nullptr,
+ // 0x2e DW_CFA_GNU_args_size
+ &DwarfCfa::cfa_nop,
+ // 0x2f DW_CFA_GNU_negative_offset_extended
+ &DwarfCfa::cfa_gnu_negative_offset_extended,
+ // 0x30 illegal cfa
+ nullptr,
+ // 0x31 illegal cfa
+ nullptr,
+ // 0x32 illegal cfa
+ nullptr,
+ // 0x33 illegal cfa
+ nullptr,
+ // 0x34 illegal cfa
+ nullptr,
+ // 0x35 illegal cfa
+ nullptr,
+ // 0x36 illegal cfa
+ nullptr,
+ // 0x37 illegal cfa
+ nullptr,
+ // 0x38 illegal cfa
+ nullptr,
+ // 0x39 illegal cfa
+ nullptr,
+ // 0x3a illegal cfa
+ nullptr,
+ // 0x3b illegal cfa
+ nullptr,
+ // 0x3c illegal cfa
+ nullptr,
+ // 0x3d illegal cfa
+ nullptr,
+ // 0x3e illegal cfa
+ nullptr,
+ // 0x3f DW_CFA_hi_user (Treat this as illegal)
+ nullptr,
+ };
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_CFA_H
diff --git a/libunwindstack/DwarfDebugFrame.cpp b/libunwindstack/DwarfDebugFrame.cpp
new file mode 100644
index 0000000..5707596
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.cpp
@@ -0,0 +1,316 @@
+/*
+ * 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 <stdlib.h>
+
+#include <algorithm>
+
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Memory.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+namespace unwindstack {
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
+ offset_ = offset;
+ end_offset_ = offset + size;
+
+ memory_.clear_func_offset();
+ memory_.clear_text_offset();
+ memory_.set_data_offset(offset);
+ memory_.set_cur_offset(offset);
+
+ return CreateSortedFdeList();
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
+ uint8_t version;
+ if (!memory_.ReadBytes(&version, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ // Read the augmentation string.
+ std::vector<char> aug_string;
+ char aug_value;
+ bool get_encoding = false;
+ do {
+ if (!memory_.ReadBytes(&aug_value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (aug_value == 'R') {
+ get_encoding = true;
+ }
+ aug_string.push_back(aug_value);
+ } while (aug_value != '\0');
+
+ if (version == 4) {
+ // Skip the Address Size field.
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+
+ // Read the segment size.
+ if (!memory_.ReadBytes(segment_size, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } else {
+ *segment_size = 0;
+ }
+
+ if (aug_string[0] != 'z' || !get_encoding) {
+ // No encoding
+ return true;
+ }
+
+ // Skip code alignment factor
+ uint8_t value;
+ do {
+ if (!memory_.ReadBytes(&value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } while (value & 0x80);
+
+ // Skip data alignment factor
+ do {
+ if (!memory_.ReadBytes(&value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } while (value & 0x80);
+
+ if (version == 1) {
+ // Skip return address register.
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+ } else {
+ // Skip return address register.
+ do {
+ if (!memory_.ReadBytes(&value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } while (value & 0x80);
+ }
+
+ // Skip the augmentation length.
+ do {
+ if (!memory_.ReadBytes(&value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } while (value & 0x80);
+
+ for (size_t i = 1; i < aug_string.size(); i++) {
+ if (aug_string[i] == 'R') {
+ if (!memory_.ReadBytes(encoding, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ // Got the encoding, that's all we are looking for.
+ return true;
+ } else if (aug_string[i] == 'L') {
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+ } else if (aug_string[i] == 'P') {
+ uint8_t encoding;
+ if (!memory_.ReadBytes(&encoding, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ uint64_t value;
+ if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ }
+ }
+
+ // It should be impossible to get here.
+ abort();
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::AddFdeInfo(uint64_t entry_offset, uint8_t segment_size,
+ uint8_t encoding) {
+ if (segment_size != 0) {
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+ }
+
+ uint64_t start;
+ if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ uint64_t length;
+ if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (length != 0) {
+ fdes_.emplace_back(entry_offset, start, length);
+ }
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::CreateSortedFdeList() {
+ memory_.set_cur_offset(offset_);
+
+ // Loop through all of the entries and read just enough to create
+ // a sorted list of pcs.
+ // This code assumes that first comes the cie, then the fdes that
+ // it applies to.
+ uint64_t cie_offset = 0;
+ uint8_t address_encoding;
+ uint8_t segment_size;
+ while (memory_.cur_offset() < end_offset_) {
+ uint64_t cur_entry_offset = memory_.cur_offset();
+
+ // Figure out the entry length and type.
+ uint32_t value32;
+ if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ uint64_t next_entry_offset;
+ if (value32 == static_cast<uint32_t>(-1)) {
+ uint64_t value64;
+ if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ next_entry_offset = memory_.cur_offset() + value64;
+
+ // Read the Cie Id of a Cie or the pointer of the Fde.
+ if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (value64 == static_cast<uint64_t>(-1)) {
+ // Cie 64 bit
+ address_encoding = DW_EH_PE_sdata8;
+ if (!GetCieInfo(&segment_size, &address_encoding)) {
+ return false;
+ }
+ cie_offset = cur_entry_offset;
+ } else {
+ if (offset_ + value64 != cie_offset) {
+ // This means that this Fde is not following the Cie.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Fde 64 bit
+ if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
+ return false;
+ }
+ }
+ } else {
+ next_entry_offset = memory_.cur_offset() + value32;
+
+ // Read the Cie Id of a Cie or the pointer of the Fde.
+ if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (value32 == static_cast<uint32_t>(-1)) {
+ // Cie 32 bit
+ address_encoding = DW_EH_PE_sdata4;
+ if (!GetCieInfo(&segment_size, &address_encoding)) {
+ return false;
+ }
+ cie_offset = cur_entry_offset;
+ } else {
+ if (offset_ + value32 != cie_offset) {
+ // This means that this Fde is not following the Cie.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Fde 32 bit
+ if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
+ return false;
+ }
+ }
+ }
+
+ if (next_entry_offset < memory_.cur_offset()) {
+ // This indicates some kind of corruption, or malformed section data.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ memory_.set_cur_offset(next_entry_offset);
+ }
+
+ // Sort the entries.
+ std::sort(fdes_.begin(), fdes_.end(), [](const FdeInfo& a, const FdeInfo& b) {
+ if (a.start == b.start) return a.end < b.end;
+ return a.start < b.start;
+ });
+
+ fde_count_ = fdes_.size();
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+ if (fde_count_ == 0) {
+ return false;
+ }
+
+ size_t first = 0;
+ size_t last = fde_count_;
+ while (first < last) {
+ size_t current = (first + last) / 2;
+ const FdeInfo* info = &fdes_[current];
+ if (pc >= info->start && pc <= info->end) {
+ *fde_offset = info->offset;
+ return true;
+ }
+
+ if (pc < info->start) {
+ last = current;
+ } else {
+ first = current + 1;
+ }
+ }
+ return false;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfDebugFrame<AddressType>::GetFdeFromIndex(size_t index) {
+ if (index >= fdes_.size()) {
+ return nullptr;
+ }
+ return this->GetFdeFromOffset(fdes_[index].offset);
+}
+
+// Explicitly instantiate DwarfDebugFrame.
+template class DwarfDebugFrame<uint32_t>;
+template class DwarfDebugFrame<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
new file mode 100644
index 0000000..6a6178e
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -0,0 +1,80 @@
+/*
+ * 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_DWARF_DEBUG_FRAME_H
+#define _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
+template <typename AddressType>
+class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
+ public:
+ // Add these so that the protected members of DwarfSectionImpl
+ // can be accessed without needing a this->.
+ using DwarfSectionImpl<AddressType>::memory_;
+ using DwarfSectionImpl<AddressType>::fde_count_;
+ using DwarfSectionImpl<AddressType>::last_error_;
+
+ struct FdeInfo {
+ FdeInfo(uint64_t offset, uint64_t start, uint64_t length)
+ : offset(offset), start(start), end(start + length) {}
+
+ uint64_t offset;
+ AddressType start;
+ AddressType end;
+ };
+
+ DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+ virtual ~DwarfDebugFrame() = default;
+
+ bool Init(uint64_t offset, uint64_t size) override;
+
+ bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+
+ const DwarfFde* GetFdeFromIndex(size_t index) override;
+
+ bool IsCie32(uint32_t value32) override { return value32 == static_cast<uint32_t>(-1); }
+
+ bool IsCie64(uint64_t value64) override { return value64 == static_cast<uint64_t>(-1); }
+
+ uint64_t GetCieOffsetFromFde32(uint32_t pointer) override { return offset_ + pointer; }
+
+ uint64_t GetCieOffsetFromFde64(uint64_t pointer) override { return offset_ + pointer; }
+
+ uint64_t AdjustPcFromFde(uint64_t pc) override { return pc; }
+
+ bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);
+
+ bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding);
+
+ bool CreateSortedFdeList();
+
+ protected:
+ uint64_t offset_;
+ uint64_t end_offset_;
+
+ std::vector<FdeInfo> fdes_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrame.cpp
new file mode 100644
index 0000000..db8f558
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.cpp
@@ -0,0 +1,217 @@
+/*
+ * 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 <unwindstack/DwarfStructs.h>
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+#include "DwarfEhFrame.h"
+#include "DwarfError.h"
+
+namespace unwindstack {
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
+ uint8_t data[4];
+
+ memory_.clear_func_offset();
+ memory_.clear_text_offset();
+ memory_.set_data_offset(offset);
+ memory_.set_cur_offset(offset);
+
+ // Read the first four bytes all at once.
+ if (!memory_.ReadBytes(data, 4)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ version_ = data[0];
+ if (version_ != 1) {
+ // Unknown version.
+ last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+ return false;
+ }
+
+ ptr_encoding_ = data[1];
+ uint8_t fde_count_encoding = data[2];
+ table_encoding_ = data[3];
+ table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
+
+ memory_.set_pc_offset(memory_.cur_offset());
+ if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ memory_.set_pc_offset(memory_.cur_offset());
+ if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ entries_offset_ = memory_.cur_offset();
+ entries_end_ = offset + size;
+ entries_data_offset_ = offset;
+ cur_entries_offset_ = entries_offset_;
+
+ return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfEhFrame<AddressType>::GetFdeFromIndex(size_t index) {
+ const FdeInfo* info = GetFdeInfoFromIndex(index);
+ if (info == nullptr) {
+ return nullptr;
+ }
+ return this->GetFdeFromOffset(info->offset);
+}
+
+template <typename AddressType>
+const typename DwarfEhFrame<AddressType>::FdeInfo* DwarfEhFrame<AddressType>::GetFdeInfoFromIndex(
+ size_t index) {
+ auto entry = fde_info_.find(index);
+ if (entry != fde_info_.end()) {
+ return &fde_info_[index];
+ }
+ FdeInfo* info = &fde_info_[index];
+
+ memory_.set_data_offset(entries_data_offset_);
+ memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
+ memory_.set_pc_offset(memory_.cur_offset());
+ uint64_t value;
+ if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
+ !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ fde_info_.erase(index);
+ return nullptr;
+ }
+ info->pc = value + 4;
+ return info;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
+ uint64_t total_entries) {
+ CHECK(fde_count_ > 0);
+ CHECK(total_entries <= fde_count_);
+
+ size_t first = 0;
+ size_t last = total_entries;
+ while (first < last) {
+ size_t current = (first + last) / 2;
+ const FdeInfo* info = GetFdeInfoFromIndex(current);
+ if (pc == info->pc) {
+ *fde_offset = info->offset;
+ return true;
+ }
+ if (pc < info->pc) {
+ last = current;
+ } else {
+ first = current + 1;
+ }
+ }
+ if (last != 0) {
+ const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
+ *fde_offset = info->offset;
+ return true;
+ }
+ return false;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
+ CHECK(fde_count_ != 0);
+ last_error_ = DWARF_ERROR_NONE;
+ // We can do a binary search if the pc is in the range of the elements
+ // that have already been cached.
+ if (!fde_info_.empty()) {
+ const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
+ if (pc >= info->pc) {
+ *fde_offset = info->offset;
+ return true;
+ }
+ if (pc < info->pc) {
+ return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
+ }
+ }
+
+ if (cur_entries_offset_ == 0) {
+ // All entries read, or error encountered.
+ return false;
+ }
+
+ memory_.set_data_offset(entries_data_offset_);
+ memory_.set_cur_offset(cur_entries_offset_);
+ cur_entries_offset_ = 0;
+
+ FdeInfo* prev_info = nullptr;
+ for (size_t current = fde_info_.size();
+ current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
+ memory_.set_pc_offset(memory_.cur_offset());
+ uint64_t value;
+ if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ FdeInfo* info = &fde_info_[current];
+ if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+ fde_info_.erase(current);
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ info->pc = value + 4;
+
+ if (pc < info->pc) {
+ if (prev_info == nullptr) {
+ return false;
+ }
+ cur_entries_offset_ = memory_.cur_offset();
+ *fde_offset = prev_info->offset;
+ return true;
+ }
+ prev_info = info;
+ }
+
+ if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
+ *fde_offset = prev_info->offset;
+ return true;
+ }
+ return false;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+ if (fde_count_ == 0) {
+ return false;
+ }
+
+ if (table_entry_size_ > 0) {
+ // Do a binary search since the size of each table entry is fixed.
+ return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
+ } else {
+ // Do a sequential search since each table entry size is variable.
+ return GetFdeOffsetSequential(pc, fde_offset);
+ }
+}
+
+// Explicitly instantiate DwarfEhFrame.
+template class DwarfEhFrame<uint32_t>;
+template class DwarfEhFrame<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
new file mode 100644
index 0000000..4207b42
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 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_DWARF_EH_FRAME_H
+#define _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+
+#include <stdint.h>
+
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+template <typename AddressType>
+class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
+ public:
+ // Add these so that the protected members of DwarfSectionImpl
+ // can be accessed without needing a this->.
+ using DwarfSectionImpl<AddressType>::memory_;
+ using DwarfSectionImpl<AddressType>::fde_count_;
+ using DwarfSectionImpl<AddressType>::last_error_;
+
+ struct FdeInfo {
+ AddressType pc;
+ uint64_t offset;
+ };
+
+ DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+ virtual ~DwarfEhFrame() = default;
+
+ bool Init(uint64_t offset, uint64_t size) override;
+
+ bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+
+ const DwarfFde* GetFdeFromIndex(size_t index) override;
+
+ bool IsCie32(uint32_t value32) override { return value32 == 0; }
+
+ bool IsCie64(uint64_t value64) override { return value64 == 0; }
+
+ uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+ return memory_.cur_offset() - pointer - 4;
+ }
+
+ uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+ return memory_.cur_offset() - pointer - 8;
+ }
+
+ uint64_t AdjustPcFromFde(uint64_t pc) override {
+ // The eh_frame uses relative pcs.
+ return pc + memory_.cur_offset();
+ }
+
+ const FdeInfo* GetFdeInfoFromIndex(size_t index);
+
+ bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset);
+
+ bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
+
+ protected:
+ uint8_t version_;
+ uint8_t ptr_encoding_;
+ uint8_t table_encoding_;
+ size_t table_entry_size_;
+
+ uint64_t ptr_offset_;
+
+ uint64_t entries_offset_;
+ uint64_t entries_end_;
+ uint64_t entries_data_offset_;
+ uint64_t cur_entries_offset_ = 0;
+
+ std::unordered_map<uint64_t, FdeInfo> fde_info_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h
new file mode 100644
index 0000000..20db222
--- /dev/null
+++ b/libunwindstack/DwarfEncoding.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 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_DWARF_ENCODING_H
+#define _LIBUNWINDSTACK_DWARF_ENCODING_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum DwarfEncoding : uint8_t {
+ DW_EH_PE_omit = 0xff,
+
+ DW_EH_PE_absptr = 0x00,
+ DW_EH_PE_uleb128 = 0x01,
+ DW_EH_PE_udata2 = 0x02,
+ DW_EH_PE_udata4 = 0x03,
+ DW_EH_PE_udata8 = 0x04,
+ DW_EH_PE_sleb128 = 0x09,
+ DW_EH_PE_sdata2 = 0x0a,
+ DW_EH_PE_sdata4 = 0x0b,
+ DW_EH_PE_sdata8 = 0x0c,
+
+ DW_EH_PE_pcrel = 0x10,
+ DW_EH_PE_textrel = 0x20,
+ DW_EH_PE_datarel = 0x30,
+ DW_EH_PE_funcrel = 0x40,
+ DW_EH_PE_aligned = 0x50,
+
+ // The following are special values used to encode CFA and OP operands.
+ DW_EH_PE_udata1 = 0x0d,
+ DW_EH_PE_sdata1 = 0x0e,
+ DW_EH_PE_block = 0x0f,
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/libunwindstack/DwarfError.h b/libunwindstack/DwarfError.h
new file mode 100644
index 0000000..54199b8
--- /dev/null
+++ b/libunwindstack/DwarfError.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 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_DWARF_ERROR_H
+#define _LIBUNWINDSTACK_DWARF_ERROR_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum DwarfError : uint8_t {
+ DWARF_ERROR_NONE,
+ DWARF_ERROR_MEMORY_INVALID,
+ DWARF_ERROR_ILLEGAL_VALUE,
+ DWARF_ERROR_ILLEGAL_STATE,
+ DWARF_ERROR_STACK_INDEX_NOT_VALID,
+ DWARF_ERROR_NOT_IMPLEMENTED,
+ DWARF_ERROR_TOO_MANY_ITERATIONS,
+ DWARF_ERROR_CFA_NOT_DEFINED,
+ DWARF_ERROR_UNSUPPORTED_VERSION,
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
new file mode 100644
index 0000000..901f492
--- /dev/null
+++ b/libunwindstack/DwarfMemory.cpp
@@ -0,0 +1,253 @@
+/*
+ * 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 <string>
+
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+#include "DwarfEncoding.h"
+
+namespace unwindstack {
+
+bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) {
+ if (!memory_->Read(cur_offset_, dst, num_bytes)) {
+ return false;
+ }
+ cur_offset_ += num_bytes;
+ return true;
+}
+
+template <typename SignedType>
+bool DwarfMemory::ReadSigned(uint64_t* value) {
+ SignedType signed_value;
+ if (!ReadBytes(&signed_value, sizeof(SignedType))) {
+ return false;
+ }
+ *value = static_cast<int64_t>(signed_value);
+ return true;
+}
+
+bool DwarfMemory::ReadULEB128(uint64_t* value) {
+ uint64_t cur_value = 0;
+ uint64_t shift = 0;
+ uint8_t byte;
+ do {
+ if (!ReadBytes(&byte, 1)) {
+ return false;
+ }
+ cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+ *value = cur_value;
+ return true;
+}
+
+bool DwarfMemory::ReadSLEB128(int64_t* value) {
+ uint64_t cur_value = 0;
+ uint64_t shift = 0;
+ uint8_t byte;
+ do {
+ if (!ReadBytes(&byte, 1)) {
+ return false;
+ }
+ cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+ if (byte & 0x40) {
+ // Negative value, need to sign extend.
+ cur_value |= static_cast<uint64_t>(-1) << shift;
+ }
+ *value = static_cast<int64_t>(cur_value);
+ return true;
+}
+
+template <typename AddressType>
+size_t DwarfMemory::GetEncodedSize(uint8_t encoding) {
+ switch (encoding & 0x0f) {
+ case DW_EH_PE_absptr:
+ return sizeof(AddressType);
+ case DW_EH_PE_udata1:
+ case DW_EH_PE_sdata1:
+ return 1;
+ case DW_EH_PE_udata2:
+ case DW_EH_PE_sdata2:
+ return 2;
+ case DW_EH_PE_udata4:
+ case DW_EH_PE_sdata4:
+ return 4;
+ case DW_EH_PE_udata8:
+ case DW_EH_PE_sdata8:
+ return 8;
+ case DW_EH_PE_uleb128:
+ case DW_EH_PE_sleb128:
+ default:
+ return 0;
+ }
+}
+
+bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) {
+ CHECK((encoding & 0x0f) == 0);
+ CHECK(encoding != DW_EH_PE_aligned);
+
+ // Handle the encoding.
+ switch (encoding) {
+ case DW_EH_PE_absptr:
+ // Nothing to do.
+ break;
+ case DW_EH_PE_pcrel:
+ if (pc_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += pc_offset_;
+ break;
+ case DW_EH_PE_textrel:
+ if (text_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += text_offset_;
+ break;
+ case DW_EH_PE_datarel:
+ if (data_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += data_offset_;
+ break;
+ case DW_EH_PE_funcrel:
+ if (func_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += func_offset_;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfMemory::ReadEncodedValue(uint8_t encoding, uint64_t* value) {
+ if (encoding == DW_EH_PE_omit) {
+ *value = 0;
+ return true;
+ } else if (encoding == DW_EH_PE_aligned) {
+ if (__builtin_add_overflow(cur_offset_, sizeof(AddressType) - 1, &cur_offset_)) {
+ return false;
+ }
+ cur_offset_ &= -sizeof(AddressType);
+
+ if (sizeof(AddressType) != sizeof(uint64_t)) {
+ *value = 0;
+ }
+ return ReadBytes(value, sizeof(AddressType));
+ }
+
+ // Get the data.
+ switch (encoding & 0x0f) {
+ case DW_EH_PE_absptr:
+ if (sizeof(AddressType) != sizeof(uint64_t)) {
+ *value = 0;
+ }
+ if (!ReadBytes(value, sizeof(AddressType))) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_uleb128:
+ if (!ReadULEB128(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_sleb128:
+ int64_t signed_value;
+ if (!ReadSLEB128(&signed_value)) {
+ return false;
+ }
+ *value = static_cast<uint64_t>(signed_value);
+ break;
+ case DW_EH_PE_udata1: {
+ uint8_t value8;
+ if (!ReadBytes(&value8, 1)) {
+ return false;
+ }
+ *value = value8;
+ } break;
+ case DW_EH_PE_sdata1:
+ if (!ReadSigned<int8_t>(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_udata2: {
+ uint16_t value16;
+ if (!ReadBytes(&value16, 2)) {
+ return false;
+ }
+ *value = value16;
+ } break;
+ case DW_EH_PE_sdata2:
+ if (!ReadSigned<int16_t>(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_udata4: {
+ uint32_t value32;
+ if (!ReadBytes(&value32, 4)) {
+ return false;
+ }
+ *value = value32;
+ } break;
+ case DW_EH_PE_sdata4:
+ if (!ReadSigned<int32_t>(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_udata8:
+ if (!ReadBytes(value, sizeof(uint64_t))) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_sdata8:
+ if (!ReadSigned<int64_t>(value)) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return AdjustEncodedValue(encoding & 0x70, value);
+}
+
+// Instantiate all of the needed template functions.
+template bool DwarfMemory::ReadSigned<int8_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int16_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int32_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int64_t>(uint64_t*);
+
+template size_t DwarfMemory::GetEncodedSize<uint32_t>(uint8_t);
+template size_t DwarfMemory::GetEncodedSize<uint64_t>(uint8_t);
+
+template bool DwarfMemory::ReadEncodedValue<uint32_t>(uint8_t, uint64_t*);
+template bool DwarfMemory::ReadEncodedValue<uint64_t>(uint8_t, uint64_t*);
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
new file mode 100644
index 0000000..b3fd0df
--- /dev/null
+++ b/libunwindstack/DwarfOp.cpp
@@ -0,0 +1,467 @@
+/*
+ * 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 <deque>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "DwarfError.h"
+#include "DwarfOp.h"
+
+namespace unwindstack {
+
+template <typename AddressType>
+constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end, uint8_t dwarf_version) {
+ uint32_t iterations = 0;
+ is_register_ = false;
+ stack_.clear();
+ memory_->set_cur_offset(start);
+ while (memory_->cur_offset() < end) {
+ if (!Decode(dwarf_version)) {
+ return false;
+ }
+ // To protect against a branch that creates an infinite loop,
+ // terminate if the number of iterations gets too high.
+ if (iterations++ == 1000) {
+ last_error_ = DWARF_ERROR_TOO_MANY_ITERATIONS;
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::Decode(uint8_t dwarf_version) {
+ last_error_ = DWARF_ERROR_NONE;
+ if (!memory_->ReadBytes(&cur_op_, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ const auto* op = &kCallbackTable[cur_op_];
+ const auto handle_func = op->handle_func;
+ if (handle_func == nullptr) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Check for an unsupported opcode.
+ if (dwarf_version < op->supported_version) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Make sure that the required number of stack elements is available.
+ if (stack_.size() < op->num_required_stack_values) {
+ last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+ return false;
+ }
+
+ operands_.clear();
+ for (size_t i = 0; i < op->num_operands; i++) {
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ operands_.push_back(value);
+ }
+ return (this->*handle_func)();
+}
+
+template <typename AddressType>
+void DwarfOp<AddressType>::GetLogInfo(uint64_t start, uint64_t end,
+ std::vector<std::string>* lines) {
+ memory_->set_cur_offset(start);
+ while (memory_->cur_offset() < end) {
+ uint8_t cur_op;
+ if (!memory_->ReadBytes(&cur_op, 1)) {
+ return;
+ }
+
+ std::string raw_string(android::base::StringPrintf("Raw Data: 0x%02x", cur_op));
+ std::string log_string;
+ const auto* op = &kCallbackTable[cur_op];
+ if (op->handle_func == nullptr) {
+ log_string = "Illegal";
+ } else {
+ log_string = op->name;
+ uint64_t start_offset = memory_->cur_offset();
+ for (size_t i = 0; i < op->num_operands; i++) {
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
+ return;
+ }
+ log_string += ' ' + std::to_string(value);
+ }
+ uint64_t end_offset = memory_->cur_offset();
+
+ memory_->set_cur_offset(start_offset);
+ for (size_t i = start_offset; i < end_offset; i++) {
+ uint8_t byte;
+ if (!memory_->ReadBytes(&byte, 1)) {
+ return;
+ }
+ raw_string += android::base::StringPrintf(" 0x%02x", byte);
+ }
+ memory_->set_cur_offset(end_offset);
+ }
+ lines->push_back(std::move(log_string));
+ lines->push_back(std::move(raw_string));
+ }
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_deref() {
+ // Read the address and dereference it.
+ AddressType addr = StackPop();
+ AddressType value;
+ if (!regular_memory()->Read(addr, &value, sizeof(value))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ stack_.push_front(value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_deref_size() {
+ AddressType bytes_to_read = OperandAt(0);
+ if (bytes_to_read > sizeof(AddressType) || bytes_to_read == 0) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ // Read the address and dereference it.
+ AddressType addr = StackPop();
+ AddressType value = 0;
+ if (!regular_memory()->Read(addr, &value, bytes_to_read)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ stack_.push_front(value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_push() {
+ // Push all of the operands.
+ for (auto operand : operands_) {
+ stack_.push_front(operand);
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_dup() {
+ stack_.push_front(StackAt(0));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_drop() {
+ StackPop();
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_over() {
+ stack_.push_front(StackAt(1));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_pick() {
+ AddressType index = OperandAt(0);
+ if (index > StackSize()) {
+ last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+ return false;
+ }
+ stack_.push_front(StackAt(index));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_swap() {
+ AddressType old_value = stack_[0];
+ stack_[0] = stack_[1];
+ stack_[1] = old_value;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_rot() {
+ AddressType top = stack_[0];
+ stack_[0] = stack_[1];
+ stack_[1] = stack_[2];
+ stack_[2] = top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_abs() {
+ SignedType signed_value = static_cast<SignedType>(stack_[0]);
+ if (signed_value < 0) {
+ signed_value = -signed_value;
+ }
+ stack_[0] = static_cast<AddressType>(signed_value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_and() {
+ AddressType top = StackPop();
+ stack_[0] &= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_div() {
+ AddressType top = StackPop();
+ if (top == 0) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ SignedType signed_divisor = static_cast<SignedType>(top);
+ SignedType signed_dividend = static_cast<SignedType>(stack_[0]);
+ stack_[0] = static_cast<AddressType>(signed_dividend / signed_divisor);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_minus() {
+ AddressType top = StackPop();
+ stack_[0] -= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_mod() {
+ AddressType top = StackPop();
+ if (top == 0) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ stack_[0] %= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_mul() {
+ AddressType top = StackPop();
+ stack_[0] *= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_neg() {
+ SignedType signed_value = static_cast<SignedType>(stack_[0]);
+ stack_[0] = static_cast<AddressType>(-signed_value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_not() {
+ stack_[0] = ~stack_[0];
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_or() {
+ AddressType top = StackPop();
+ stack_[0] |= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_plus() {
+ AddressType top = StackPop();
+ stack_[0] += top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_plus_uconst() {
+ stack_[0] += OperandAt(0);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shl() {
+ AddressType top = StackPop();
+ stack_[0] <<= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shr() {
+ AddressType top = StackPop();
+ stack_[0] >>= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shra() {
+ AddressType top = StackPop();
+ SignedType signed_value = static_cast<SignedType>(stack_[0]) >> top;
+ stack_[0] = static_cast<AddressType>(signed_value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_xor() {
+ AddressType top = StackPop();
+ stack_[0] ^= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_bra() {
+ // Requires one stack element.
+ AddressType top = StackPop();
+ int16_t offset = static_cast<int16_t>(OperandAt(0));
+ uint64_t cur_offset;
+ if (top != 0) {
+ cur_offset = memory_->cur_offset() + offset;
+ } else {
+ cur_offset = memory_->cur_offset() - offset;
+ }
+ memory_->set_cur_offset(cur_offset);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_eq() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] == top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_ge() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] >= top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_gt() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] > top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_le() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] <= top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_lt() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] < top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_ne() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] != top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_skip() {
+ int16_t offset = static_cast<int16_t>(OperandAt(0));
+ uint64_t cur_offset = memory_->cur_offset() + offset;
+ memory_->set_cur_offset(cur_offset);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_lit() {
+ stack_.push_front(cur_op() - 0x30);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_reg() {
+ is_register_ = true;
+ stack_.push_front(cur_op() - 0x50);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_regx() {
+ is_register_ = true;
+ stack_.push_front(OperandAt(0));
+ return true;
+}
+
+// It's not clear for breg/bregx, if this op should read the current
+// value of the register, or where we think that register is located.
+// For simplicity, the code will read the value before doing the unwind.
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_breg() {
+ uint16_t reg = cur_op() - 0x70;
+ if (reg >= regs_->total_regs()) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ stack_.push_front((*regs_)[reg] + OperandAt(0));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_bregx() {
+ AddressType reg = OperandAt(0);
+ if (reg >= regs_->total_regs()) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ stack_.push_front((*regs_)[reg] + OperandAt(1));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_nop() {
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_not_implemented() {
+ last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
+ return false;
+}
+
+// Explicitly instantiate DwarfOp.
+template class DwarfOp<uint32_t>;
+template class DwarfOp<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
new file mode 100644
index 0000000..c29bf35
--- /dev/null
+++ b/libunwindstack/DwarfOp.h
@@ -0,0 +1,1641 @@
+/*
+ * Copyright (C) 2016 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_DWARF_OP_H
+#define _LIBUNWINDSTACK_DWARF_OP_H
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+namespace unwindstack {
+
+enum DwarfVersion : uint8_t {
+ DWARF_VERSION_2 = 2,
+ DWARF_VERSION_3 = 3,
+ DWARF_VERSION_4 = 4,
+ DWARF_VERSION_MAX = DWARF_VERSION_4,
+};
+
+// Forward declarations.
+class DwarfMemory;
+class Memory;
+template <typename AddressType>
+class RegsImpl;
+
+template <typename AddressType>
+class DwarfOp {
+ // Signed version of AddressType
+ typedef typename std::make_signed<AddressType>::type SignedType;
+
+ struct OpCallback {
+ const char* name;
+ bool (DwarfOp::*handle_func)();
+ uint8_t supported_version;
+ uint8_t num_required_stack_values;
+ uint8_t num_operands;
+ uint8_t operands[2];
+ };
+
+ public:
+ DwarfOp(DwarfMemory* memory, Memory* regular_memory)
+ : memory_(memory), regular_memory_(regular_memory) {}
+ virtual ~DwarfOp() = default;
+
+ bool Decode(uint8_t dwarf_version);
+
+ bool Eval(uint64_t start, uint64_t end, uint8_t dwarf_version);
+
+ void GetLogInfo(uint64_t start, uint64_t end, std::vector<std::string>* lines);
+
+ AddressType StackAt(size_t index) { return stack_[index]; }
+ size_t StackSize() { return stack_.size(); }
+
+ void set_regs(RegsImpl<AddressType>* regs) { regs_ = regs; }
+
+ DwarfError last_error() { return last_error_; }
+
+ bool is_register() { return is_register_; }
+
+ uint8_t cur_op() { return cur_op_; }
+
+ Memory* regular_memory() { return regular_memory_; }
+
+ protected:
+ AddressType OperandAt(size_t index) { return operands_[index]; }
+ size_t OperandsSize() { return operands_.size(); }
+
+ AddressType StackPop() {
+ AddressType value = stack_.front();
+ stack_.pop_front();
+ return value;
+ }
+
+ private:
+ DwarfMemory* memory_;
+ Memory* regular_memory_;
+
+ RegsImpl<AddressType>* regs_;
+ bool is_register_ = false;
+ DwarfError last_error_ = DWARF_ERROR_NONE;
+ uint8_t cur_op_;
+ std::vector<AddressType> operands_;
+ std::deque<AddressType> stack_;
+
+ inline AddressType bool_to_dwarf_bool(bool value) { return value ? 1 : 0; }
+
+ // Op processing functions.
+ bool op_deref();
+ bool op_deref_size();
+ bool op_push();
+ bool op_dup();
+ bool op_drop();
+ bool op_over();
+ bool op_pick();
+ bool op_swap();
+ bool op_rot();
+ bool op_abs();
+ bool op_and();
+ bool op_div();
+ bool op_minus();
+ bool op_mod();
+ bool op_mul();
+ bool op_neg();
+ bool op_not();
+ bool op_or();
+ bool op_plus();
+ bool op_plus_uconst();
+ bool op_shl();
+ bool op_shr();
+ bool op_shra();
+ bool op_xor();
+ bool op_bra();
+ bool op_eq();
+ bool op_ge();
+ bool op_gt();
+ bool op_le();
+ bool op_lt();
+ bool op_ne();
+ bool op_skip();
+ bool op_lit();
+ bool op_reg();
+ bool op_regx();
+ bool op_breg();
+ bool op_bregx();
+ bool op_nop();
+ bool op_not_implemented();
+
+ constexpr static OpCallback kCallbackTable[256] = {
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x00 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x01 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x02 illegal op
+ {
+ // 0x03 DW_OP_addr
+ "DW_OP_addr",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_absptr},
+ },
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x04 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x05 illegal op
+ {
+ // 0x06 DW_OP_deref
+ "DW_OP_deref",
+ &DwarfOp::op_deref,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x07 illegal op
+ {
+ // 0x08 DW_OP_const1u
+ "DW_OP_const1u",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x09 DW_OP_const1s
+ "DW_OP_const1s",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata1},
+ },
+ {
+ // 0x0a DW_OP_const2u
+ "DW_OP_const2u",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata2},
+ },
+ {
+ // 0x0b DW_OP_const2s
+ "DW_OP_const2s",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x0c DW_OP_const4u
+ "DW_OP_const4u",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata4},
+ },
+ {
+ // 0x0d DW_OP_const4s
+ "DW_OP_const4s",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata4},
+ },
+ {
+ // 0x0e DW_OP_const8u
+ "DW_OP_const8u",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata8},
+ },
+ {
+ // 0x0f DW_OP_const8s
+ "DW_OP_const8s",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata8},
+ },
+ {
+ // 0x10 DW_OP_constu
+ "DW_OP_constu",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x11 DW_OP_consts
+ "DW_OP_consts",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x12 DW_OP_dup
+ "DW_OP_dup",
+ &DwarfOp::op_dup,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x13 DW_OP_drop
+ "DW_OP_drop",
+ &DwarfOp::op_drop,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x14 DW_OP_over
+ "DW_OP_over",
+ &DwarfOp::op_over,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x15 DW_OP_pick
+ "DW_OP_pick",
+ &DwarfOp::op_pick,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x16 DW_OP_swap
+ "DW_OP_swap",
+ &DwarfOp::op_swap,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x17 DW_OP_rot
+ "DW_OP_rot",
+ &DwarfOp::op_rot,
+ DWARF_VERSION_2,
+ 3,
+ 0,
+ {},
+ },
+ {
+ // 0x18 DW_OP_xderef
+ "DW_OP_xderef",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x19 DW_OP_abs
+ "DW_OP_abs",
+ &DwarfOp::op_abs,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x1a DW_OP_and
+ "DW_OP_and",
+ &DwarfOp::op_and,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1b DW_OP_div
+ "DW_OP_div",
+ &DwarfOp::op_div,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1c DW_OP_minus
+ "DW_OP_minus",
+ &DwarfOp::op_minus,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1d DW_OP_mod
+ "DW_OP_mod",
+ &DwarfOp::op_mod,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1e DW_OP_mul
+ "DW_OP_mul",
+ &DwarfOp::op_mul,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1f DW_OP_neg
+ "DW_OP_neg",
+ &DwarfOp::op_neg,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x20 DW_OP_not
+ "DW_OP_not",
+ &DwarfOp::op_not,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x21 DW_OP_or
+ "DW_OP_or",
+ &DwarfOp::op_or,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x22 DW_OP_plus
+ "DW_OP_plus",
+ &DwarfOp::op_plus,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x23 DW_OP_plus_uconst
+ "DW_OP_plus_uconst",
+ &DwarfOp::op_plus_uconst,
+ DWARF_VERSION_2,
+ 1,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x24 DW_OP_shl
+ "DW_OP_shl",
+ &DwarfOp::op_shl,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x25 DW_OP_shr
+ "DW_OP_shr",
+ &DwarfOp::op_shr,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x26 DW_OP_shra
+ "DW_OP_shra",
+ &DwarfOp::op_shra,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x27 DW_OP_xor
+ "DW_OP_xor",
+ &DwarfOp::op_xor,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x28 DW_OP_bra
+ "DW_OP_bra",
+ &DwarfOp::op_bra,
+ DWARF_VERSION_2,
+ 1,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x29 DW_OP_eq
+ "DW_OP_eq",
+ &DwarfOp::op_eq,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2a DW_OP_ge
+ "DW_OP_ge",
+ &DwarfOp::op_ge,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2b DW_OP_gt
+ "DW_OP_gt",
+ &DwarfOp::op_gt,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2c DW_OP_le
+ "DW_OP_le",
+ &DwarfOp::op_le,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2d DW_OP_lt
+ "DW_OP_lt",
+ &DwarfOp::op_lt,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2e DW_OP_ne
+ "DW_OP_ne",
+ &DwarfOp::op_ne,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2f DW_OP_skip
+ "DW_OP_skip",
+ &DwarfOp::op_skip,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x30 DW_OP_lit0
+ "DW_OP_lit0",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x31 DW_OP_lit1
+ "DW_OP_lit1",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x32 DW_OP_lit2
+ "DW_OP_lit2",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x33 DW_OP_lit3
+ "DW_OP_lit3",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x34 DW_OP_lit4
+ "DW_OP_lit4",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x35 DW_OP_lit5
+ "DW_OP_lit5",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x36 DW_OP_lit6
+ "DW_OP_lit6",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x37 DW_OP_lit7
+ "DW_OP_lit7",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x38 DW_OP_lit8
+ "DW_OP_lit8",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x39 DW_OP_lit9
+ "DW_OP_lit9",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3a DW_OP_lit10
+ "DW_OP_lit10",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3b DW_OP_lit11
+ "DW_OP_lit11",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3c DW_OP_lit12
+ "DW_OP_lit12",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3d DW_OP_lit13
+ "DW_OP_lit13",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3e DW_OP_lit14
+ "DW_OP_lit14",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3f DW_OP_lit15
+ "DW_OP_lit15",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x40 DW_OP_lit16
+ "DW_OP_lit16",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x41 DW_OP_lit17
+ "DW_OP_lit17",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x42 DW_OP_lit18
+ "DW_OP_lit18",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x43 DW_OP_lit19
+ "DW_OP_lit19",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x44 DW_OP_lit20
+ "DW_OP_lit20",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x45 DW_OP_lit21
+ "DW_OP_lit21",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x46 DW_OP_lit22
+ "DW_OP_lit22",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x47 DW_OP_lit23
+ "DW_OP_lit23",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x48 DW_OP_lit24
+ "DW_OP_lit24",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x49 DW_OP_lit25
+ "DW_OP_lit25",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4a DW_OP_lit26
+ "DW_OP_lit26",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4b DW_OP_lit27
+ "DW_OP_lit27",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4c DW_OP_lit28
+ "DW_OP_lit28",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4d DW_OP_lit29
+ "DW_OP_lit29",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4e DW_OP_lit30
+ "DW_OP_lit30",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4f DW_OP_lit31
+ "DW_OP_lit31",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x50 DW_OP_reg0
+ "DW_OP_reg0",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x51 DW_OP_reg1
+ "DW_OP_reg1",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x52 DW_OP_reg2
+ "DW_OP_reg2",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x53 DW_OP_reg3
+ "DW_OP_reg3",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x54 DW_OP_reg4
+ "DW_OP_reg4",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x55 DW_OP_reg5
+ "DW_OP_reg5",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x56 DW_OP_reg6
+ "DW_OP_reg6",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x57 DW_OP_reg7
+ "DW_OP_reg7",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x58 DW_OP_reg8
+ "DW_OP_reg8",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x59 DW_OP_reg9
+ "DW_OP_reg9",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5a DW_OP_reg10
+ "DW_OP_reg10",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5b DW_OP_reg11
+ "DW_OP_reg11",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5c DW_OP_reg12
+ "DW_OP_reg12",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5d DW_OP_reg13
+ "DW_OP_reg13",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5e DW_OP_reg14
+ "DW_OP_reg14",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5f DW_OP_reg15
+ "DW_OP_reg15",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x60 DW_OP_reg16
+ "DW_OP_reg16",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x61 DW_OP_reg17
+ "DW_OP_reg17",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x62 DW_OP_reg18
+ "DW_OP_reg18",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x63 DW_OP_reg19
+ "DW_OP_reg19",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x64 DW_OP_reg20
+ "DW_OP_reg20",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x65 DW_OP_reg21
+ "DW_OP_reg21",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x66 DW_OP_reg22
+ "DW_OP_reg22",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x67 DW_OP_reg23
+ "DW_OP_reg23",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x68 DW_OP_reg24
+ "DW_OP_reg24",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x69 DW_OP_reg25
+ "DW_OP_reg25",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6a DW_OP_reg26
+ "DW_OP_reg26",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6b DW_OP_reg27
+ "DW_OP_reg27",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6c DW_OP_reg28
+ "DW_OP_reg28",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6d DW_OP_reg29
+ "DW_OP_reg29",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6e DW_OP_reg30
+ "DW_OP_reg30",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6f DW_OP_reg31
+ "DW_OP_reg31",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x70 DW_OP_breg0
+ "DW_OP_breg0",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x71 DW_OP_breg1
+ "DW_OP_breg1",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x72 DW_OP_breg2
+ "DW_OP_breg2",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x73 DW_OP_breg3
+ "DW_OP_breg3",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x74 DW_OP_breg4
+ "DW_OP_breg4",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x75 DW_OP_breg5
+ "DW_OP_breg5",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x76 DW_OP_breg6
+ "DW_OP_breg6",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x77 DW_OP_breg7
+ "DW_OP_breg7",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x78 DW_OP_breg8
+ "DW_OP_breg8",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x79 DW_OP_breg9
+ "DW_OP_breg9",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7a DW_OP_breg10
+ "DW_OP_breg10",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7b DW_OP_breg11
+ "DW_OP_breg11",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7c DW_OP_breg12
+ "DW_OP_breg12",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7d DW_OP_breg13
+ "DW_OP_breg13",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7e DW_OP_breg14
+ "DW_OP_breg14",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7f DW_OP_breg15
+ "DW_OP_breg15",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x80 DW_OP_breg16
+ "DW_OP_breg16",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x81 DW_OP_breg17
+ "DW_OP_breg17",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x82 DW_OP_breg18
+ "DW_OP_breg18",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x83 DW_OP_breg19
+ "DW_OP_breg19",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x84 DW_OP_breg20
+ "DW_OP_breg20",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x85 DW_OP_breg21
+ "DW_OP_breg21",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x86 DW_OP_breg22
+ "DW_OP_breg22",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x87 DW_OP_breg23
+ "DW_OP_breg23",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x88 DW_OP_breg24
+ "DW_OP_breg24",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x89 DW_OP_breg25
+ "DW_OP_breg25",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8a DW_OP_breg26
+ "DW_OP_breg26",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8b DW_OP_breg27
+ "DW_OP_breg27",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8c DW_OP_breg28
+ "DW_OP_breg28",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8d DW_OP_breg29
+ "DW_OP_breg29",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8e DW_OP_breg30
+ "DW_OP_breg30",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8f DW_OP_breg31
+ "DW_OP_breg31",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x90 DW_OP_regx
+ "DW_OP_regx",
+ &DwarfOp::op_regx,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x91 DW_OP_fbreg
+ "DW_OP_fbreg",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x92 DW_OP_bregx
+ "DW_OP_bregx",
+ &DwarfOp::op_bregx,
+ DWARF_VERSION_2,
+ 0,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ },
+ {
+ // 0x93 DW_OP_piece
+ "DW_OP_piece",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x94 DW_OP_deref_size
+ "DW_OP_deref_size",
+ &DwarfOp::op_deref_size,
+ DWARF_VERSION_2,
+ 1,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x95 DW_OP_xderef_size
+ "DW_OP_xderef_size",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x96 DW_OP_nop
+ "DW_OP_nop",
+ &DwarfOp::op_nop,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x97 DW_OP_push_object_address
+ "DW_OP_push_object_address",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x98 DW_OP_call2
+ "DW_OP_call2",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 1,
+ {DW_EH_PE_udata2},
+ },
+ {
+ // 0x99 DW_OP_call4
+ "DW_OP_call4",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 1,
+ {DW_EH_PE_udata4},
+ },
+ {
+ // 0x9a DW_OP_call_ref
+ "DW_OP_call_ref",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 0, // Has a different sized operand (4 bytes or 8 bytes).
+ {},
+ },
+ {
+ // 0x9b DW_OP_form_tls_address
+ "DW_OP_form_tls_address",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x9c DW_OP_call_frame_cfa
+ "DW_OP_call_frame_cfa",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x9d DW_OP_bit_piece
+ "DW_OP_bit_piece",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ },
+ {
+ // 0x9e DW_OP_implicit_value
+ "DW_OP_implicit_value",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_4,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x9f DW_OP_stack_value
+ "DW_OP_stack_value",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_4,
+ 1,
+ 0,
+ {},
+ },
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xaa illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xab illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xac illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xad illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xae illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xaf illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xba illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbc illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbd illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbe illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbf illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xca illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xcb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xcc illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xcd illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xce illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xcf illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xda illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xdb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xdc illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xdd illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xde illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xdf illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe0 DW_OP_lo_user
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xea illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xeb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xec illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xed illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xee illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xef illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfa illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfc illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfd illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfe illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xff DW_OP_hi_user
+ };
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
new file mode 100644
index 0000000..1234eb1
--- /dev/null
+++ b/libunwindstack/DwarfSection.cpp
@@ -0,0 +1,555 @@
+/*
+ * 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 <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "DwarfCfa.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+#include "DwarfOp.h"
+
+namespace unwindstack {
+
+DwarfSection::DwarfSection(Memory* memory) : memory_(memory), last_error_(DWARF_ERROR_NONE) {}
+
+const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
+ uint64_t fde_offset;
+ if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
+ return nullptr;
+ }
+ const DwarfFde* fde = GetFdeFromOffset(fde_offset);
+ // Guaranteed pc >= pc_start, need to check pc in the fde range.
+ if (pc < fde->pc_end) {
+ return fde;
+ }
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return nullptr;
+}
+
+bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+ last_error_ = DWARF_ERROR_NONE;
+ const DwarfFde* fde = GetFdeFromPc(pc);
+ if (fde == nullptr || fde->cie == nullptr) {
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+
+ // Now get the location information for this pc.
+ dwarf_loc_regs_t loc_regs;
+ if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
+ return false;
+ }
+
+ // Now eval the actual registers.
+ return Eval(fde->cie, process_memory, loc_regs, regs);
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uint8_t version,
+ Memory* regular_memory, AddressType* value) {
+ DwarfOp<AddressType> op(&memory_, regular_memory);
+
+ // Need to evaluate the op data.
+ uint64_t start = loc.values[1];
+ uint64_t end = start + loc.values[0];
+ if (!op.Eval(start, end, version)) {
+ last_error_ = op.last_error();
+ return false;
+ }
+ if (op.StackSize() == 0) {
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ // We don't support an expression that evaluates to a register number.
+ if (op.is_register()) {
+ last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
+ return false;
+ }
+ *value = op.StackAt(0);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
+ const dwarf_loc_regs_t& loc_regs, Regs* regs) {
+ RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
+ if (cie->return_address_register >= cur_regs->total_regs()) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Get the cfa value;
+ auto cfa_entry = loc_regs.find(CFA_REG);
+ if (cfa_entry == loc_regs.end()) {
+ last_error_ = DWARF_ERROR_CFA_NOT_DEFINED;
+ return false;
+ }
+
+ AddressType prev_pc = regs->pc();
+ AddressType prev_cfa = regs->sp();
+
+ AddressType cfa;
+ const DwarfLocation* loc = &cfa_entry->second;
+ // Only a few location types are valid for the cfa.
+ switch (loc->type) {
+ case DWARF_LOCATION_REGISTER:
+ if (loc->values[0] >= cur_regs->total_regs()) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ // If the stack pointer register is the CFA, and the stack
+ // pointer register does not have any associated location
+ // information, use the current cfa value.
+ if (regs->sp_reg() == loc->values[0] && loc_regs.count(regs->sp_reg()) == 0) {
+ cfa = prev_cfa;
+ } else {
+ cfa = (*cur_regs)[loc->values[0]];
+ }
+ cfa += loc->values[1];
+ break;
+ case DWARF_LOCATION_EXPRESSION:
+ case DWARF_LOCATION_VAL_EXPRESSION: {
+ AddressType value;
+ if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
+ return false;
+ }
+ if (loc->type == DWARF_LOCATION_EXPRESSION) {
+ if (!regular_memory->Read(value, &cfa, sizeof(AddressType))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } else {
+ cfa = value;
+ }
+ break;
+ }
+ default:
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // This code is not guaranteed to work in cases where a register location
+ // is a double indirection to the actual value. For example, if r3 is set
+ // to r5 + 4, and r5 is set to CFA + 4, then this won't necessarily work
+ // because it does not guarantee that r5 is evaluated before r3.
+ // Check that this case does not exist, and error if it does.
+ bool return_address_undefined = false;
+ for (const auto& entry : loc_regs) {
+ uint16_t reg = entry.first;
+ // Already handled the CFA register.
+ if (reg == CFA_REG) continue;
+
+ if (reg >= cur_regs->total_regs()) {
+ // Skip this unknown register.
+ continue;
+ }
+
+ const DwarfLocation* loc = &entry.second;
+ switch (loc->type) {
+ case DWARF_LOCATION_OFFSET:
+ if (!regular_memory->Read(cfa + loc->values[0], &(*cur_regs)[reg], sizeof(AddressType))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ break;
+ case DWARF_LOCATION_VAL_OFFSET:
+ (*cur_regs)[reg] = cfa + loc->values[0];
+ break;
+ case DWARF_LOCATION_REGISTER: {
+ uint16_t cur_reg = loc->values[0];
+ if (cur_reg >= cur_regs->total_regs()) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ if (loc_regs.find(cur_reg) != loc_regs.end()) {
+ // This is a double indirection, a register definition references
+ // another register which is also defined as something other
+ // than a register.
+ log(0,
+ "Invalid indirection: register %d references register %d which is "
+ "not a plain register.\n",
+ reg, cur_reg);
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ (*cur_regs)[reg] = (*cur_regs)[cur_reg] + loc->values[1];
+ break;
+ }
+ case DWARF_LOCATION_EXPRESSION:
+ case DWARF_LOCATION_VAL_EXPRESSION: {
+ AddressType value;
+ if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
+ return false;
+ }
+ if (loc->type == DWARF_LOCATION_EXPRESSION) {
+ if (!regular_memory->Read(value, &(*cur_regs)[reg], sizeof(AddressType))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } else {
+ (*cur_regs)[reg] = value;
+ }
+ break;
+ }
+ case DWARF_LOCATION_UNDEFINED:
+ if (reg == cie->return_address_register) {
+ return_address_undefined = true;
+ }
+ default:
+ break;
+ }
+ }
+
+ // Find the return address location.
+ if (return_address_undefined) {
+ cur_regs->set_pc(0);
+ } else {
+ cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
+ }
+ cur_regs->set_sp(cfa);
+ // Stop if the cfa and pc are the same.
+ return prev_cfa != cfa || prev_pc != cur_regs->pc();
+}
+
+template <typename AddressType>
+const DwarfCie* DwarfSectionImpl<AddressType>::GetCie(uint64_t offset) {
+ auto cie_entry = cie_entries_.find(offset);
+ if (cie_entry != cie_entries_.end()) {
+ return &cie_entry->second;
+ }
+ DwarfCie* cie = &cie_entries_[offset];
+ memory_.set_cur_offset(offset);
+ if (!FillInCie(cie)) {
+ // Erase the cached entry.
+ cie_entries_.erase(offset);
+ return nullptr;
+ }
+ return cie;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
+ uint32_t length32;
+ if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ // Set the default for the lsda encoding.
+ cie->lsda_encoding = DW_EH_PE_omit;
+
+ if (length32 == static_cast<uint32_t>(-1)) {
+ // 64 bit Cie
+ uint64_t length64;
+ if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ cie->cfa_instructions_end = memory_.cur_offset() + length64;
+ cie->fde_address_encoding = DW_EH_PE_sdata8;
+
+ uint64_t cie_id;
+ if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (!IsCie64(cie_id)) {
+ // This is not a Cie, something has gone horribly wrong.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ } else {
+ // 32 bit Cie
+ cie->cfa_instructions_end = memory_.cur_offset() + length32;
+ cie->fde_address_encoding = DW_EH_PE_sdata4;
+
+ uint32_t cie_id;
+ if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (!IsCie32(cie_id)) {
+ // This is not a Cie, something has gone horribly wrong.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ }
+
+ if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
+ // Unrecognized version.
+ last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+ return false;
+ }
+
+ // Read the augmentation string.
+ char aug_value;
+ do {
+ if (!memory_.ReadBytes(&aug_value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ cie->augmentation_string.push_back(aug_value);
+ } while (aug_value != '\0');
+
+ if (cie->version == 4) {
+ // Skip the Address Size field since we only use it for validation.
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+
+ // Segment Size
+ if (!memory_.ReadBytes(&cie->segment_size, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ }
+
+ // Code Alignment Factor
+ if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ // Data Alignment Factor
+ if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (cie->version == 1) {
+ // Return Address is a single byte.
+ uint8_t return_address_register;
+ if (!memory_.ReadBytes(&return_address_register, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ cie->return_address_register = return_address_register;
+ } else if (!memory_.ReadULEB128(&cie->return_address_register)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (cie->augmentation_string[0] != 'z') {
+ cie->cfa_instructions_offset = memory_.cur_offset();
+ return true;
+ }
+
+ uint64_t aug_length;
+ if (!memory_.ReadULEB128(&aug_length)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
+
+ for (size_t i = 1; i < cie->augmentation_string.size(); i++) {
+ switch (cie->augmentation_string[i]) {
+ case 'L':
+ if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ break;
+ case 'P': {
+ uint8_t encoding;
+ if (!memory_.ReadBytes(&encoding, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } break;
+ case 'R':
+ if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromOffset(uint64_t offset) {
+ auto fde_entry = fde_entries_.find(offset);
+ if (fde_entry != fde_entries_.end()) {
+ return &fde_entry->second;
+ }
+ DwarfFde* fde = &fde_entries_[offset];
+ memory_.set_cur_offset(offset);
+ if (!FillInFde(fde)) {
+ fde_entries_.erase(offset);
+ return nullptr;
+ }
+ return fde;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
+ uint32_t length32;
+ if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (length32 == static_cast<uint32_t>(-1)) {
+ // 64 bit Fde.
+ uint64_t length64;
+ if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ fde->cfa_instructions_end = memory_.cur_offset() + length64;
+
+ uint64_t value64;
+ if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (IsCie64(value64)) {
+ // This is a Cie, this means something has gone wrong.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Get the Cie pointer, which is necessary to properly read the rest of
+ // of the Fde information.
+ fde->cie_offset = GetCieOffsetFromFde64(value64);
+ } else {
+ // 32 bit Fde.
+ fde->cfa_instructions_end = memory_.cur_offset() + length32;
+
+ uint32_t value32;
+ if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (IsCie32(value32)) {
+ // This is a Cie, this means something has gone wrong.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Get the Cie pointer, which is necessary to properly read the rest of
+ // of the Fde information.
+ fde->cie_offset = GetCieOffsetFromFde32(value32);
+ }
+ uint64_t cur_offset = memory_.cur_offset();
+
+ const DwarfCie* cie = GetCie(fde->cie_offset);
+ if (cie == nullptr) {
+ return false;
+ }
+ fde->cie = cie;
+
+ if (cie->segment_size != 0) {
+ // Skip over the segment selector for now.
+ cur_offset += cie->segment_size;
+ }
+ memory_.set_cur_offset(cur_offset);
+
+ if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_start)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ fde->pc_start = AdjustPcFromFde(fde->pc_start);
+
+ if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_end)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ fde->pc_end += fde->pc_start;
+ if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
+ // Augmentation Size
+ uint64_t aug_length;
+ if (!memory_.ReadULEB128(&aug_length)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ uint64_t cur_offset = memory_.cur_offset();
+
+ if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ // Set our position to after all of the augmentation data.
+ memory_.set_cur_offset(cur_offset + aug_length);
+ }
+ fde->cfa_instructions_offset = memory_.cur_offset();
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
+ dwarf_loc_regs_t* loc_regs) {
+ DwarfCfa<AddressType> cfa(&memory_, fde);
+
+ // Look for the cached copy of the cie data.
+ auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
+ if (reg_entry == cie_loc_regs_.end()) {
+ if (!cfa.GetLocationInfo(pc, fde->cie->cfa_instructions_offset, fde->cie->cfa_instructions_end,
+ loc_regs)) {
+ last_error_ = cfa.last_error();
+ return false;
+ }
+ cie_loc_regs_[fde->cie_offset] = *loc_regs;
+ }
+ cfa.set_cie_loc_regs(&cie_loc_regs_[fde->cie_offset]);
+ if (!cfa.GetLocationInfo(pc, fde->cfa_instructions_offset, fde->cfa_instructions_end, loc_regs)) {
+ last_error_ = cfa.last_error();
+ return false;
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, uint64_t load_bias,
+ const DwarfFde* fde) {
+ DwarfCfa<AddressType> cfa(&memory_, fde);
+
+ // Always print the cie information.
+ const DwarfCie* cie = fde->cie;
+ if (!cfa.Log(indent, pc, load_bias, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
+ last_error_ = cfa.last_error();
+ return false;
+ }
+ if (!cfa.Log(indent, pc, load_bias, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
+ last_error_ = cfa.last_error();
+ return false;
+ }
+ return true;
+}
+
+// Explicitly instantiate DwarfSectionImpl
+template class DwarfSectionImpl<uint32_t>;
+template class DwarfSectionImpl<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 272b5f0..4f7476d 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -23,12 +23,17 @@
#define LOG_TAG "unwind"
#include <log/log.h>
-#include "Elf.h"
-#include "ElfInterface.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
#include "ElfInterfaceArm.h"
#include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+#include "Symbols.h"
+
+namespace unwindstack {
bool Elf::Init() {
if (!memory_) {
@@ -49,6 +54,59 @@
return valid_;
}
+// It is expensive to initialize the .gnu_debugdata section. Provide a method
+// to initialize this data separately.
+void Elf::InitGnuDebugdata() {
+ if (!valid_ || interface_->gnu_debugdata_offset() == 0) {
+ return;
+ }
+
+ gnu_debugdata_memory_.reset(interface_->CreateGnuDebugdataMemory());
+ gnu_debugdata_interface_.reset(CreateInterfaceFromMemory(gnu_debugdata_memory_.get()));
+ ElfInterface* gnu = gnu_debugdata_interface_.get();
+ if (gnu == nullptr) {
+ return;
+ }
+ if (gnu->Init()) {
+ gnu->InitHeaders();
+ } else {
+ // Free all of the memory associated with the gnu_debugdata section.
+ gnu_debugdata_memory_.reset(nullptr);
+ gnu_debugdata_interface_.reset(nullptr);
+ }
+}
+
+bool Elf::GetSoname(std::string* name) {
+ return valid_ && interface_->GetSoname(name);
+}
+
+uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {
+ uint64_t load_bias = 0;
+ if (valid()) {
+ load_bias = interface_->load_bias();
+ }
+
+ return pc - map_info->start + load_bias + map_info->elf_offset;
+}
+
+bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
+ return valid_ && (interface_->GetFunctionName(addr, name, func_offset) ||
+ (gnu_debugdata_interface_ &&
+ gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
+}
+
+bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
+ return valid_ && (regs->StepIfSignalHandler(rel_pc, this, process_memory) ||
+ interface_->Step(rel_pc, regs, process_memory) ||
+ (gnu_debugdata_interface_ &&
+ gnu_debugdata_interface_->Step(rel_pc, regs, process_memory)));
+}
+
+uint64_t Elf::GetLoadBias() {
+ if (!valid_) return 0;
+ return interface_->load_bias();
+}
+
bool Elf::IsValidElf(Memory* memory) {
if (memory == nullptr) {
return false;
@@ -66,6 +124,28 @@
return true;
}
+void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) {
+ if (!IsValidElf(memory)) {
+ *valid = false;
+ return;
+ }
+ *size = 0;
+ *valid = true;
+
+ // Now read the section header information.
+ uint8_t class_type;
+ if (!memory->Read(EI_CLASS, &class_type, 1)) {
+ return;
+ }
+ if (class_type == ELFCLASS32) {
+ ElfInterface32::GetMaxSize(memory, size);
+ } else if (class_type == ELFCLASS64) {
+ ElfInterface64::GetMaxSize(memory, size);
+ } else {
+ *valid = false;
+ }
+}
+
ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
if (!IsValidElf(memory)) {
return nullptr;
@@ -90,24 +170,27 @@
machine_type_ = e_machine;
if (e_machine == EM_ARM) {
interface.reset(new ElfInterfaceArm(memory));
- } else {
+ } else if (e_machine == EM_386) {
interface.reset(new ElfInterface32(memory));
+ } else {
+ ALOGI("32 bit elf that is neither arm nor x86: e_machine = %d\n", e_machine);
+ return nullptr;
}
} else if (class_type_ == ELFCLASS64) {
Elf64_Half e_machine;
if (!memory->Read(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) {
return nullptr;
}
-
if (e_machine != EM_AARCH64 && e_machine != EM_X86_64) {
// Unsupported.
ALOGI("64 bit elf that is neither aarch64 nor x86_64: e_machine = %d\n", e_machine);
return nullptr;
}
-
machine_type_ = e_machine;
interface.reset(new ElfInterface64(memory));
}
return interface.release();
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index d59e9d8..be4f88a 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -20,9 +20,102 @@
#include <memory>
#include <string>
-#include "ElfInterface.h"
-#include "Memory.h"
-#include "Regs.h"
+#include <7zCrc.h>
+#include <Xz.h>
+#include <XzCrc64.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEhFrame.h"
+#include "Symbols.h"
+
+namespace unwindstack {
+
+ElfInterface::~ElfInterface() {
+ for (auto symbol : symbols_) {
+ delete symbol;
+ }
+}
+
+Memory* ElfInterface::CreateGnuDebugdataMemory() {
+ if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
+ return nullptr;
+ }
+
+ // TODO: Only call these initialization functions once.
+ CrcGenerateTable();
+ Crc64GenerateTable();
+
+ std::vector<uint8_t> src(gnu_debugdata_size_);
+ if (!memory_->Read(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) {
+ gnu_debugdata_offset_ = 0;
+ gnu_debugdata_size_ = static_cast<uint64_t>(-1);
+ return nullptr;
+ }
+
+ ISzAlloc alloc;
+ CXzUnpacker state;
+ alloc.Alloc = [](void*, size_t size) { return malloc(size); };
+ alloc.Free = [](void*, void* ptr) { return free(ptr); };
+
+ XzUnpacker_Construct(&state, &alloc);
+
+ std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer);
+ int return_val;
+ size_t src_offset = 0;
+ size_t dst_offset = 0;
+ ECoderStatus status;
+ dst->Resize(5 * gnu_debugdata_size_);
+ do {
+ size_t src_remaining = src.size() - src_offset;
+ size_t dst_remaining = dst->Size() - dst_offset;
+ if (dst_remaining < 2 * gnu_debugdata_size_) {
+ dst->Resize(dst->Size() + 2 * gnu_debugdata_size_);
+ dst_remaining += 2 * gnu_debugdata_size_;
+ }
+ return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset],
+ &src_remaining, CODER_FINISH_ANY, &status);
+ src_offset += src_remaining;
+ dst_offset += dst_remaining;
+ } while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED);
+ XzUnpacker_Free(&state);
+ if (return_val != SZ_OK || !XzUnpacker_IsStreamWasFinished(&state)) {
+ gnu_debugdata_offset_ = 0;
+ gnu_debugdata_size_ = static_cast<uint64_t>(-1);
+ return nullptr;
+ }
+
+ // Shrink back down to the exact size.
+ dst->Resize(dst_offset);
+
+ return dst.release();
+}
+
+template <typename AddressType>
+void ElfInterface::InitHeadersWithTemplate() {
+ if (eh_frame_offset_ != 0) {
+ eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
+ if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) {
+ eh_frame_.reset(nullptr);
+ eh_frame_offset_ = 0;
+ eh_frame_size_ = static_cast<uint64_t>(-1);
+ }
+ }
+
+ if (debug_frame_offset_ != 0) {
+ debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
+ if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_)) {
+ debug_frame_.reset(nullptr);
+ debug_frame_offset_ = 0;
+ debug_frame_size_ = static_cast<uint64_t>(-1);
+ }
+ }
+}
template <typename EhdrType, typename PhdrType, typename ShdrType>
bool ElfInterface::ReadAllHeaders() {
@@ -34,7 +127,13 @@
if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr)) {
return false;
}
- return ReadSectionHeaders<EhdrType, ShdrType>(ehdr);
+
+ // We could still potentially unwind without the section header
+ // information, so ignore any errors.
+ if (!ReadSectionHeaders<EhdrType, ShdrType>(ehdr)) {
+ log(0, "Malformed section header found, ignoring...");
+ }
+ return true;
}
template <typename EhdrType, typename PhdrType>
@@ -42,7 +141,7 @@
uint64_t offset = ehdr.e_phoff;
for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
PhdrType phdr;
- if (!memory_->Read(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
return false;
}
@@ -54,20 +153,20 @@
case PT_LOAD:
{
// Get the flags first, if this isn't an executable header, ignore it.
- if (!memory_->Read(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
return false;
}
if ((phdr.p_flags & PF_X) == 0) {
continue;
}
- if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
return false;
}
- if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
return false;
}
- if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
return false;
}
pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
@@ -79,22 +178,22 @@
}
case PT_GNU_EH_FRAME:
- if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
return false;
}
eh_frame_offset_ = phdr.p_offset;
- if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
return false;
}
eh_frame_size_ = phdr.p_memsz;
break;
case PT_DYNAMIC:
- if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
return false;
}
dynamic_offset_ = phdr.p_offset;
- if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
return false;
}
dynamic_size_ = phdr.p_memsz;
@@ -116,39 +215,68 @@
ShdrType shdr;
if (ehdr.e_shstrndx < ehdr.e_shnum) {
uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
- if (memory_->Read(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
- && memory_->Read(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+ if (memory_->ReadField(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+ memory_->ReadField(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
sec_offset = shdr.sh_offset;
sec_size = shdr.sh_size;
}
}
// Skip the first header, it's always going to be NULL.
+ offset += ehdr.e_shentsize;
for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
- if (!memory_->Read(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
+ if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
return false;
}
- if (shdr.sh_type == SHT_PROGBITS) {
+ if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
+ if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
+ return false;
+ }
+ // Need to go get the information about the section that contains
+ // the string terminated names.
+ ShdrType str_shdr;
+ if (shdr.sh_link >= ehdr.e_shnum) {
+ return false;
+ }
+ uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
+ if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_type, sizeof(str_shdr.sh_type))) {
+ return false;
+ }
+ if (str_shdr.sh_type != SHT_STRTAB) {
+ return false;
+ }
+ if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_offset,
+ sizeof(str_shdr.sh_offset))) {
+ return false;
+ }
+ if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_size, sizeof(str_shdr.sh_size))) {
+ return false;
+ }
+ symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
+ str_shdr.sh_offset, str_shdr.sh_size));
+ } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) {
// Look for the .debug_frame and .gnu_debugdata.
- if (!memory_->Read(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
+ if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
return false;
}
if (shdr.sh_name < sec_size) {
std::string name;
if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
+ uint64_t* offset_ptr = nullptr;
+ uint64_t* size_ptr = nullptr;
if (name == ".debug_frame") {
- if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
- && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
- debug_frame_offset_ = shdr.sh_offset;
- debug_frame_size_ = shdr.sh_size;
- }
+ offset_ptr = &debug_frame_offset_;
+ size_ptr = &debug_frame_size_;
} else if (name == ".gnu_debugdata") {
- if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
- && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
- gnu_debugdata_offset_ = shdr.sh_offset;
- gnu_debugdata_size_ = shdr.sh_size;
- }
+ offset_ptr = &gnu_debugdata_offset_;
+ size_ptr = &gnu_debugdata_size_;
+ }
+ if (offset_ptr != nullptr &&
+ memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+ memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+ *offset_ptr = shdr.sh_offset;
+ *size_ptr = shdr.sh_size;
}
}
}
@@ -205,11 +333,63 @@
return true;
}
-bool ElfInterface::Step(uint64_t, Regs*, Memory*) {
+template <typename SymType>
+bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
+ uint64_t* func_offset) {
+ if (symbols_.empty()) {
+ return false;
+ }
+
+ for (const auto symbol : symbols_) {
+ if (symbol->GetName<SymType>(addr, load_bias_, memory_, name, func_offset)) {
+ return true;
+ }
+ }
return false;
}
+bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+ // Need to subtract off the load_bias to get the correct pc.
+ if (pc < load_bias_) {
+ return false;
+ }
+ 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)) {
+ return true;
+ }
+
+ // Try the debug_frame next.
+ DwarfSection* debug_frame = debug_frame_.get();
+ if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory)) {
+ return true;
+ }
+
+ return false;
+}
+
+// This is an estimation of the size of the elf file using the location
+// of the section headers and size. This assumes that the section headers
+// are at the end of the elf file. If the elf has a load bias, the size
+// will be too large, but this is acceptable.
+template <typename EhdrType>
+void ElfInterface::GetMaxSizeWithTemplate(Memory* memory, uint64_t* size) {
+ EhdrType ehdr;
+ if (!memory->Read(0, &ehdr, sizeof(ehdr))) {
+ return;
+ }
+ if (ehdr.e_shnum == 0) {
+ return;
+ }
+ *size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
+}
+
// Instantiate all of the needed template functions.
+template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
+template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
+
template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
@@ -221,3 +401,13 @@
template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
+
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
+ uint64_t*);
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
+ uint64_t*);
+
+template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
+template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
+
+} // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index e157320..66bc51f 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -17,12 +17,14 @@
#include <elf.h>
#include <stdint.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
#include "ArmExidx.h"
-#include "ElfInterface.h"
#include "ElfInterfaceArm.h"
#include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) {
if (start_offset_ == 0 || total_entries_ == 0) {
@@ -85,10 +87,10 @@
}
Elf32_Phdr phdr;
- if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
return true;
}
- if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
return true;
}
// The load_bias_ should always be set by this time.
@@ -98,13 +100,15 @@
}
bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
- return StepExidx(pc, regs, process_memory) ||
- ElfInterface32::Step(pc, regs, process_memory);
+ // 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) || StepExidx(pc, regs, process_memory);
}
bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) {
RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
- // First try arm, then try dwarf.
uint64_t entry_offset;
if (!FindEntry(pc, &entry_offset)) {
return false;
@@ -125,3 +129,5 @@
}
return false;
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index ece694f..1f4e8cb 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -23,8 +23,10 @@
#include <iterator>
#include <unordered_map>
-#include "ElfInterface.h"
-#include "Memory.h"
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
class ElfInterfaceArm : public ElfInterface32 {
public:
@@ -87,4 +89,6 @@
std::unordered_map<size_t, uint32_t> addrs_;
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
diff --git a/libunwindstack/Log.cpp b/libunwindstack/Log.cpp
index faeb66c..436e23c 100644
--- a/libunwindstack/Log.cpp
+++ b/libunwindstack/Log.cpp
@@ -25,7 +25,9 @@
#include <android-base/stringprintf.h>
-#include "Log.h"
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
static bool g_print_to_stdout = false;
@@ -51,3 +53,5 @@
}
va_end(args);
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Machine.h b/libunwindstack/Machine.h
index 323ce80..1fb9309 100644
--- a/libunwindstack/Machine.h
+++ b/libunwindstack/Machine.h
@@ -19,6 +19,8 @@
#include <stdint.h>
+namespace unwindstack {
+
enum ArmReg : uint16_t {
ARM_REG_R0 = 0,
ARM_REG_R1,
@@ -83,51 +85,57 @@
ARM64_REG_LR = ARM64_REG_R30,
};
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
enum X86Reg : uint16_t {
X86_REG_EAX = 0,
- X86_REG_ECX,
- X86_REG_EDX,
- X86_REG_EBX,
- X86_REG_ESP,
- X86_REG_EBP,
- X86_REG_ESI,
- X86_REG_EDI,
- X86_REG_EIP,
- X86_REG_EFL,
- X86_REG_CS,
- X86_REG_SS,
- X86_REG_DS,
- X86_REG_ES,
- X86_REG_FS,
- X86_REG_GS,
+ X86_REG_ECX = 1,
+ X86_REG_EDX = 2,
+ X86_REG_EBX = 3,
+ X86_REG_ESP = 4,
+ X86_REG_EBP = 5,
+ X86_REG_ESI = 6,
+ X86_REG_EDI = 7,
+ X86_REG_EIP = 8,
+ X86_REG_EFL = 9,
+ X86_REG_CS = 10,
+ X86_REG_SS = 11,
+ X86_REG_DS = 12,
+ X86_REG_ES = 13,
+ X86_REG_FS = 14,
+ X86_REG_GS = 15,
X86_REG_LAST,
X86_REG_SP = X86_REG_ESP,
X86_REG_PC = X86_REG_EIP,
};
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
enum X86_64Reg : uint16_t {
X86_64_REG_RAX = 0,
- X86_64_REG_RDX,
- X86_64_REG_RCX,
- X86_64_REG_RBX,
- X86_64_REG_RSI,
- X86_64_REG_RDI,
- X86_64_REG_RBP,
- X86_64_REG_RSP,
- X86_64_REG_R8,
- X86_64_REG_R9,
- X86_64_REG_R10,
- X86_64_REG_R11,
- X86_64_REG_R12,
- X86_64_REG_R13,
- X86_64_REG_R14,
- X86_64_REG_R15,
- X86_64_REG_RIP,
+ X86_64_REG_RDX = 1,
+ X86_64_REG_RCX = 2,
+ X86_64_REG_RBX = 3,
+ X86_64_REG_RSI = 4,
+ X86_64_REG_RDI = 5,
+ X86_64_REG_RBP = 6,
+ X86_64_REG_RSP = 7,
+ X86_64_REG_R8 = 8,
+ X86_64_REG_R9 = 9,
+ X86_64_REG_R10 = 10,
+ X86_64_REG_R11 = 11,
+ X86_64_REG_R12 = 12,
+ X86_64_REG_R13 = 13,
+ X86_64_REG_R14 = 14,
+ X86_64_REG_R15 = 15,
+ X86_64_REG_RIP = 16,
X86_64_REG_LAST,
X86_64_REG_SP = X86_64_REG_RSP,
X86_64_REG_PC = X86_64_REG_RIP,
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_MACHINE_H
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
new file mode 100644
index 0000000..96f2cb4
--- /dev/null
+++ b/libunwindstack/MapInfo.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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 <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+Memory* MapInfo::GetFileMemory() {
+ std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
+ if (offset == 0) {
+ if (memory->Init(name, 0)) {
+ return memory.release();
+ }
+ return nullptr;
+ }
+
+ // There are two possibilities when the offset is non-zero.
+ // - There is an elf file embedded in a file.
+ // - The whole file is an elf file, and the offset needs to be saved.
+ //
+ // Map in just the part of the file for the map. If this is not
+ // a valid elf, then reinit as if the whole file is an elf file.
+ // If the offset is a valid elf, then determine the size of the map
+ // and reinit to that size. This is needed because the dynamic linker
+ // only maps in a portion of the original elf, and never the symbol
+ // file data.
+ uint64_t map_size = end - start;
+ if (!memory->Init(name, offset, map_size)) {
+ return nullptr;
+ }
+
+ bool valid;
+ uint64_t max_size;
+ Elf::GetInfo(memory.get(), &valid, &max_size);
+ if (!valid) {
+ // Init as if the whole file is an elf.
+ if (memory->Init(name, 0)) {
+ elf_offset = offset;
+ return memory.release();
+ }
+ return nullptr;
+ }
+
+ if (max_size > map_size) {
+ if (memory->Init(name, offset, max_size)) {
+ return memory.release();
+ }
+ // Try to reinit using the default map_size.
+ if (memory->Init(name, offset, map_size)) {
+ return memory.release();
+ }
+ return nullptr;
+ }
+ return memory.release();
+}
+
+Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
+ if (end <= start) {
+ return nullptr;
+ }
+
+ elf_offset = 0;
+
+ // Fail on device maps.
+ if (flags & MAPS_FLAGS_DEVICE_MAP) {
+ return nullptr;
+ }
+
+ // First try and use the file associated with the info.
+ if (!name.empty()) {
+ Memory* memory = GetFileMemory();
+ if (memory != nullptr) {
+ return memory;
+ }
+ }
+
+ // If the map isn't readable, don't bother trying to read from process memory.
+ if (!(flags & PROT_READ)) {
+ return nullptr;
+ }
+ return new MemoryRange(process_memory, start, end);
+}
+
+Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
+ if (elf) {
+ return elf;
+ }
+
+ elf = new Elf(CreateMemory(process_memory));
+ if (elf->Init() && init_gnu_debugdata) {
+ elf->InitGnuDebugdata();
+ }
+ // If the init fails, keep the elf around as an invalid object so we
+ // don't try to reinit the object.
+ return elf;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
new file mode 100644
index 0000000..5e1c0a2
--- /dev/null
+++ b/libunwindstack/Maps.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2016 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 <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+
+#include <cctype>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+MapInfo* Maps::Find(uint64_t pc) {
+ if (maps_.empty()) {
+ return nullptr;
+ }
+ size_t first = 0;
+ size_t last = maps_.size();
+ while (first < last) {
+ size_t index = (first + last) / 2;
+ MapInfo* cur = &maps_[index];
+ if (pc >= cur->start && pc < cur->end) {
+ return cur;
+ } else if (pc < cur->start) {
+ last = index;
+ } else {
+ first = index + 1;
+ }
+ }
+ return nullptr;
+}
+
+// Assumes that line does not end in '\n'.
+static bool InternalParseLine(const char* line, MapInfo* map_info) {
+ // Do not use a sscanf implementation since it is not performant.
+
+ // Example linux /proc/<pid>/maps lines:
+ // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
+ char* str;
+ const char* old_str = line;
+ map_info->start = strtoul(old_str, &str, 16);
+ if (old_str == str || *str++ != '-') {
+ return false;
+ }
+
+ old_str = str;
+ map_info->end = strtoul(old_str, &str, 16);
+ if (old_str == str || !std::isspace(*str++)) {
+ return false;
+ }
+
+ while (std::isspace(*str)) {
+ str++;
+ }
+
+ // Parse permissions data.
+ if (*str == '\0') {
+ return false;
+ }
+ map_info->flags = 0;
+ if (*str == 'r') {
+ map_info->flags |= PROT_READ;
+ } else if (*str != '-') {
+ return false;
+ }
+ str++;
+ if (*str == 'w') {
+ map_info->flags |= PROT_WRITE;
+ } else if (*str != '-') {
+ return false;
+ }
+ str++;
+ if (*str == 'x') {
+ map_info->flags |= PROT_EXEC;
+ } else if (*str != '-') {
+ return false;
+ }
+ str++;
+ if (*str != 'p' && *str != 's') {
+ return false;
+ }
+ str++;
+
+ if (!std::isspace(*str++)) {
+ return false;
+ }
+
+ old_str = str;
+ map_info->offset = strtoul(old_str, &str, 16);
+ if (old_str == str || !std::isspace(*str)) {
+ return false;
+ }
+
+ // Ignore the 00:00 values.
+ old_str = str;
+ (void)strtoul(old_str, &str, 16);
+ if (old_str == str || *str++ != ':') {
+ return false;
+ }
+ if (std::isspace(*str)) {
+ return false;
+ }
+
+ // Skip the inode.
+ old_str = str;
+ (void)strtoul(str, &str, 16);
+ if (old_str == str || !std::isspace(*str++)) {
+ return false;
+ }
+
+ // Skip decimal digit.
+ old_str = str;
+ (void)strtoul(old_str, &str, 10);
+ if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
+ return false;
+ }
+
+ while (std::isspace(*str)) {
+ str++;
+ }
+ if (*str == '\0') {
+ map_info->name = str;
+ return true;
+ }
+
+ // Save the name data.
+ map_info->name = str;
+
+ // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
+ if (map_info->name.substr(0, 5) == "/dev/" && map_info->name.substr(5, 7) != "ashmem/") {
+ map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
+ }
+ return true;
+}
+
+bool Maps::Parse() {
+ int fd = open(GetMapsFile().c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ return false;
+ }
+
+ bool return_value = true;
+ char buffer[2048];
+ size_t leftover = 0;
+ while (true) {
+ ssize_t bytes = read(fd, &buffer[leftover], 2048 - leftover);
+ if (bytes == -1) {
+ return_value = false;
+ break;
+ }
+ if (bytes == 0) {
+ break;
+ }
+ bytes += leftover;
+ char* line = buffer;
+ while (bytes > 0) {
+ char* newline = static_cast<char*>(memchr(line, '\n', bytes));
+ if (newline == nullptr) {
+ memmove(buffer, line, bytes);
+ break;
+ }
+ *newline = '\0';
+
+ MapInfo map_info;
+ if (!InternalParseLine(line, &map_info)) {
+ return_value = false;
+ break;
+ }
+ maps_.push_back(map_info);
+
+ bytes -= newline - line + 1;
+ line = newline + 1;
+ }
+ leftover = bytes;
+ }
+ close(fd);
+ return return_value;
+}
+
+Maps::~Maps() {
+ for (auto& map : maps_) {
+ delete map.elf;
+ map.elf = nullptr;
+ }
+}
+
+bool BufferMaps::Parse() {
+ const char* start_of_line = buffer_;
+ do {
+ std::string line;
+ const char* end_of_line = strchr(start_of_line, '\n');
+ if (end_of_line == nullptr) {
+ line = start_of_line;
+ } else {
+ line = std::string(start_of_line, end_of_line - start_of_line);
+ end_of_line++;
+ }
+
+ MapInfo map_info;
+ if (!InternalParseLine(line.c_str(), &map_info)) {
+ return false;
+ }
+ maps_.push_back(map_info);
+
+ start_of_line = end_of_line;
+ } while (start_of_line != nullptr && *start_of_line != '\0');
+ return true;
+}
+
+const std::string RemoteMaps::GetMapsFile() const {
+ 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) {
+ MapInfo map_info;
+ ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.start, sizeof(map_info.start)));
+ if (bytes == 0) {
+ break;
+ }
+ if (bytes == -1 || bytes != sizeof(map_info.start)) {
+ return false;
+ }
+ bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.end, sizeof(map_info.end)));
+ if (bytes == -1 || bytes != sizeof(map_info.end)) {
+ return false;
+ }
+ bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.offset, sizeof(map_info.offset)));
+ if (bytes == -1 || bytes != sizeof(map_info.offset)) {
+ return false;
+ }
+ bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.flags, sizeof(map_info.flags)));
+ if (bytes == -1 || bytes != sizeof(map_info.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;
+ }
+ map_info.name = std::string(name.data(), len);
+ }
+ maps_.push_back(map_info);
+ }
+ return true;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 1fcf842..32753df 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -28,7 +28,11 @@
#include <android-base/unique_fd.h>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+
+namespace unwindstack {
bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
string->clear();
@@ -48,6 +52,13 @@
return false;
}
+std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) {
+ if (pid == getpid()) {
+ return std::shared_ptr<Memory>(new MemoryLocal());
+ }
+ return std::shared_ptr<Memory>(new MemoryRemote(pid));
+}
+
bool MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
uint64_t last_read_byte;
if (__builtin_add_overflow(size, addr, &last_read_byte)) {
@@ -96,10 +107,16 @@
offset_ = offset & (getpagesize() - 1);
uint64_t aligned_offset = offset & ~(getpagesize() - 1);
+ if (aligned_offset > static_cast<uint64_t>(buf.st_size) ||
+ offset > static_cast<uint64_t>(buf.st_size)) {
+ return false;
+ }
+
size_ = buf.st_size - aligned_offset;
- if (size < (UINT64_MAX - offset_) && size + offset_ < size_) {
+ uint64_t max_size;
+ if (!__builtin_add_overflow(size, offset_, &max_size) && max_size < size_) {
// Truncate the mapped size.
- size_ = size + offset_;
+ size_ = max_size;
}
void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
if (map == MAP_FAILED) {
@@ -113,14 +130,15 @@
}
bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
- if (addr + size > size_) {
+ uint64_t max_size;
+ if (__builtin_add_overflow(addr, size, &max_size) || max_size > size_) {
return false;
}
memcpy(dst, &data_[addr], size);
return true;
}
-static bool PtraceRead(pid_t pid, uint64_t addr, long* value) {
+bool MemoryRemote::PtraceRead(uint64_t addr, long* value) {
#if !defined(__LP64__)
// Cannot read an address greater than 32 bits.
if (addr > UINT32_MAX) {
@@ -130,7 +148,7 @@
// ptrace() returns -1 and sets errno when the operation fails.
// To disambiguate -1 from a valid result, we clear errno beforehand.
errno = 0;
- *value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
+ *value = ptrace(PTRACE_PEEKTEXT, pid_, reinterpret_cast<void*>(addr), nullptr);
if (*value == -1 && errno) {
return false;
}
@@ -138,11 +156,17 @@
}
bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
+ // Make sure that there is no overflow.
+ uint64_t max_size;
+ if (__builtin_add_overflow(addr, bytes, &max_size)) {
+ return false;
+ }
+
size_t bytes_read = 0;
long data;
size_t align_bytes = addr & (sizeof(long) - 1);
if (align_bytes != 0) {
- if (!PtraceRead(pid_, addr & ~(sizeof(long) - 1), &data)) {
+ if (!PtraceRead(addr & ~(sizeof(long) - 1), &data)) {
return false;
}
size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
@@ -154,7 +178,7 @@
}
for (size_t i = 0; i < bytes / sizeof(long); i++) {
- if (!PtraceRead(pid_, addr, &data)) {
+ if (!PtraceRead(addr, &data)) {
return false;
}
memcpy(dst, &data, sizeof(long));
@@ -165,7 +189,7 @@
size_t left_over = bytes & (sizeof(long) - 1);
if (left_over) {
- if (!PtraceRead(pid_, addr, &data)) {
+ if (!PtraceRead(addr, &data)) {
return false;
}
memcpy(dst, &data, left_over);
@@ -175,7 +199,13 @@
}
bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
- // The process_vm_readv call does will not always work on remote
+ // Make sure that there is no overflow.
+ uint64_t max_size;
+ if (__builtin_add_overflow(addr, size, &max_size)) {
+ return false;
+ }
+
+ // The process_vm_readv call will not always work on remote
// processes, so only use it for reads from the current pid.
// Use this method to avoid crashes if an address is invalid since
// unwind data could try to access any part of the address space.
@@ -208,9 +238,36 @@
}
bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
- if (addr < start_ || addr + size > start_ + offset_ + size_) {
+ uint64_t max_size;
+ if (__builtin_add_overflow(addr, size, &max_size)) {
+ return false;
+ }
+
+ uint64_t real_size;
+ if (__builtin_add_overflow(start_, offset_, &real_size) ||
+ __builtin_add_overflow(real_size, size_, &real_size)) {
+ return false;
+ }
+
+ if (addr < start_ || max_size > real_size) {
return false;
}
memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
return true;
}
+
+MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t end)
+ : memory_(memory), begin_(begin), length_(end - begin) {
+ CHECK(end > begin);
+}
+
+bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
+ uint64_t max_read;
+ if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) {
+ return false;
+ }
+ // The check above guarantees that addr + begin_ will not overflow.
+ return memory_->Read(addr + begin_, dst, size);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index adb6522..4d09c1b 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <assert.h>
#include <elf.h>
#include <stdint.h>
#include <sys/ptrace.h>
@@ -22,28 +21,23 @@
#include <vector>
-#include "Elf.h"
-#include "ElfInterface.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "Check.h"
#include "Machine.h"
-#include "MapInfo.h"
-#include "Regs.h"
+#include "Ucontext.h"
#include "User.h"
-template <typename AddressType>
-uint64_t RegsTmpl<AddressType>::GetRelPc(Elf* elf, const MapInfo* map_info) {
- uint64_t load_bias = 0;
- if (elf->valid()) {
- load_bias = elf->interface()->load_bias();
- }
-
- return pc_ - map_info->start + load_bias + map_info->elf_offset;
-}
+namespace unwindstack {
template <typename AddressType>
-bool RegsTmpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
+bool RegsImpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
switch (return_loc_.type) {
case LOCATION_REGISTER:
- assert(return_loc_.value < total_regs_);
+ CHECK(return_loc_.value < total_regs_);
*value = regs_[return_loc_.value];
return true;
case LOCATION_SP_OFFSET:
@@ -59,8 +53,11 @@
}
}
-RegsArm::RegsArm() : RegsTmpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP,
- Location(LOCATION_REGISTER, ARM_REG_LR)) {
+RegsArm::RegsArm()
+ : RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
+
+uint32_t RegsArm::MachineType() {
+ return EM_ARM;
}
uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
@@ -89,8 +86,16 @@
return rel_pc - 4;
}
-RegsArm64::RegsArm64() : RegsTmpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP,
- Location(LOCATION_REGISTER, ARM64_REG_LR)) {
+void RegsArm::SetFromRaw() {
+ set_pc(regs_[ARM_REG_PC]);
+ set_sp(regs_[ARM_REG_SP]);
+}
+
+RegsArm64::RegsArm64()
+ : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
+
+uint32_t RegsArm64::MachineType() {
+ return EM_AARCH64;
}
uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
@@ -104,8 +109,16 @@
return rel_pc - 4;
}
-RegsX86::RegsX86() : RegsTmpl<uint32_t>(X86_REG_LAST, X86_REG_SP,
- Location(LOCATION_SP_OFFSET, -4)) {
+void RegsArm64::SetFromRaw() {
+ set_pc(regs_[ARM64_REG_PC]);
+ set_sp(regs_[ARM64_REG_SP]);
+}
+
+RegsX86::RegsX86()
+ : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
+
+uint32_t RegsX86::MachineType() {
+ return EM_386;
}
uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
@@ -119,8 +132,16 @@
return rel_pc - 1;
}
-RegsX86_64::RegsX86_64() : RegsTmpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP,
- Location(LOCATION_SP_OFFSET, -8)) {
+void RegsX86::SetFromRaw() {
+ set_pc(regs_[X86_REG_PC]);
+ set_sp(regs_[X86_REG_SP]);
+}
+
+RegsX86_64::RegsX86_64()
+ : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
+
+uint32_t RegsX86_64::MachineType() {
+ return EM_X86_64;
}
uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
@@ -135,15 +156,17 @@
return rel_pc - 1;
}
+void RegsX86_64::SetFromRaw() {
+ set_pc(regs_[X86_64_REG_PC]);
+ set_sp(regs_[X86_64_REG_SP]);
+}
+
static Regs* ReadArm(void* remote_data) {
arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
RegsArm* regs = new RegsArm();
memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t));
-
- regs->set_pc(user->regs[ARM_REG_PC]);
- regs->set_sp(user->regs[ARM_REG_SP]);
-
+ regs->SetFromRaw();
return regs;
}
@@ -152,9 +175,10 @@
RegsArm64* regs = new RegsArm64();
memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t));
- regs->set_pc(user->pc);
- regs->set_sp(user->sp);
-
+ uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+ reg_data[ARM64_REG_PC] = user->pc;
+ reg_data[ARM64_REG_SP] = user->sp;
+ regs->SetFromRaw();
return regs;
}
@@ -172,9 +196,7 @@
(*regs)[X86_REG_ESP] = user->esp;
(*regs)[X86_REG_EIP] = user->eip;
- regs->set_pc(user->eip);
- regs->set_sp(user->esp);
-
+ regs->SetFromRaw();
return regs;
}
@@ -200,15 +222,13 @@
(*regs)[X86_64_REG_RSP] = user->rsp;
(*regs)[X86_64_REG_RIP] = user->rip;
- regs->set_pc(user->rip);
- regs->set_sp(user->rsp);
-
+ regs->SetFromRaw();
return regs;
}
// This function assumes that reg_data is already aligned to a 64 bit value.
// If not this could crash with an unaligned access.
-Regs* Regs::RemoteGet(pid_t pid, uint32_t* machine_type) {
+Regs* Regs::RemoteGet(pid_t pid) {
// Make the buffer large enough to contain the largest registers type.
std::vector<uint64_t> buffer(MAX_USER_REGS_SIZE / sizeof(uint64_t));
struct iovec io;
@@ -221,17 +241,312 @@
switch (io.iov_len) {
case sizeof(x86_user_regs):
- *machine_type = EM_386;
return ReadX86(buffer.data());
case sizeof(x86_64_user_regs):
- *machine_type = EM_X86_64;
return ReadX86_64(buffer.data());
case sizeof(arm_user_regs):
- *machine_type = EM_ARM;
return ReadArm(buffer.data());
case sizeof(arm64_user_regs):
- *machine_type = EM_AARCH64;
return ReadArm64(buffer.data());
}
return nullptr;
}
+
+static Regs* CreateFromArmUcontext(void* ucontext) {
+ arm_ucontext_t* arm_ucontext = reinterpret_cast<arm_ucontext_t*>(ucontext);
+
+ RegsArm* regs = new RegsArm();
+ memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t));
+ regs->SetFromRaw();
+ return regs;
+}
+
+static Regs* CreateFromArm64Ucontext(void* ucontext) {
+ arm64_ucontext_t* arm64_ucontext = reinterpret_cast<arm64_ucontext_t*>(ucontext);
+
+ RegsArm64* regs = new RegsArm64();
+ memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t));
+ regs->SetFromRaw();
+ return regs;
+}
+
+void RegsX86::SetFromUcontext(x86_ucontext_t* ucontext) {
+ // Put the registers in the expected order.
+ regs_[X86_REG_EDI] = ucontext->uc_mcontext.edi;
+ regs_[X86_REG_ESI] = ucontext->uc_mcontext.esi;
+ regs_[X86_REG_EBP] = ucontext->uc_mcontext.ebp;
+ regs_[X86_REG_ESP] = ucontext->uc_mcontext.esp;
+ regs_[X86_REG_EBX] = ucontext->uc_mcontext.ebx;
+ regs_[X86_REG_EDX] = ucontext->uc_mcontext.edx;
+ regs_[X86_REG_ECX] = ucontext->uc_mcontext.ecx;
+ regs_[X86_REG_EAX] = ucontext->uc_mcontext.eax;
+ regs_[X86_REG_EIP] = ucontext->uc_mcontext.eip;
+ SetFromRaw();
+}
+
+static Regs* CreateFromX86Ucontext(void* ucontext) {
+ x86_ucontext_t* x86_ucontext = reinterpret_cast<x86_ucontext_t*>(ucontext);
+
+ RegsX86* regs = new RegsX86();
+ regs->SetFromUcontext(x86_ucontext);
+ return regs;
+}
+
+void RegsX86_64::SetFromUcontext(x86_64_ucontext_t* ucontext) {
+ // R8-R15
+ memcpy(®s_[X86_64_REG_R8], &ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
+
+ // Rest of the registers.
+ regs_[X86_64_REG_RDI] = ucontext->uc_mcontext.rdi;
+ regs_[X86_64_REG_RSI] = ucontext->uc_mcontext.rsi;
+ regs_[X86_64_REG_RBP] = ucontext->uc_mcontext.rbp;
+ regs_[X86_64_REG_RBX] = ucontext->uc_mcontext.rbx;
+ regs_[X86_64_REG_RDX] = ucontext->uc_mcontext.rdx;
+ regs_[X86_64_REG_RAX] = ucontext->uc_mcontext.rax;
+ regs_[X86_64_REG_RCX] = ucontext->uc_mcontext.rcx;
+ regs_[X86_64_REG_RSP] = ucontext->uc_mcontext.rsp;
+ regs_[X86_64_REG_RIP] = ucontext->uc_mcontext.rip;
+
+ SetFromRaw();
+}
+
+static Regs* CreateFromX86_64Ucontext(void* ucontext) {
+ x86_64_ucontext_t* x86_64_ucontext = reinterpret_cast<x86_64_ucontext_t*>(ucontext);
+
+ RegsX86_64* regs = new RegsX86_64();
+ regs->SetFromUcontext(x86_64_ucontext);
+ return regs;
+}
+
+Regs* Regs::CreateFromUcontext(uint32_t machine_type, void* ucontext) {
+ switch (machine_type) {
+ case EM_386:
+ return CreateFromX86Ucontext(ucontext);
+ case EM_X86_64:
+ return CreateFromX86_64Ucontext(ucontext);
+ case EM_ARM:
+ return CreateFromArmUcontext(ucontext);
+ case EM_AARCH64:
+ return CreateFromArm64Ucontext(ucontext);
+ }
+ return nullptr;
+}
+
+uint32_t Regs::CurrentMachineType() {
+#if defined(__arm__)
+ return EM_ARM;
+#elif defined(__aarch64__)
+ return EM_AARCH64;
+#elif defined(__i386__)
+ return EM_386;
+#elif defined(__x86_64__)
+ return EM_X86_64;
+#else
+ abort();
+#endif
+}
+
+Regs* Regs::CreateFromLocal() {
+ Regs* regs;
+#if defined(__arm__)
+ regs = new RegsArm();
+#elif defined(__aarch64__)
+ regs = new RegsArm64();
+#elif defined(__i386__)
+ regs = new RegsX86();
+#elif defined(__x86_64__)
+ regs = new RegsX86_64();
+#else
+ abort();
+#endif
+ return regs;
+}
+
+bool RegsArm::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+ uint32_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;
+ }
+
+ uint64_t offset = 0;
+ if (data == 0xe3a07077 || data == 0xef900077 || data == 0xdf002777) {
+ // non-RT sigreturn call.
+ // __restore:
+ //
+ // Form 1 (arm):
+ // 0x77 0x70 mov r7, #0x77
+ // 0xa0 0xe3 svc 0x00000000
+ //
+ // Form 2 (arm):
+ // 0x77 0x00 0x90 0xef svc 0x00900077
+ //
+ // Form 3 (thumb):
+ // 0x77 0x27 movs r7, #77
+ // 0x00 0xdf svc 0
+ if (!process_memory->Read(sp(), &data, sizeof(data))) {
+ return false;
+ }
+ if (data == 0x5ac3c35a) {
+ // SP + uc_mcontext offset + r0 offset.
+ offset = sp() + 0x14 + 0xc;
+ } else {
+ // SP + r0 offset
+ offset = sp() + 0xc;
+ }
+ } else if (data == 0xe3a070ad || data == 0xef9000ad || data == 0xdf0027ad) {
+ // RT sigreturn call.
+ // __restore_rt:
+ //
+ // Form 1 (arm):
+ // 0xad 0x70 mov r7, #0xad
+ // 0xa0 0xe3 svc 0x00000000
+ //
+ // Form 2 (arm):
+ // 0xad 0x00 0x90 0xef svc 0x009000ad
+ //
+ // Form 3 (thumb):
+ // 0xad 0x27 movs r7, #ad
+ // 0x00 0xdf svc 0
+ if (!process_memory->Read(sp(), &data, sizeof(data))) {
+ return false;
+ }
+ if (data == sp() + 8) {
+ // SP + 8 + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
+ offset = sp() + 8 + 0x80 + 0x14 + 0xc;
+ } else {
+ // SP + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
+ offset = sp() + 0x80 + 0x14 + 0xc;
+ }
+ }
+ if (offset == 0) {
+ return false;
+ }
+
+ if (!process_memory->Read(offset, regs_.data(), sizeof(uint32_t) * ARM_REG_LAST)) {
+ return false;
+ }
+ SetFromRaw();
+ return true;
+}
+
+bool RegsArm64::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.
+ // __kernel_rt_sigreturn:
+ // 0xd2801168 mov x8, #0x8b
+ // 0xd4000001 svc #0x0
+ if (data != 0xd4000001d2801168ULL) {
+ return false;
+ }
+
+ // SP + sizeof(siginfo_t) + uc_mcontext offset + X0 offset.
+ if (!process_memory->Read(sp() + 0x80 + 0xb0 + 0x08, regs_.data(),
+ sizeof(uint64_t) * ARM64_REG_LAST)) {
+ return false;
+ }
+
+ SetFromRaw();
+ return true;
+}
+
+bool RegsX86::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;
+ }
+
+ if (data == 0x80cd00000077b858ULL) {
+ // Without SA_SIGINFO set, the return sequence is:
+ //
+ // __restore:
+ // 0x58 pop %eax
+ // 0xb8 0x77 0x00 0x00 0x00 movl 0x77,%eax
+ // 0xcd 0x80 int 0x80
+ //
+ // SP points at arguments:
+ // int signum
+ // struct sigcontext (same format as mcontext)
+ struct x86_mcontext_t context;
+ if (!process_memory->Read(sp() + 4, &context, sizeof(context))) {
+ return false;
+ }
+ regs_[X86_REG_EBP] = context.ebp;
+ regs_[X86_REG_ESP] = context.esp;
+ regs_[X86_REG_EBX] = context.ebx;
+ regs_[X86_REG_EDX] = context.edx;
+ regs_[X86_REG_ECX] = context.ecx;
+ regs_[X86_REG_EAX] = context.eax;
+ regs_[X86_REG_EIP] = context.eip;
+ SetFromRaw();
+ return true;
+ } else if ((data & 0x00ffffffffffffffULL) == 0x0080cd000000adb8ULL) {
+ // With SA_SIGINFO set, the return sequence is:
+ //
+ // __restore_rt:
+ // 0xb8 0xad 0x00 0x00 0x00 movl 0xad,%eax
+ // 0xcd 0x80 int 0x80
+ //
+ // SP points at arguments:
+ // int signum
+ // siginfo*
+ // ucontext*
+
+ // Get the location of the sigcontext data.
+ uint32_t ptr;
+ if (!process_memory->Read(sp() + 8, &ptr, sizeof(ptr))) {
+ return false;
+ }
+ // Only read the portion of the data structure we care about.
+ x86_ucontext_t x86_ucontext;
+ if (!process_memory->Read(ptr + 0x14, &x86_ucontext.uc_mcontext, sizeof(x86_mcontext_t))) {
+ return false;
+ }
+ SetFromUcontext(&x86_ucontext);
+ return true;
+ }
+ return false;
+}
+
+bool RegsX86_64::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)) || data != 0x0f0000000fc0c748) {
+ return false;
+ }
+
+ uint16_t data2;
+ if (!elf_memory->Read(rel_pc + 8, &data2, sizeof(data2)) || data2 != 0x0f05) {
+ return false;
+ }
+
+ // __restore_rt:
+ // 0x48 0xc7 0xc0 0x0f 0x00 0x00 0x00 mov $0xf,%rax
+ // 0x0f 0x05 syscall
+ // 0x0f nopl 0x0($rax)
+
+ // Read the mcontext data from the stack.
+ // sp points to the ucontext data structure, read only the mcontext part.
+ x86_64_ucontext_t x86_64_ucontext;
+ if (!process_memory->Read(sp() + 0x28, &x86_64_ucontext.uc_mcontext, sizeof(x86_64_mcontext_t))) {
+ return false;
+ }
+ SetFromUcontext(&x86_64_ucontext);
+ return true;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
new file mode 100644
index 0000000..42d816a
--- /dev/null
+++ b/libunwindstack/Symbols.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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 <elf.h>
+#include <stdint.h>
+
+#include <string>
+
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+#include "Symbols.h"
+
+namespace unwindstack {
+
+Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
+ uint64_t str_size)
+ : cur_offset_(offset),
+ offset_(offset),
+ end_(offset + size),
+ entry_size_(entry_size),
+ str_offset_(str_offset),
+ str_end_(str_offset_ + str_size) {}
+
+const Symbols::Info* Symbols::GetInfoFromCache(uint64_t addr) {
+ // Binary search the table.
+ size_t first = 0;
+ size_t last = symbols_.size();
+ while (first < last) {
+ size_t current = first + (last - first) / 2;
+ const Info* info = &symbols_[current];
+ if (addr < info->start_offset) {
+ last = current;
+ } else if (addr < info->end_offset) {
+ return info;
+ } else {
+ first = current + 1;
+ }
+ }
+ return nullptr;
+}
+
+template <typename SymType>
+bool Symbols::GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
+ uint64_t* func_offset) {
+ addr += load_bias;
+
+ if (symbols_.size() != 0) {
+ const Info* info = GetInfoFromCache(addr);
+ if (info) {
+ CHECK(addr >= info->start_offset && addr <= info->end_offset);
+ *func_offset = addr - info->start_offset;
+ return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset);
+ }
+ }
+
+ bool symbol_added = false;
+ bool return_value = false;
+ while (cur_offset_ + entry_size_ <= end_) {
+ SymType entry;
+ if (!elf_memory->Read(cur_offset_, &entry, sizeof(entry))) {
+ // Stop all processing, something looks like it is corrupted.
+ cur_offset_ = UINT64_MAX;
+ return false;
+ }
+ cur_offset_ += entry_size_;
+
+ if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
+ // Treat st_value as virtual address.
+ uint64_t start_offset = entry.st_value;
+ if (entry.st_shndx != SHN_ABS) {
+ start_offset += load_bias;
+ }
+ uint64_t end_offset = start_offset + entry.st_size;
+
+ // Cache the value.
+ symbols_.emplace_back(start_offset, end_offset, str_offset_ + entry.st_name);
+ symbol_added = true;
+
+ if (addr >= start_offset && addr < end_offset) {
+ *func_offset = addr - start_offset;
+ uint64_t offset = str_offset_ + entry.st_name;
+ if (offset < str_end_) {
+ return_value = elf_memory->ReadString(offset, name, str_end_ - offset);
+ }
+ break;
+ }
+ }
+ }
+
+ if (symbol_added) {
+ std::sort(symbols_.begin(), symbols_.end(),
+ [](const Info& a, const Info& b) { return a.start_offset < b.start_offset; });
+ }
+ return return_value;
+}
+
+// Instantiate all of the needed template functions.
+template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
+
+} // namespace unwindstack
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
new file mode 100644
index 0000000..689144b
--- /dev/null
+++ b/libunwindstack/Symbols.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 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_SYMBOLS_H
+#define _LIBUNWINDSTACK_SYMBOLS_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+namespace unwindstack {
+
+// Forward declaration.
+class Memory;
+
+class Symbols {
+ struct Info {
+ Info(uint64_t start_offset, uint64_t end_offset, uint64_t str_offset)
+ : start_offset(start_offset), end_offset(end_offset), str_offset(str_offset) {}
+ uint64_t start_offset;
+ uint64_t end_offset;
+ uint64_t str_offset;
+ };
+
+ public:
+ Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
+ uint64_t str_size);
+ virtual ~Symbols() = default;
+
+ const Info* GetInfoFromCache(uint64_t addr);
+
+ template <typename SymType>
+ bool GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
+ uint64_t* func_offset);
+
+ void ClearCache() {
+ symbols_.clear();
+ cur_offset_ = offset_;
+ }
+
+ private:
+ uint64_t cur_offset_;
+ uint64_t offset_;
+ uint64_t end_;
+ uint64_t entry_size_;
+ uint64_t str_offset_;
+ uint64_t str_end_;
+
+ std::vector<Info> symbols_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/Ucontext.h b/libunwindstack/Ucontext.h
new file mode 100644
index 0000000..22f6a89
--- /dev/null
+++ b/libunwindstack/Ucontext.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 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_H
+#define _LIBUNWINDSTACK_UCONTEXT_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+//-------------------------------------------------------------------
+// ARM ucontext structures
+//-------------------------------------------------------------------
+struct arm_stack_t {
+ uint32_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint32_t ss_size; // size_t
+};
+
+struct arm_mcontext_t {
+ uint32_t trap_no; // unsigned long
+ uint32_t error_code; // unsigned long
+ uint32_t oldmask; // unsigned long
+ uint32_t regs[ARM_REG_LAST]; // unsigned long
+ uint32_t cpsr; // unsigned long
+ uint32_t fault_address; // unsigned long
+};
+
+struct arm_ucontext_t {
+ uint32_t uc_flags; // unsigned long
+ uint32_t uc_link; // struct ucontext*
+ arm_stack_t uc_stack;
+ arm_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// ARM64 ucontext structures
+//-------------------------------------------------------------------
+struct arm64_stack_t {
+ uint64_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint64_t ss_size; // size_t
+};
+
+struct arm64_sigset_t {
+ uint64_t sig; // unsigned long
+};
+
+struct arm64_mcontext_t {
+ uint64_t fault_address; // __u64
+ uint64_t regs[ARM64_REG_LAST]; // __u64
+ uint64_t pstate; // __u64
+ // Nothing else is used, so don't define it.
+};
+
+struct arm64_ucontext_t {
+ uint64_t uc_flags; // unsigned long
+ uint64_t uc_link; // struct ucontext*
+ arm64_stack_t uc_stack;
+ arm64_sigset_t uc_sigmask;
+ // The kernel adds extra padding after uc_sigmask to match glibc sigset_t on ARM64.
+ char __padding[128 - sizeof(arm64_sigset_t)];
+ // The full structure requires 16 byte alignment, but our partial structure
+ // doesn't, so force the alignment.
+ arm64_mcontext_t uc_mcontext __attribute__((aligned(16)));
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// X86 ucontext structures
+//-------------------------------------------------------------------
+struct x86_stack_t {
+ uint32_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint32_t ss_size; // size_t
+};
+
+struct x86_mcontext_t {
+ uint32_t gs;
+ uint32_t fs;
+ uint32_t es;
+ uint32_t ds;
+ uint32_t edi;
+ uint32_t esi;
+ uint32_t ebp;
+ uint32_t esp;
+ uint32_t ebx;
+ uint32_t edx;
+ uint32_t ecx;
+ uint32_t eax;
+ uint32_t trapno;
+ uint32_t err;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t efl;
+ uint32_t uesp;
+ uint32_t ss;
+ // Only care about the registers, skip everything else.
+};
+
+struct x86_ucontext_t {
+ uint32_t uc_flags; // unsigned long
+ uint32_t uc_link; // struct ucontext*
+ x86_stack_t uc_stack;
+ x86_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// X86_64 ucontext structures
+//-------------------------------------------------------------------
+struct x86_64_stack_t {
+ uint64_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint64_t ss_size; // size_t
+};
+
+struct x86_64_mcontext_t {
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rdi;
+ uint64_t rsi;
+ uint64_t rbp;
+ uint64_t rbx;
+ uint64_t rdx;
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rsp;
+ uint64_t rip;
+ uint64_t efl;
+ uint64_t csgsfs;
+ uint64_t err;
+ uint64_t trapno;
+ uint64_t oldmask;
+ uint64_t cr2;
+ // Only care about the registers, skip everything else.
+};
+
+struct x86_64_ucontext_t {
+ uint64_t uc_flags; // unsigned long
+ uint64_t uc_link; // struct ucontext*
+ x86_64_stack_t uc_stack;
+ x86_64_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+//-------------------------------------------------------------------
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_UCONTEXT_H
diff --git a/libunwindstack/User.h b/libunwindstack/User.h
index a695467..53f7e50 100644
--- a/libunwindstack/User.h
+++ b/libunwindstack/User.h
@@ -29,6 +29,8 @@
#ifndef _LIBUNWINDSTACK_USER_H
#define _LIBUNWINDSTACK_USER_H
+namespace unwindstack {
+
struct x86_user_regs {
uint32_t ebx;
uint32_t ecx;
@@ -93,4 +95,6 @@
// The largest user structure.
constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10;
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_USER_H
diff --git a/libunwindstack/include/unwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
new file mode 100644
index 0000000..3467e6a
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 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_DWARF_LOCATION_H
+#define _LIBUNWINDSTACK_DWARF_LOCATION_H
+
+#include <stdint.h>
+
+#include <unordered_map>
+
+namespace unwindstack {
+
+enum DwarfLocationEnum : uint8_t {
+ DWARF_LOCATION_INVALID = 0,
+ DWARF_LOCATION_UNDEFINED,
+ DWARF_LOCATION_OFFSET,
+ DWARF_LOCATION_VAL_OFFSET,
+ DWARF_LOCATION_REGISTER,
+ DWARF_LOCATION_EXPRESSION,
+ DWARF_LOCATION_VAL_EXPRESSION,
+};
+
+struct DwarfLocation {
+ DwarfLocationEnum type;
+ uint64_t values[2];
+};
+
+typedef std::unordered_map<uint16_t, DwarfLocation> dwarf_loc_regs_t;
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/include/unwindstack/DwarfMemory.h b/libunwindstack/include/unwindstack/DwarfMemory.h
new file mode 100644
index 0000000..8dd8d2b
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfMemory.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 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_DWARF_MEMORY_H
+#define _LIBUNWINDSTACK_DWARF_MEMORY_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class DwarfMemory {
+ public:
+ DwarfMemory(Memory* memory) : memory_(memory) {}
+ virtual ~DwarfMemory() = default;
+
+ bool ReadBytes(void* dst, size_t num_bytes);
+
+ template <typename SignedType>
+ bool ReadSigned(uint64_t* value);
+
+ bool ReadULEB128(uint64_t* value);
+
+ bool ReadSLEB128(int64_t* value);
+
+ template <typename AddressType>
+ size_t GetEncodedSize(uint8_t encoding);
+
+ bool AdjustEncodedValue(uint8_t encoding, uint64_t* value);
+
+ template <typename AddressType>
+ bool ReadEncodedValue(uint8_t encoding, uint64_t* value);
+
+ uint64_t cur_offset() { return cur_offset_; }
+ void set_cur_offset(uint64_t cur_offset) { cur_offset_ = cur_offset; }
+
+ void set_pc_offset(uint64_t offset) { pc_offset_ = offset; }
+ void clear_pc_offset() { pc_offset_ = static_cast<uint64_t>(-1); }
+
+ void set_data_offset(uint64_t offset) { data_offset_ = offset; }
+ void clear_data_offset() { data_offset_ = static_cast<uint64_t>(-1); }
+
+ void set_func_offset(uint64_t offset) { func_offset_ = offset; }
+ void clear_func_offset() { func_offset_ = static_cast<uint64_t>(-1); }
+
+ void set_text_offset(uint64_t offset) { text_offset_ = offset; }
+ void clear_text_offset() { text_offset_ = static_cast<uint64_t>(-1); }
+
+ private:
+ Memory* memory_;
+ uint64_t cur_offset_ = 0;
+
+ uint64_t pc_offset_ = static_cast<uint64_t>(-1);
+ uint64_t data_offset_ = static_cast<uint64_t>(-1);
+ uint64_t func_offset_ = static_cast<uint64_t>(-1);
+ uint64_t text_offset_ = static_cast<uint64_t>(-1);
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
new file mode 100644
index 0000000..26485ae
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2016 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_DWARF_SECTION_H
+#define _LIBUNWINDSTACK_DWARF_SECTION_H
+
+#include <stdint.h>
+
+#include <iterator>
+#include <unordered_map>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+enum DwarfError : uint8_t;
+class Memory;
+class Regs;
+
+class DwarfSection {
+ public:
+ DwarfSection(Memory* memory);
+ virtual ~DwarfSection() = default;
+
+ class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> {
+ public:
+ iterator(DwarfSection* section, size_t index) : section_(section), index_(index) {}
+
+ iterator& operator++() {
+ index_++;
+ return *this;
+ }
+ iterator& operator++(int increment) {
+ index_ += increment;
+ return *this;
+ }
+ iterator& operator--() {
+ index_--;
+ return *this;
+ }
+ iterator& operator--(int decrement) {
+ index_ -= decrement;
+ return *this;
+ }
+
+ bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
+ bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
+
+ const DwarfFde* operator*() { return section_->GetFdeFromIndex(index_); }
+
+ private:
+ DwarfSection* section_ = nullptr;
+ size_t index_ = 0;
+ };
+
+ iterator begin() { return iterator(this, 0); }
+ iterator end() { return iterator(this, fde_count_); }
+
+ DwarfError last_error() { return last_error_; }
+
+ virtual bool Init(uint64_t offset, uint64_t size) = 0;
+
+ virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*) = 0;
+
+ virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
+
+ virtual bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) = 0;
+
+ virtual const DwarfFde* GetFdeFromIndex(size_t index) = 0;
+
+ const DwarfFde* GetFdeFromPc(uint64_t pc);
+
+ virtual const DwarfFde* GetFdeFromOffset(uint64_t fde_offset) = 0;
+
+ virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
+
+ virtual bool IsCie32(uint32_t value32) = 0;
+
+ virtual bool IsCie64(uint64_t value64) = 0;
+
+ virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
+
+ virtual uint64_t GetCieOffsetFromFde64(uint64_t pointer) = 0;
+
+ virtual uint64_t AdjustPcFromFde(uint64_t pc) = 0;
+
+ bool Step(uint64_t pc, Regs* regs, Memory* process_memory);
+
+ protected:
+ DwarfMemory memory_;
+ DwarfError last_error_;
+
+ uint64_t fde_count_ = 0;
+ std::unordered_map<uint64_t, DwarfFde> fde_entries_;
+ std::unordered_map<uint64_t, DwarfCie> cie_entries_;
+ std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
+};
+
+template <typename AddressType>
+class DwarfSectionImpl : public DwarfSection {
+ public:
+ DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
+ virtual ~DwarfSectionImpl() = default;
+
+ bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
+ Regs* regs) override;
+
+ const DwarfCie* GetCie(uint64_t offset);
+ bool FillInCie(DwarfCie* cie);
+
+ const DwarfFde* GetFdeFromOffset(uint64_t offset) override;
+ bool FillInFde(DwarfFde* fde);
+
+ bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
+
+ bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) override;
+
+ protected:
+ bool EvalExpression(const DwarfLocation& loc, uint8_t version, Memory* regular_memory,
+ AddressType* value);
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_SECTION_H
diff --git a/libunwindstack/include/unwindstack/DwarfStructs.h b/libunwindstack/include/unwindstack/DwarfStructs.h
new file mode 100644
index 0000000..4b481f0
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfStructs.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 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_DWARF_STRUCTS_H
+#define _LIBUNWINDSTACK_DWARF_STRUCTS_H
+
+#include <stdint.h>
+
+#include <vector>
+
+namespace unwindstack {
+
+struct DwarfCie {
+ uint8_t version = 0;
+ uint8_t fde_address_encoding = 0;
+ uint8_t lsda_encoding = 0;
+ uint8_t segment_size = 0;
+ std::vector<char> augmentation_string;
+ uint64_t personality_handler = 0;
+ uint64_t cfa_instructions_offset = 0;
+ uint64_t cfa_instructions_end = 0;
+ uint64_t code_alignment_factor = 0;
+ int64_t data_alignment_factor = 0;
+ uint64_t return_address_register = 0;
+};
+
+struct DwarfFde {
+ uint64_t cie_offset = 0;
+ uint64_t cfa_instructions_offset = 0;
+ uint64_t cfa_instructions_end = 0;
+ uint64_t pc_start = 0;
+ uint64_t pc_end = 0;
+ uint64_t lsda_address = 0;
+ const DwarfCie* cie = nullptr;
+};
+
+constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
similarity index 72%
rename from libunwindstack/Elf.h
rename to libunwindstack/include/unwindstack/Elf.h
index 7bf45b8..4e7eb34 100644
--- a/libunwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -22,14 +22,17 @@
#include <memory>
#include <string>
-#include "ElfInterface.h"
-#include "Memory.h"
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
#if !defined(EM_AARCH64)
#define EM_AARCH64 183
#endif
+namespace unwindstack {
+
// Forward declaration.
+struct MapInfo;
class Regs;
class Elf {
@@ -41,20 +44,18 @@
void InitGnuDebugdata();
- bool GetSoname(std::string* name) {
- return valid_ && interface_->GetSoname(name);
- }
+ bool GetSoname(std::string* name);
- bool GetFunctionName(uint64_t, std::string*, uint64_t*) {
- return false;
- }
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
- bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
- return valid_ && interface_->Step(rel_pc, regs, process_memory);
- }
+ uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
+
+ bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory);
ElfInterface* CreateInterfaceFromMemory(Memory* memory);
+ uint64_t GetLoadBias();
+
bool valid() { return valid_; }
uint32_t machine_type() { return machine_type_; }
@@ -65,14 +66,23 @@
ElfInterface* interface() { return interface_.get(); }
+ ElfInterface* gnu_debugdata_interface() { return gnu_debugdata_interface_.get(); }
+
static bool IsValidElf(Memory* memory);
+ static void GetInfo(Memory* memory, bool* valid, uint64_t* size);
+
protected:
bool valid_ = false;
std::unique_ptr<ElfInterface> interface_;
std::unique_ptr<Memory> memory_;
uint32_t machine_type_;
uint8_t class_type_;
+
+ std::unique_ptr<Memory> gnu_debugdata_memory_;
+ std::unique_ptr<ElfInterface> gnu_debugdata_interface_;
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_ELF_H
diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
similarity index 70%
rename from libunwindstack/ElfInterface.h
rename to libunwindstack/include/unwindstack/ElfInterface.h
index 944146c..142a625 100644
--- a/libunwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -25,9 +25,14 @@
#include <unordered_map>
#include <vector>
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
// Forward declarations.
class Memory;
class Regs;
+class Symbols;
struct LoadInfo {
uint64_t offset;
@@ -44,7 +49,7 @@
class ElfInterface {
public:
ElfInterface(Memory* memory) : memory_(memory) {}
- virtual ~ElfInterface() = default;
+ virtual ~ElfInterface();
virtual bool Init() = 0;
@@ -68,10 +73,18 @@
uint64_t dynamic_size() { return dynamic_size_; }
uint64_t eh_frame_offset() { return eh_frame_offset_; }
uint64_t eh_frame_size() { return eh_frame_size_; }
+ uint64_t debug_frame_offset() { return debug_frame_offset_; }
+ uint64_t debug_frame_size() { return debug_frame_size_; }
uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; }
uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; }
+ DwarfSection* eh_frame() { return eh_frame_.get(); }
+ DwarfSection* debug_frame() { return debug_frame_.get(); }
+
protected:
+ template <typename AddressType>
+ void InitHeadersWithTemplate();
+
template <typename EhdrType, typename PhdrType, typename ShdrType>
bool ReadAllHeaders();
@@ -84,8 +97,14 @@
template <typename DynType>
bool GetSonameWithTemplate(std::string* soname);
+ template <typename SymType>
+ bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
+
virtual bool HandleType(uint64_t, uint32_t) { return false; }
+ template <typename EhdrType>
+ static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
+
Memory* memory_;
std::unordered_map<uint64_t, LoadInfo> pt_loads_;
uint64_t load_bias_ = 0;
@@ -105,6 +124,11 @@
uint8_t soname_type_ = SONAME_UNKNOWN;
std::string soname_;
+
+ std::unique_ptr<DwarfSection> eh_frame_;
+ std::unique_ptr<DwarfSection> debug_frame_;
+
+ std::vector<Symbols*> symbols_;
};
class ElfInterface32 : public ElfInterface {
@@ -116,15 +140,18 @@
return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
}
- void InitHeaders() override {
- }
+ void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
bool GetSoname(std::string* soname) override {
return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
}
- bool GetFunctionName(uint64_t, std::string*, uint64_t*) override {
- return false;
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+ return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
+ }
+
+ static void GetMaxSize(Memory* memory, uint64_t* size) {
+ GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
}
};
@@ -137,16 +164,21 @@
return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
}
- void InitHeaders() override {
- }
+ void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
bool GetSoname(std::string* soname) override {
return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
}
- bool GetFunctionName(uint64_t, std::string*, uint64_t*) override {
- return false;
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+ return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
+ }
+
+ static void GetMaxSize(Memory* memory, uint64_t* size) {
+ GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
}
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_ELF_INTERFACE_H
diff --git a/libunwindstack/Log.h b/libunwindstack/include/unwindstack/Log.h
similarity index 93%
rename from libunwindstack/Log.h
rename to libunwindstack/include/unwindstack/Log.h
index 2d01aa8..aa1219c 100644
--- a/libunwindstack/Log.h
+++ b/libunwindstack/include/unwindstack/Log.h
@@ -19,7 +19,11 @@
#include <stdint.h>
+namespace unwindstack {
+
void log_to_stdout(bool enable);
void log(uint8_t indent, const char* format, ...);
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_LOG_H
diff --git a/libunwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
similarity index 79%
rename from libunwindstack/MapInfo.h
rename to libunwindstack/include/unwindstack/MapInfo.h
index 8342904..f108766 100644
--- a/libunwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -21,6 +21,8 @@
#include <string>
+namespace unwindstack {
+
// Forward declarations.
class Elf;
class Memory;
@@ -38,7 +40,15 @@
// instead of a portion of the file.
uint64_t elf_offset;
- Memory* CreateMemory(pid_t pid);
+ // This function guarantees it will never return nullptr.
+ Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
+
+ private:
+ Memory* GetFileMemory();
+
+ Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
new file mode 100644
index 0000000..22122a9
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 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_MAPS_H
+#define _LIBUNWINDSTACK_MAPS_H
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <unwindstack/MapInfo.h>
+
+namespace unwindstack {
+
+// Special flag to indicate a map is in /dev/. However, a map in
+// /dev/ashmem/... does not set this flag.
+static constexpr int MAPS_FLAGS_DEVICE_MAP = 0x8000;
+
+class Maps {
+ public:
+ Maps() = default;
+ virtual ~Maps();
+
+ MapInfo* Find(uint64_t pc);
+
+ virtual bool Parse();
+
+ virtual const std::string GetMapsFile() const { return ""; }
+
+ typedef std::vector<MapInfo>::iterator iterator;
+ iterator begin() { return maps_.begin(); }
+ iterator end() { return maps_.end(); }
+
+ typedef std::vector<MapInfo>::const_iterator const_iterator;
+ const_iterator begin() const { return maps_.begin(); }
+ const_iterator end() const { return maps_.end(); }
+
+ size_t Total() { return maps_.size(); }
+
+ MapInfo* Get(size_t index) {
+ if (index >= maps_.size()) return nullptr;
+ return &maps_[index];
+ }
+
+ protected:
+ std::vector<MapInfo> maps_;
+};
+
+class RemoteMaps : public Maps {
+ public:
+ RemoteMaps(pid_t pid) : pid_(pid) {}
+ virtual ~RemoteMaps() = default;
+
+ virtual const std::string GetMapsFile() const override;
+
+ private:
+ pid_t pid_;
+};
+
+class LocalMaps : public RemoteMaps {
+ public:
+ LocalMaps() : RemoteMaps(getpid()) {}
+ virtual ~LocalMaps() = default;
+};
+
+class BufferMaps : public Maps {
+ public:
+ BufferMaps(const char* buffer) : buffer_(buffer) {}
+ virtual ~BufferMaps() = default;
+
+ bool Parse() override;
+
+ private:
+ const char* buffer_;
+};
+
+class FileMaps : public Maps {
+ public:
+ FileMaps(const std::string& file) : file_(file) {}
+ virtual ~FileMaps() = default;
+
+ const std::string GetMapsFile() const override { return file_; }
+
+ protected:
+ 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/Memory.h b/libunwindstack/include/unwindstack/Memory.h
similarity index 70%
rename from libunwindstack/Memory.h
rename to libunwindstack/include/unwindstack/Memory.h
index c5316a1..183b899 100644
--- a/libunwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -21,30 +21,38 @@
#include <sys/types.h>
#include <unistd.h>
+#include <memory>
#include <string>
#include <vector>
+namespace unwindstack {
+
class Memory {
public:
Memory() = default;
virtual ~Memory() = default;
+ static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid);
+
virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;
- inline bool Read(uint64_t addr, void* start, void* field, size_t size) {
- return Read(addr + reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start),
- field, size);
+ inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) {
+ if (reinterpret_cast<uintptr_t>(field) < reinterpret_cast<uintptr_t>(start)) {
+ return false;
+ }
+ uint64_t offset = reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start);
+ if (__builtin_add_overflow(addr, offset, &offset)) {
+ return false;
+ }
+ // The read will check if offset + size overflows.
+ return Read(offset, field, size);
}
- inline bool Read32(uint64_t addr, uint32_t* dst) {
- return Read(addr, dst, sizeof(uint32_t));
- }
+ inline bool Read32(uint64_t addr, uint32_t* dst) { return Read(addr, dst, sizeof(uint32_t)); }
- inline bool Read64(uint64_t addr, uint64_t* dst) {
- return Read(addr, dst, sizeof(uint64_t));
- }
+ inline bool Read64(uint64_t addr, uint64_t* dst) { return Read(addr, dst, sizeof(uint64_t)); }
};
class MemoryBuffer : public Memory {
@@ -103,6 +111,9 @@
pid_t pid() { return pid_; }
+ protected:
+ virtual bool PtraceRead(uint64_t addr, long* value);
+
private:
pid_t pid_;
};
@@ -117,21 +128,17 @@
class MemoryRange : public Memory {
public:
- MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
- : memory_(memory), begin_(begin), length_(end - begin_) {}
- virtual ~MemoryRange() { delete memory_; }
+ MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t end);
+ virtual ~MemoryRange() = default;
- inline bool Read(uint64_t addr, void* dst, size_t size) override {
- if (addr + size <= length_) {
- return memory_->Read(addr + begin_, dst, size);
- }
- return false;
- }
+ bool Read(uint64_t addr, void* dst, size_t size) override;
private:
- Memory* memory_;
+ std::shared_ptr<Memory> memory_;
uint64_t begin_;
uint64_t length_;
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
similarity index 63%
rename from libunwindstack/Regs.h
rename to libunwindstack/include/unwindstack/Regs.h
index 718fc85..ed4d38a 100644
--- a/libunwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -21,9 +21,14 @@
#include <vector>
+namespace unwindstack {
+
// Forward declarations.
class Elf;
struct MapInfo;
+class Memory;
+struct x86_ucontext_t;
+struct x86_64_ucontext_t;
class Regs {
public:
@@ -44,20 +49,27 @@
: total_regs_(total_regs), sp_reg_(sp_reg), return_loc_(return_loc) {}
virtual ~Regs() = default;
+ virtual uint32_t MachineType() = 0;
+
virtual void* RawData() = 0;
virtual uint64_t pc() = 0;
virtual uint64_t sp() = 0;
virtual bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) = 0;
- virtual uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) = 0;
-
virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
+ virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
+
+ virtual void SetFromRaw() = 0;
+
uint16_t sp_reg() { return sp_reg_; }
uint16_t total_regs() { return total_regs_; }
- static Regs* RemoteGet(pid_t pid, uint32_t* machine_type);
+ static uint32_t CurrentMachineType();
+ static Regs* RemoteGet(pid_t pid);
+ static Regs* CreateFromUcontext(uint32_t machine_type, void* ucontext);
+ static Regs* CreateFromLocal();
protected:
uint16_t total_regs_;
@@ -66,13 +78,11 @@
};
template <typename AddressType>
-class RegsTmpl : public Regs {
+class RegsImpl : public Regs {
public:
- RegsTmpl(uint16_t total_regs, uint16_t sp_reg, Location return_loc)
+ RegsImpl(uint16_t total_regs, uint16_t sp_reg, Location return_loc)
: Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {}
- virtual ~RegsTmpl() = default;
-
- uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) override;
+ virtual ~RegsImpl() = default;
bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) override;
@@ -92,36 +102,66 @@
std::vector<AddressType> regs_;
};
-class RegsArm : public RegsTmpl<uint32_t> {
+class RegsArm : public RegsImpl<uint32_t> {
public:
RegsArm();
virtual ~RegsArm() = default;
+ virtual uint32_t MachineType() override final;
+
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
+
+ bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
};
-class RegsArm64 : public RegsTmpl<uint64_t> {
+class RegsArm64 : public RegsImpl<uint64_t> {
public:
RegsArm64();
virtual ~RegsArm64() = default;
+ virtual uint32_t MachineType() override final;
+
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
+
+ bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
};
-class RegsX86 : public RegsTmpl<uint32_t> {
+class RegsX86 : public RegsImpl<uint32_t> {
public:
RegsX86();
virtual ~RegsX86() = default;
+ virtual uint32_t MachineType() override final;
+
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
+
+ bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+ void SetFromUcontext(x86_ucontext_t* ucontext);
};
-class RegsX86_64 : public RegsTmpl<uint64_t> {
+class RegsX86_64 : public RegsImpl<uint64_t> {
public:
RegsX86_64();
virtual ~RegsX86_64() = default;
+ virtual uint32_t MachineType() override final;
+
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
+
+ bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+ void SetFromUcontext(x86_64_ucontext_t* ucontext);
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
new file mode 100644
index 0000000..d1461d8
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 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_REGS_GET_LOCAL_H
+#define _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+
+namespace unwindstack {
+
+#if defined(__arm__)
+
+inline void RegsGetLocal(Regs* regs) {
+ void* reg_data = regs->RawData();
+ asm volatile(
+ ".align 2\n"
+ "bx pc\n"
+ "nop\n"
+ ".code 32\n"
+ "stmia %[base], {r0-r12}\n"
+ "add %[base], #52\n"
+ "mov r1, r13\n"
+ "mov r2, r14\n"
+ "mov r3, r15\n"
+ "stmia %[base], {r1-r3}\n"
+ "orr %[base], pc, #1\n"
+ "bx %[base]\n"
+ : [base] "+r"(reg_data)
+ :
+ : "memory");
+
+ regs->SetFromRaw();
+}
+
+#elif defined(__aarch64__)
+
+inline void RegsGetLocal(Regs* regs) {
+ void* reg_data = regs->RawData();
+ asm volatile(
+ "1:\n"
+ "stp x0, x1, [%[base], #0]\n"
+ "stp x2, x3, [%[base], #16]\n"
+ "stp x4, x5, [%[base], #32]\n"
+ "stp x6, x7, [%[base], #48]\n"
+ "stp x8, x9, [%[base], #64]\n"
+ "stp x10, x11, [%[base], #80]\n"
+ "stp x12, x13, [%[base], #96]\n"
+ "stp x14, x15, [%[base], #112]\n"
+ "stp x16, x17, [%[base], #128]\n"
+ "stp x18, x19, [%[base], #144]\n"
+ "stp x20, x21, [%[base], #160]\n"
+ "stp x22, x23, [%[base], #176]\n"
+ "stp x24, x25, [%[base], #192]\n"
+ "stp x26, x27, [%[base], #208]\n"
+ "stp x28, x29, [%[base], #224]\n"
+ "str x30, [%[base], #240]\n"
+ "mov x12, sp\n"
+ "adr x13, 1b\n"
+ "stp x12, x13, [%[base], #248]\n"
+ : [base] "+r"(reg_data)
+ :
+ : "x12", "x13", "memory");
+
+ regs->SetFromRaw();
+}
+
+#elif defined(__i386__) || defined(__x86_64__)
+
+extern "C" void AsmGetRegs(void* regs);
+
+inline void RegsGetLocal(Regs* regs) {
+ AsmGetRegs(regs->RawData());
+
+ regs->SetFromRaw();
+}
+
+#elif defined(__mips__)
+
+// Stub to allow mips to build.
+void RegsGetLocal(Regs*) {}
+
+#endif
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_REGS_GET_LOCAL_H
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 4fff48e..94cb493 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -23,13 +23,16 @@
#include <gtest/gtest.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Regs.h>
+
#include "ArmExidx.h"
-#include "Regs.h"
-#include "Log.h"
#include "LogFake.h"
#include "MemoryFake.h"
+namespace unwindstack {
+
class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
protected:
void Init(Memory* process_memory = nullptr) {
@@ -1092,3 +1095,5 @@
}
INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging"));
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
index aed75bf..caad131 100644
--- a/libunwindstack/tests/ArmExidxExtractTest.cpp
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -21,12 +21,15 @@
#include <gtest/gtest.h>
+#include <unwindstack/Log.h>
+
#include "ArmExidx.h"
-#include "Log.h"
#include "LogFake.h"
#include "MemoryFake.h"
+namespace unwindstack {
+
class ArmExidxExtractTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -329,3 +332,5 @@
ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
ASSERT_EQ("4 unwind Raw Data: 0x11 0x22 0x33 0xb0\n", GetFakeLogPrint());
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
new file mode 100644
index 0000000..b17ca33
--- /dev/null
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -0,0 +1,819 @@
+/*
+ * Copyright (C) 2016 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 <memory>
+#include <type_traits>
+#include <unordered_map>
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+
+#include "DwarfCfa.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class DwarfCfaLogTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.Clear();
+
+ dmem_.reset(new DwarfMemory(&memory_));
+
+ cie_.cfa_instructions_offset = 0x1000;
+ cie_.cfa_instructions_end = 0x1030;
+ // These two values should be different to distinguish between
+ // operations that deal with code versus data.
+ cie_.code_alignment_factor = 4;
+ cie_.data_alignment_factor = 8;
+
+ fde_.cfa_instructions_offset = 0x2000;
+ fde_.cfa_instructions_end = 0x2030;
+ fde_.pc_start = 0x2000;
+ fde_.pc_end = 0x2000;
+ fde_.pc_end = 0x10000;
+ fde_.cie = &cie_;
+ cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+ }
+
+ MemoryFake memory_;
+ std::unique_ptr<DwarfMemory> dmem_;
+ std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
+ DwarfCie cie_;
+ DwarfFde fde_;
+};
+TYPED_TEST_CASE_P(DwarfCfaLogTest);
+
+// NOTE: All class variable references have to be prefaced with this->.
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_illegal) {
+ for (uint8_t i = 0x17; i < 0x3f; i++) {
+ if (i == 0x2e || i == 0x2f) {
+ // Skip gnu extension ops.
+ continue;
+ }
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ std::string expected = "4 unwind Illegal\n";
+ expected += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", i);
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+ }
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_nop) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ std::string expected =
+ "4 unwind DW_CFA_nop\n"
+ "4 unwind Raw Data: 0x00\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+ std::string expected =
+ "4 unwind DW_CFA_offset register(3) 4\n"
+ "4 unwind Raw Data: 0x83 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+ expected =
+ "4 unwind DW_CFA_offset register(3) 132\n"
+ "4 unwind Raw Data: 0x83 0x84 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+ std::string expected =
+ "4 unwind DW_CFA_offset_extended register(3) 2\n"
+ "4 unwind Raw Data: 0x05 0x03 0x02\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+ expected =
+ "4 unwind DW_CFA_offset_extended register(129) 2306\n"
+ "4 unwind Raw Data: 0x05 0x81 0x01 0x82 0x12\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended_sf) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+ std::string expected =
+ "4 unwind DW_CFA_offset_extended_sf register(5) 16\n"
+ "4 unwind Raw Data: 0x11 0x05 0x10\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check a negative value for the offset.
+ ResetLogs();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+ expected =
+ "4 unwind DW_CFA_offset_extended_sf register(134) -1\n"
+ "4 unwind Raw Data: 0x11 0x86 0x01 0xff 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_restore) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ std::string expected =
+ "4 unwind DW_CFA_restore register(2)\n"
+ "4 unwind Raw Data: 0xc2\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3003));
+ expected =
+ "4 unwind DW_CFA_offset register(2) 4\n"
+ "4 unwind Raw Data: 0x82 0x04\n"
+ "4 unwind DW_CFA_restore register(2)\n"
+ "4 unwind Raw Data: 0xc2\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_restore_extended) {
+ this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4000, 0x4002));
+ std::string expected =
+ "4 unwind DW_CFA_restore_extended register(8)\n"
+ "4 unwind Raw Data: 0x06 0x08\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5007));
+ expected =
+ "4 unwind DW_CFA_offset_extended register(258) 4\n"
+ "4 unwind Raw Data: 0x05 0x82 0x02 0x04\n"
+ "4 unwind DW_CFA_restore_extended register(258)\n"
+ "4 unwind Raw Data: 0x06 0x82 0x02\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_set_loc) {
+ uint8_t buffer[1 + sizeof(TypeParam)];
+ buffer[0] = 0x1;
+ TypeParam address;
+ std::string raw_data("Raw Data: 0x01 ");
+ std::string address_str;
+ if (std::is_same<TypeParam, uint32_t>::value) {
+ address = 0x81234578U;
+ address_str = "0x81234578";
+ raw_data += "0x78 0x45 0x23 0x81";
+ } else {
+ address = 0x8123456712345678ULL;
+ address_str = "0x8123456712345678";
+ raw_data += "0x78 0x56 0x34 0x12 0x67 0x45 0x23 0x81";
+ }
+ memcpy(&buffer[1], &address, sizeof(address));
+
+ this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
+ ResetLogs();
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+ std::string expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
+ expected += "4 unwind " + raw_data + "\n";
+ expected += "4 unwind \n";
+ expected += "4 unwind PC " + address_str + "\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check for a set going back.
+ ResetLogs();
+ this->fde_.pc_start = address + 0x10;
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+ expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
+ expected += "4 unwind " + raw_data + "\n";
+ expected += "4 unwind \n";
+ expected += "4 unwind PC " + address_str + "\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc) {
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x44});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x201));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc 4\n"
+ "4 unwind Raw Data: 0x44\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2010\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x200, 0x201));
+ expected =
+ "4 unwind DW_CFA_advance_loc 4\n"
+ "4 unwind Raw Data: 0x44\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2110\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc1) {
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x202));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc1 4\n"
+ "4 unwind Raw Data: 0x02 0x04\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2004\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x10, 0x200, 0x202));
+ expected =
+ "4 unwind DW_CFA_advance_loc1 4\n"
+ "4 unwind Raw Data: 0x02 0x04\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2014\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc2) {
+ this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x600, 0x603));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc2 772\n"
+ "4 unwind Raw Data: 0x03 0x04 0x03\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1000, 0x600, 0x603));
+ expected =
+ "4 unwind DW_CFA_advance_loc2 772\n"
+ "4 unwind Raw Data: 0x03 0x04 0x03\n"
+ "4 unwind \n"
+ "4 unwind PC 0x3304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc4) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x505));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc4 16909060\n"
+ "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
+ "4 unwind \n"
+ "4 unwind PC 0x1022304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x500, 0x505));
+ expected =
+ "4 unwind DW_CFA_advance_loc4 16909060\n"
+ "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
+ "4 unwind \n"
+ "4 unwind PC 0x1024304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_undefined) {
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa02));
+ std::string expected =
+ "4 unwind DW_CFA_undefined register(9)\n"
+ "4 unwind Raw Data: 0x07 0x09\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ dwarf_loc_regs_t cie_loc_regs;
+ this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1a00, 0x1a03));
+ expected =
+ "4 unwind DW_CFA_undefined register(129)\n"
+ "4 unwind Raw Data: 0x07 0x81 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_same) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ std::string expected =
+ "4 unwind DW_CFA_same_value register(127)\n"
+ "4 unwind Raw Data: 0x08 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+ expected =
+ "4 unwind DW_CFA_same_value register(255)\n"
+ "4 unwind Raw Data: 0x08 0xff 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_register) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x303));
+ std::string expected =
+ "4 unwind DW_CFA_register register(2) register(1)\n"
+ "4 unwind Raw Data: 0x09 0x02 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4305));
+ expected =
+ "4 unwind DW_CFA_register register(255) register(511)\n"
+ "4 unwind Raw Data: 0x09 0xff 0x01 0xff 0x03\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_state) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x301));
+
+ std::string expected =
+ "4 unwind DW_CFA_remember_state\n"
+ "4 unwind Raw Data: 0x0a\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4301));
+
+ expected =
+ "4 unwind DW_CFA_restore_state\n"
+ "4 unwind Raw Data: 0x0b\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_state_cfa_offset_restore) {
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3004));
+
+ std::string expected =
+ "4 unwind DW_CFA_remember_state\n"
+ "4 unwind Raw Data: 0x0a\n"
+ "4 unwind DW_CFA_def_cfa_offset 64\n"
+ "4 unwind Raw Data: 0x0e 0x40\n"
+ "4 unwind DW_CFA_restore_state\n"
+ "4 unwind Raw Data: 0x0b\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa register(127) 116\n"
+ "4 unwind Raw Data: 0x0c 0x7f 0x74\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa register(383) 628\n"
+ "4 unwind Raw Data: 0x0c 0xff 0x02 0xf4 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_sf register(48) 37\n"
+ "4 unwind Raw Data: 0x12 0x30 0x25\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Test a negative value.
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_sf register(163) -6\n"
+ "4 unwind Raw Data: 0x12 0xa3 0x01 0xfa 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_register) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_register register(114)\n"
+ "4 unwind Raw Data: 0x0d 0x72\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_register register(4217)\n"
+ "4 unwind Raw Data: 0x0d 0xf9 0x20\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_offset 89\n"
+ "4 unwind Raw Data: 0x0e 0x59\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset 89\n"
+ "4 unwind Raw Data: 0x0e 0x59\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset 1364\n"
+ "4 unwind Raw Data: 0x0e 0xd4 0x0a\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
+ "4 unwind Raw Data: 0x13 0x23\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
+ "4 unwind Raw Data: 0x13 0x23\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative offset.
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset_sf -10\n"
+ "4 unwind Raw Data: 0x13 0xf6 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x04, 0x05});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x106));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_expression 4\n"
+ "4 unwind Raw Data: 0x0f 0x04 0x01 0x02 0x04 0x05\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x01\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x02\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x04\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x05\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x0f, 0x81, 0x01};
+ expected = "4 unwind Raw Data: 0x0f 0x81 0x01";
+ std::string op_string;
+ for (uint8_t i = 3; i < 132; i++) {
+ ops.push_back(0x05);
+ op_string +=
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x05\n";
+ expected += " 0x05";
+ if (((i + 1) % 10) == 0) {
+ expected += "\n4 unwind Raw Data:";
+ }
+ }
+ expected += '\n';
+ this->memory_.SetMemory(0x200, ops);
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x284));
+
+ expected = "4 unwind DW_CFA_def_cfa_expression 129\n" + expected;
+ ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0xc0, 0xc1});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+
+ std::string expected =
+ "4 unwind DW_CFA_expression register(4) 2\n"
+ "4 unwind Raw Data: 0x10 0x04 0x02 0xc0 0xc1\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xc0\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xc1\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x10, 0xff, 0x01, 0x82, 0x01};
+ expected = "4 unwind Raw Data: 0x10 0xff 0x01 0x82 0x01";
+ std::string op_string;
+ for (uint8_t i = 5; i < 135; i++) {
+ ops.push_back(0xa0 + (i - 5) % 96);
+ op_string += "4 unwind Illegal\n";
+ op_string += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", ops.back());
+ expected += android::base::StringPrintf(" 0x%02x", ops.back());
+ if (((i + 1) % 10) == 0) {
+ expected += "\n4 unwind Raw Data:";
+ }
+ }
+ expected = "4 unwind DW_CFA_expression register(255) 130\n" + expected + "\n";
+
+ this->memory_.SetMemory(0x200, ops);
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x287));
+
+ ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_val_offset register(69) 84\n"
+ "4 unwind Raw Data: 0x14 0x45 0x54\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x400, 0x405));
+
+ expected =
+ "4 unwind DW_CFA_val_offset register(290) 692\n"
+ "4 unwind Raw Data: 0x14 0xa2 0x02 0xb4 0x05\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_val_offset_sf register(86) 18\n"
+ "4 unwind Raw Data: 0x15 0x56 0x12\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative value.
+ ResetLogs();
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa05));
+
+ expected =
+ "4 unwind DW_CFA_val_offset_sf register(255) -64\n"
+ "4 unwind Raw Data: 0x15 0xff 0x01 0xc0 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0xb0, 0xb1});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+
+ std::string expected =
+ "4 unwind DW_CFA_val_expression register(5) 2\n"
+ "4 unwind Raw Data: 0x16 0x05 0x02 0xb0 0xb1\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xb0\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xb1\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x16, 0x83, 0x10, 0xa8, 0x01};
+ expected = "4 unwind Raw Data: 0x16 0x83 0x10 0xa8 0x01";
+ std::string op_string;
+ for (uint8_t i = 0; i < 168; i++) {
+ ops.push_back(0xa0 + (i % 96));
+ op_string += "4 unwind Illegal\n";
+ op_string += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", ops.back());
+ expected += android::base::StringPrintf(" 0x%02x", ops.back());
+ if (((i + 6) % 10) == 0) {
+ expected += "\n4 unwind Raw Data:";
+ }
+ }
+ expected = "4 unwind DW_CFA_val_expression register(2051) 168\n" + expected + "\n";
+
+ this->memory_.SetMemory(0xa00, ops);
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xaad));
+
+ ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_args_size) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+
+ std::string expected =
+ "4 unwind DW_CFA_GNU_args_size 4\n"
+ "4 unwind Raw Data: 0x2e 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5004));
+
+ expected =
+ "4 unwind DW_CFA_GNU_args_size 65572\n"
+ "4 unwind Raw Data: 0x2e 0xa4 0x80 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_negative_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+
+ std::string expected =
+ "4 unwind DW_CFA_GNU_negative_offset_extended register(8) 16\n"
+ "4 unwind Raw Data: 0x2f 0x08 0x10\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+
+ expected =
+ "4 unwind DW_CFA_GNU_negative_offset_extended register(257) 255\n"
+ "4 unwind Raw Data: 0x2f 0x81 0x02 0xff 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_register_override) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x306));
+
+ std::string expected =
+ "4 unwind DW_CFA_register register(2) register(1)\n"
+ "4 unwind Raw Data: 0x09 0x02 0x01\n"
+ "4 unwind DW_CFA_register register(2) register(4)\n"
+ "4 unwind Raw Data: 0x09 0x02 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+ cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+ cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
+ cfa_undefined, cfa_same, cfa_register, cfa_state,
+ cfa_state_cfa_offset_restore, cfa_def_cfa, cfa_def_cfa_sf,
+ cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
+ cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
+ cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
+ cfa_gnu_negative_offset_extended, cfa_register_override);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
new file mode 100644
index 0000000..73a67ac
--- /dev/null
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -0,0 +1,965 @@
+/*
+ * Copyright (C) 2016 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 <memory>
+#include <unordered_map>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+
+#include "DwarfCfa.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class DwarfCfaTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.Clear();
+
+ dmem_.reset(new DwarfMemory(&memory_));
+
+ cie_.cfa_instructions_offset = 0x1000;
+ cie_.cfa_instructions_end = 0x1030;
+ // These two values should be different to distinguish between
+ // operations that deal with code versus data.
+ cie_.code_alignment_factor = 4;
+ cie_.data_alignment_factor = 8;
+
+ fde_.cfa_instructions_offset = 0x2000;
+ fde_.cfa_instructions_end = 0x2030;
+ fde_.pc_start = 0x2000;
+ fde_.cie = &cie_;
+
+ cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+ }
+
+ MemoryFake memory_;
+ std::unique_ptr<DwarfMemory> dmem_;
+ std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
+ DwarfCie cie_;
+ DwarfFde fde_;
+};
+TYPED_TEST_CASE_P(DwarfCfaTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfCfaTest, cfa_illegal) {
+ for (uint8_t i = 0x17; i < 0x3f; i++) {
+ if (i == 0x2e || i == 0x2f) {
+ // Skip gnu extension ops.
+ continue;
+ }
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->cfa_->last_error());
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+ }
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_nop) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+// This test needs to be examined.
+TYPED_TEST_P(DwarfCfaTest, cfa_offset) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2002, &loc_regs));
+ ASSERT_EQ(0x2002U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(3);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(32U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
+ loc_regs.clear();
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2100, 0x2103, &loc_regs));
+ ASSERT_EQ(0x2103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(3);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(1056U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+ ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(3);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(2U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+ ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(129);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(2306U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_offset_extended_sf) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+ ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(5);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(0x80U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check a negative value for the offset.
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+ ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(134);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-8), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_restore) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ dwarf_loc_regs_t cie_loc_regs;
+ cie_loc_regs[2] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ this->cfa_->set_cie_loc_regs(&cie_loc_regs);
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x3000, 0x3003, &loc_regs));
+ ASSERT_EQ(0x3003U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(2);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_restore_extended) {
+ this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4000, 0x4002, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+ ASSERT_EQ(0x4002U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
+ dwarf_loc_regs_t cie_loc_regs;
+ cie_loc_regs[258] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ this->cfa_->set_cie_loc_regs(&cie_loc_regs);
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x5000, 0x5007, &loc_regs));
+ ASSERT_EQ(0x5007U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(258);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_set_loc) {
+ uint8_t buffer[1 + sizeof(TypeParam)];
+ buffer[0] = 0x1;
+ TypeParam address;
+ std::string raw_data("Raw Data: 0x01 ");
+ std::string address_str;
+ if (sizeof(TypeParam) == 4) {
+ address = 0x81234578U;
+ address_str = "0x81234578";
+ raw_data += "0x78 0x45 0x23 0x81";
+ } else {
+ address = 0x8123456712345678ULL;
+ address_str = "0x8123456712345678";
+ raw_data += "0x78 0x56 0x34 0x12 0x67 0x45 0x23 0x81";
+ }
+ memcpy(&buffer[1], &address, sizeof(address));
+
+ this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
+ ResetLogs();
+ dwarf_loc_regs_t loc_regs;
+ ASSERT_TRUE(
+ this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam), &loc_regs));
+ ASSERT_EQ(0x51 + sizeof(TypeParam), this->dmem_->cur_offset());
+ ASSERT_EQ(address, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check for a set going back.
+ ResetLogs();
+ loc_regs.clear();
+ this->fde_.pc_start = address + 0x10;
+ ASSERT_TRUE(
+ this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam), &loc_regs));
+ ASSERT_EQ(0x51 + sizeof(TypeParam), this->dmem_->cur_offset());
+ ASSERT_EQ(address, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ std::string cur_address_str(address_str);
+ cur_address_str[cur_address_str.size() - 2] = '8';
+ std::string expected = "4 unwind Warning: PC is moving backwards: old " + cur_address_str +
+ " new " + address_str + "\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc1) {
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x202, &loc_regs));
+ ASSERT_EQ(0x202U, this->dmem_->cur_offset());
+ ASSERT_EQ(this->fde_.pc_start + 0x10, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc2) {
+ this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x600, 0x603, &loc_regs));
+ ASSERT_EQ(0x603U, this->dmem_->cur_offset());
+ ASSERT_EQ(this->fde_.pc_start + 0xc10U, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc4) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x505, &loc_regs));
+ ASSERT_EQ(0x505U, this->dmem_->cur_offset());
+ ASSERT_EQ(this->fde_.pc_start + 0x4080c10, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_undefined) {
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xa02, &loc_regs));
+ ASSERT_EQ(0xa02U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(9);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1a00, 0x1a03, &loc_regs));
+ ASSERT_EQ(0x1a03U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(129);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_same) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
+ dwarf_loc_regs_t loc_regs;
+
+ loc_regs[127] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(0U, loc_regs.count(127));
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
+
+ loc_regs[255] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2100, 0x2103, &loc_regs));
+ ASSERT_EQ(0x2103U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(0U, loc_regs.count(255));
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_register) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x303, &loc_regs));
+ ASSERT_EQ(0x303U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(2);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+ ASSERT_EQ(1U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4300, 0x4305, &loc_regs));
+ ASSERT_EQ(0x4305U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(255);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+ ASSERT_EQ(511U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_state) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x301, &loc_regs));
+ ASSERT_EQ(0x301U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4300, 0x4301, &loc_regs));
+ ASSERT_EQ(0x4301U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x85, 0x02, 0x0a, 0x86, 0x04, 0x0b});
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2005, &loc_regs));
+ ASSERT_EQ(0x2005U, this->dmem_->cur_offset());
+ ASSERT_EQ(2U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2006, &loc_regs));
+ ASSERT_EQ(0x2006U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+
+ ResetLogs();
+ this->memory_.SetMemory(
+ 0x6000, std::vector<uint8_t>{0x0a, 0x85, 0x02, 0x0a, 0x86, 0x04, 0x0a, 0x87, 0x01, 0x0a, 0x89,
+ 0x05, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b});
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600c, &loc_regs));
+ ASSERT_EQ(0x600cU, this->dmem_->cur_offset());
+ ASSERT_EQ(4U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(7));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(9));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600d, &loc_regs));
+ ASSERT_EQ(0x600dU, this->dmem_->cur_offset());
+ ASSERT_EQ(3U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(7));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600e, &loc_regs));
+ ASSERT_EQ(0x600eU, this->dmem_->cur_offset());
+ ASSERT_EQ(2U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600f, &loc_regs));
+ ASSERT_EQ(0x600fU, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x6010, &loc_regs));
+ ASSERT_EQ(0x6010U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x6011, &loc_regs));
+ ASSERT_EQ(0x6011U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+}
+
+// This test verifies that the cfa offset is saved and restored properly.
+// Even though the spec is not clear about whether the offset is also
+// restored, the gcc unwinder does, and libunwind does too.
+TYPED_TEST_P(DwarfCfaTest, cfa_state_cfa_offset_restore) {
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
+ dwarf_loc_regs_t loc_regs;
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {5, 100}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x3000, 0x3004, &loc_regs));
+ ASSERT_EQ(0x3004U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(5U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(100U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x7fU, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x74U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x205, &loc_regs));
+ ASSERT_EQ(0x205U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x17fU, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x274U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x30U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x128U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Test a negative value.
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x205, &loc_regs));
+ ASSERT_EQ(0x205U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0xa3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(static_cast<uint64_t>(-48), loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_register) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
+ dwarf_loc_regs_t loc_regs;
+
+ // This fails because the cfa is not defined as a register.
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+
+ ASSERT_EQ("4 unwind Attempt to set new register, but cfa is not already set to a register.\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3, 20}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x72U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(20U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3, 60}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+ ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x1079U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(60U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
+ dwarf_loc_regs_t loc_regs;
+
+ // This fails because the cfa is not defined as a register.
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+
+ ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x59U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+ ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x554U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
+ dwarf_loc_regs_t loc_regs;
+
+ // This fails because the cfa is not defined as a register.
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+
+ ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x118U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative offset.
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+ ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(static_cast<TypeParam>(-80), static_cast<TypeParam>(loc_regs[CFA_REG].values[1]));
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x03, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x106, &loc_regs));
+ ASSERT_EQ(0x106U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x0f, 0x81, 0x01};
+ for (uint8_t i = 3; i < 132; i++) {
+ ops.push_back(i - 1);
+ }
+ this->memory_.SetMemory(0x200, ops);
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x284, &loc_regs));
+ ASSERT_EQ(0x284U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0x40, 0x20});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x105, &loc_regs));
+ ASSERT_EQ(0x105U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(4);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_EXPRESSION, location->second.type);
+ ASSERT_EQ(2U, location->second.values[0]);
+ ASSERT_EQ(0x105U, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x10, 0xff, 0x01, 0x82, 0x01};
+ for (uint8_t i = 5; i < 135; i++) {
+ ops.push_back(i - 4);
+ }
+
+ this->memory_.SetMemory(0x200, ops);
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x287, &loc_regs));
+ ASSERT_EQ(0x287U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(255);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_EXPRESSION, location->second.type);
+ ASSERT_EQ(130U, location->second.values[0]);
+ ASSERT_EQ(0x287U, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(69);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(0x2a0U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x400, 0x405, &loc_regs));
+ ASSERT_EQ(0x405U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(290);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(0x15a0U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(86);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(0x90U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative value.
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xa05, &loc_regs));
+ ASSERT_EQ(0xa05U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(255);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-512), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0x10, 0x20});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x105, &loc_regs));
+ ASSERT_EQ(0x105U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(5);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, location->second.type);
+ ASSERT_EQ(2U, location->second.values[0]);
+ ASSERT_EQ(0x105U, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x16, 0x83, 0x10, 0xa8, 0x01};
+ for (uint8_t i = 0; i < 168; i++) {
+ ops.push_back(i);
+ }
+
+ this->memory_.SetMemory(0xa00, ops);
+ loc_regs.clear();
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xaad, &loc_regs));
+ ASSERT_EQ(0xaadU, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(2051);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, location->second.type);
+ ASSERT_EQ(168U, location->second.values[0]);
+ ASSERT_EQ(0xaadU, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_gnu_args_size) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2002, &loc_regs));
+ ASSERT_EQ(0x2002U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x5000, 0x5004, &loc_regs));
+ ASSERT_EQ(0x5004U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_gnu_negative_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+ ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(8);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-16), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+ ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(257);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-255), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_register_override) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x306, &loc_regs));
+ ASSERT_EQ(0x306U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(2);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+ ASSERT_EQ(4U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+ cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+ cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
+ cfa_same, cfa_register, cfa_state, cfa_state_cfa_offset_restore,
+ cfa_def_cfa, cfa_def_cfa_sf, cfa_def_cfa_register, cfa_def_cfa_offset,
+ cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
+ cfa_val_offset, cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
+ cfa_gnu_negative_offset_extended, cfa_register_override);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
new file mode 100644
index 0000000..69813e5
--- /dev/null
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2016 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class MockDwarfDebugFrame : public DwarfDebugFrame<TypeParam> {
+ public:
+ MockDwarfDebugFrame(Memory* memory) : DwarfDebugFrame<TypeParam>(memory) {}
+ ~MockDwarfDebugFrame() = default;
+
+ void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
+ void TestSetOffset(uint64_t offset) { this->offset_ = offset; }
+ void TestSetEndOffset(uint64_t offset) { this->end_offset_ = offset; }
+ void TestPushFdeInfo(const typename DwarfDebugFrame<TypeParam>::FdeInfo& info) {
+ this->fdes_.push_back(info);
+ }
+
+ uint64_t TestGetFdeCount() { return this->fde_count_; }
+ uint8_t TestGetOffset() { return this->offset_; }
+ uint8_t TestGetEndOffset() { return this->end_offset_; }
+ void TestGetFdeInfo(size_t index, typename DwarfDebugFrame<TypeParam>::FdeInfo* info) {
+ *info = this->fdes_[index];
+ }
+};
+
+template <typename TypeParam>
+class DwarfDebugFrameTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ debug_frame_ = new MockDwarfDebugFrame<TypeParam>(&memory_);
+ ResetLogs();
+ }
+
+ void TearDown() override { delete debug_frame_; }
+
+ MemoryFake memory_;
+ MockDwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfDebugFrameTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init32) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 1);
+ this->memory_.SetData8(0x5009, '\0');
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0);
+ this->memory_.SetData32(0x5108, 0x1500);
+ this->memory_.SetData32(0x510c, 0x200);
+
+ this->memory_.SetData32(0x5200, 0xfc);
+ this->memory_.SetData32(0x5204, 0);
+ this->memory_.SetData32(0x5208, 0x2500);
+ this->memory_.SetData32(0x520c, 0x300);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0x5300, 0xfc);
+ this->memory_.SetData32(0x5304, 0xffffffff);
+ this->memory_.SetData8(0x5308, 1);
+ this->memory_.SetData8(0x5309, '\0');
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5400, 0xfc);
+ this->memory_.SetData32(0x5404, 0x300);
+ this->memory_.SetData32(0x5408, 0x3500);
+ this->memory_.SetData32(0x540c, 0x400);
+
+ this->memory_.SetData32(0x5500, 0xfc);
+ this->memory_.SetData32(0x5504, 0x300);
+ this->memory_.SetData32(0x5508, 0x4500);
+ this->memory_.SetData32(0x550c, 0x500);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x1500U, info.start);
+ EXPECT_EQ(0x1700U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(1, &info);
+ EXPECT_EQ(0x5200U, info.offset);
+ EXPECT_EQ(0x2500U, info.start);
+ EXPECT_EQ(0x2800U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(2, &info);
+ EXPECT_EQ(0x5400U, info.offset);
+ EXPECT_EQ(0x3500U, info.start);
+ EXPECT_EQ(0x3900U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(3, &info);
+ EXPECT_EQ(0x5500U, info.offset);
+ EXPECT_EQ(0x4500U, info.start);
+ EXPECT_EQ(0x4a00U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init32_fde_not_following_cie) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 1);
+ this->memory_.SetData8(0x5009, '\0');
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0x1000);
+ this->memory_.SetData32(0x5108, 0x1500);
+ this->memory_.SetData32(0x510c, 0x200);
+
+ ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init64) {
+ // CIE 64 information.
+ this->memory_.SetData32(0x5000, 0xffffffff);
+ this->memory_.SetData64(0x5004, 0xf4);
+ this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
+ this->memory_.SetData8(0x5014, 1);
+ this->memory_.SetData8(0x5015, '\0');
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x5100, 0xffffffff);
+ this->memory_.SetData64(0x5104, 0xf4);
+ this->memory_.SetData64(0x510c, 0);
+ this->memory_.SetData64(0x5114, 0x1500);
+ this->memory_.SetData64(0x511c, 0x200);
+
+ this->memory_.SetData32(0x5200, 0xffffffff);
+ this->memory_.SetData64(0x5204, 0xf4);
+ this->memory_.SetData64(0x520c, 0);
+ this->memory_.SetData64(0x5214, 0x2500);
+ this->memory_.SetData64(0x521c, 0x300);
+
+ // CIE 64 information.
+ this->memory_.SetData32(0x5300, 0xffffffff);
+ this->memory_.SetData64(0x5304, 0xf4);
+ this->memory_.SetData64(0x530c, 0xffffffffffffffffULL);
+ this->memory_.SetData8(0x5314, 1);
+ this->memory_.SetData8(0x5315, '\0');
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x5400, 0xffffffff);
+ this->memory_.SetData64(0x5404, 0xf4);
+ this->memory_.SetData64(0x540c, 0x300);
+ this->memory_.SetData64(0x5414, 0x3500);
+ this->memory_.SetData64(0x541c, 0x400);
+
+ this->memory_.SetData32(0x5500, 0xffffffff);
+ this->memory_.SetData64(0x5504, 0xf4);
+ this->memory_.SetData64(0x550c, 0x300);
+ this->memory_.SetData64(0x5514, 0x4500);
+ this->memory_.SetData64(0x551c, 0x500);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x1500U, info.start);
+ EXPECT_EQ(0x1700U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(1, &info);
+ EXPECT_EQ(0x5200U, info.offset);
+ EXPECT_EQ(0x2500U, info.start);
+ EXPECT_EQ(0x2800U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(2, &info);
+ EXPECT_EQ(0x5400U, info.offset);
+ EXPECT_EQ(0x3500U, info.start);
+ EXPECT_EQ(0x3900U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(3, &info);
+ EXPECT_EQ(0x5500U, info.offset);
+ EXPECT_EQ(0x4500U, info.start);
+ EXPECT_EQ(0x4a00U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init64_fde_not_following_cie) {
+ // CIE 64 information.
+ this->memory_.SetData32(0x5000, 0xffffffff);
+ this->memory_.SetData64(0x5004, 0xf4);
+ this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
+ this->memory_.SetData8(0x5014, 1);
+ this->memory_.SetData8(0x5015, '\0');
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x5100, 0xffffffff);
+ this->memory_.SetData64(0x5104, 0xf4);
+ this->memory_.SetData64(0x510c, 0x1000);
+ this->memory_.SetData64(0x5114, 0x1500);
+ this->memory_.SetData64(0x511c, 0x200);
+
+ ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 1);
+ // Augment string.
+ this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'R', 'P', 'L', '\0'});
+ // Code alignment factor.
+ this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x80, 0x00});
+ // Data alignment factor.
+ this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+ // Return address register
+ this->memory_.SetData8(0x5014, 0x84);
+ // Augmentation length
+ this->memory_.SetMemory(0x5015, std::vector<uint8_t>{0x84, 0x00});
+ // R data.
+ this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0);
+ this->memory_.SetData16(0x5108, 0x1500);
+ this->memory_.SetData16(0x510a, 0x200);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+ ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x1500U, info.start);
+ EXPECT_EQ(0x1700U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init_version4) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 4);
+ // Augment string.
+ this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
+ // Address size.
+ this->memory_.SetData8(0x500e, 4);
+ // Segment size.
+ this->memory_.SetData8(0x500f, 0);
+ // Code alignment factor.
+ this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x80, 0x00});
+ // Data alignment factor.
+ this->memory_.SetMemory(0x5012, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+ // Return address register
+ this->memory_.SetMemory(0x5016, std::vector<uint8_t>{0x85, 0x10});
+ // Augmentation length
+ this->memory_.SetMemory(0x5018, std::vector<uint8_t>{0x84, 0x00});
+ // L data.
+ this->memory_.SetData8(0x501a, 0x10);
+ // P data.
+ this->memory_.SetData8(0x501b, DW_EH_PE_udata4);
+ this->memory_.SetData32(0x501c, 0x100);
+ // R data.
+ this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0);
+ this->memory_.SetData16(0x5108, 0x1500);
+ this->memory_.SetData16(0x510a, 0x200);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+ ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x1500U, info.start);
+ EXPECT_EQ(0x1700U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeOffsetFromPc) {
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+ for (size_t i = 0; i < 9; i++) {
+ info.start = 0x1000 * (i + 1);
+ info.end = 0x1000 * (i + 2) - 0x10;
+ info.offset = 0x5000 + i * 0x20;
+ this->debug_frame_->TestPushFdeInfo(info);
+ }
+
+ this->debug_frame_->TestSetFdeCount(0);
+ uint64_t fde_offset;
+ ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+
+ this->debug_frame_->TestSetFdeCount(9);
+ ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+ // Odd number of elements.
+ for (size_t i = 0; i < 9; i++) {
+ TypeParam pc = 0x1000 * (i + 1);
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
+ << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
+ << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+ << "Failed at index " << i;
+ ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+ }
+
+ // Even number of elements.
+ this->debug_frame_->TestSetFdeCount(10);
+ info.start = 0xa000;
+ info.end = 0xaff0;
+ info.offset = 0x5120;
+ this->debug_frame_->TestPushFdeInfo(info);
+
+ for (size_t i = 0; i < 10; i++) {
+ TypeParam pc = 0x1000 * (i + 1);
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
+ << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
+ << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+ << "Failed at index " << i;
+ ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+ }
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde32) {
+ this->debug_frame_->TestSetOffset(0x4000);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0xf000, 0x100);
+ this->memory_.SetData32(0xf004, 0xffffffff);
+ this->memory_.SetData8(0xf008, 0x1);
+ this->memory_.SetData8(0xf009, '\0');
+ this->memory_.SetData8(0xf00a, 4);
+ this->memory_.SetData8(0xf00b, 8);
+ this->memory_.SetData8(0xf00c, 0x20);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x14000, 0x20);
+ this->memory_.SetData32(0x14004, 0xb000);
+ this->memory_.SetData32(0x14008, 0x9000);
+ this->memory_.SetData32(0x1400c, 0x100);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x14000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x9000U, fde->pc_start);
+ EXPECT_EQ(0x9100U, fde->pc_end);
+ EXPECT_EQ(0xf000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde64) {
+ this->debug_frame_->TestSetOffset(0x2000);
+
+ // CIE 64 information.
+ this->memory_.SetData32(0x6000, 0xffffffff);
+ this->memory_.SetData64(0x6004, 0x100);
+ this->memory_.SetData64(0x600c, 0xffffffffffffffffULL);
+ this->memory_.SetData8(0x6014, 0x1);
+ this->memory_.SetData8(0x6015, '\0');
+ this->memory_.SetData8(0x6016, 4);
+ this->memory_.SetData8(0x6017, 8);
+ this->memory_.SetData8(0x6018, 0x20);
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x8000, 0xffffffff);
+ this->memory_.SetData64(0x8004, 0x200);
+ this->memory_.SetData64(0x800c, 0x4000);
+ this->memory_.SetData64(0x8014, 0x5000);
+ this->memory_.SetData64(0x801c, 0x300);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x8000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+ EXPECT_EQ(0x5000U, fde->pc_start);
+ EXPECT_EQ(0x5300U, fde->pc_end);
+ EXPECT_EQ(0x6000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie, Init64,
+ Init64_fde_not_following_cie, Init_version1, Init_version4,
+ GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
new file mode 100644
index 0000000..07159b0
--- /dev/null
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2016 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DwarfEhFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class MockDwarfEhFrame : public DwarfEhFrame<TypeParam> {
+ public:
+ MockDwarfEhFrame(Memory* memory) : DwarfEhFrame<TypeParam>(memory) {}
+ ~MockDwarfEhFrame() = default;
+
+ void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
+ void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
+ void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; }
+ void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; }
+ void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; }
+ void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; }
+
+ void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
+ void TestSetFdeInfo(uint64_t index, const typename DwarfEhFrame<TypeParam>::FdeInfo& info) {
+ this->fde_info_[index] = info;
+ }
+
+ uint8_t TestGetVersion() { return this->version_; }
+ uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; }
+ uint64_t TestGetPtrOffset() { return this->ptr_offset_; }
+ uint8_t TestGetTableEncoding() { return this->table_encoding_; }
+ uint64_t TestGetTableEntrySize() { return this->table_entry_size_; }
+ uint64_t TestGetFdeCount() { return this->fde_count_; }
+ uint64_t TestGetEntriesOffset() { return this->entries_offset_; }
+ uint64_t TestGetEntriesEnd() { return this->entries_end_; }
+ uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; }
+ uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; }
+};
+
+template <typename TypeParam>
+class DwarfEhFrameTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ eh_frame_ = new MockDwarfEhFrame<TypeParam>(&memory_);
+ ResetLogs();
+ }
+
+ void TearDown() override { delete eh_frame_; }
+
+ MemoryFake memory_;
+ MockDwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfEhFrameTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfEhFrameTest, Init) {
+ this->memory_.SetMemory(
+ 0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
+ this->memory_.SetData16(0x1004, 0x500);
+ this->memory_.SetData32(0x1006, 126);
+
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100));
+ EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+ EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
+ EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
+ EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+ EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount());
+ EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
+ EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
+ EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+
+ // Verify an unexpected version will cause a fail.
+ this->memory_.SetData8(0x1000, 0);
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+ ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+ this->memory_.SetData8(0x1000, 2);
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+ ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_expect_cache_fail) {
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+ ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+ ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_pcrel) {
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
+ this->eh_frame_->TestSetEntriesOffset(0x1000);
+ this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x1384U, info->pc);
+ EXPECT_EQ(0x1540U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_datarel) {
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4);
+ this->eh_frame_->TestSetEntriesOffset(0x1000);
+ this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x3344U, info->pc);
+ EXPECT_EQ(0x3500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_cached) {
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+ this->eh_frame_->TestSetEntriesOffset(0x1000);
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x344U, info->pc);
+ EXPECT_EQ(0x500U, info->offset);
+
+ // Clear the memory so that this will fail if it doesn't read cached data.
+ this->memory_.Clear();
+
+ info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x344U, info->pc);
+ EXPECT_EQ(0x500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetBinary_verify) {
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+ this->eh_frame_->TestSetFdeCount(10);
+
+ typename DwarfEhFrame<TypeParam>::FdeInfo info;
+ for (size_t i = 0; i < 10; i++) {
+ info.pc = 0x1000 * (i + 1);
+ info.offset = 0x5000 + i * 0x20;
+ this->eh_frame_->TestSetFdeInfo(i, info);
+ }
+
+ uint64_t fde_offset;
+ EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
+ // Not an error, just not found.
+ ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+ // Even number of elements.
+ for (size_t i = 0; i < 10; i++) {
+ TypeParam pc = 0x1000 * (i + 1);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10)) << "Failed at index "
+ << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10))
+ << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ }
+ // Odd number of elements.
+ for (size_t i = 0; i < 9; i++) {
+ TypeParam pc = 0x1000 * (i + 1);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9)) << "Failed at index "
+ << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9))
+ << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ }
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential) {
+ this->eh_frame_->TestSetFdeCount(10);
+ this->eh_frame_->TestSetEntriesDataOffset(0x100);
+ this->eh_frame_->TestSetEntriesEnd(0x2000);
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ this->memory_.SetData32(0x1048, 0x440);
+ this->memory_.SetData32(0x104c, 0x600);
+
+ // Verify that if entries is zero, that it fails.
+ uint64_t fde_offset;
+ ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
+ this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
+ EXPECT_EQ(0x500U, fde_offset);
+
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
+ EXPECT_EQ(0x600U, fde_offset);
+
+ // Expect that the data is cached so no more memory reads will occur.
+ this->memory_.Clear();
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
+ EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_last_element) {
+ this->eh_frame_->TestSetFdeCount(2);
+ this->eh_frame_->TestSetEntriesDataOffset(0x100);
+ this->eh_frame_->TestSetEntriesEnd(0x2000);
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+ this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ this->memory_.SetData32(0x1048, 0x440);
+ this->memory_.SetData32(0x104c, 0x600);
+
+ uint64_t fde_offset;
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+ EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_end_check) {
+ this->eh_frame_->TestSetFdeCount(2);
+ this->eh_frame_->TestSetEntriesDataOffset(0x100);
+ this->eh_frame_->TestSetEntriesEnd(0x1048);
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ this->memory_.SetData32(0x1048, 0x440);
+ this->memory_.SetData32(0x104c, 0x600);
+
+ uint64_t fde_offset;
+ ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_fail_fde_count) {
+ this->eh_frame_->TestSetFdeCount(0);
+
+ uint64_t fde_offset;
+ ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_binary_search) {
+ this->eh_frame_->TestSetTableEntrySize(16);
+ this->eh_frame_->TestSetFdeCount(10);
+
+ typename DwarfEhFrame<TypeParam>::FdeInfo info;
+ info.pc = 0x550;
+ info.offset = 0x10500;
+ this->eh_frame_->TestSetFdeInfo(5, info);
+ info.pc = 0x750;
+ info.offset = 0x10700;
+ this->eh_frame_->TestSetFdeInfo(7, info);
+ info.pc = 0x850;
+ info.offset = 0x10800;
+ this->eh_frame_->TestSetFdeInfo(8, info);
+
+ uint64_t fde_offset;
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x800, &fde_offset));
+ EXPECT_EQ(0x10700U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_sequential_search) {
+ this->eh_frame_->TestSetFdeCount(10);
+ this->eh_frame_->TestSetTableEntrySize(0);
+
+ typename DwarfEhFrame<TypeParam>::FdeInfo info;
+ info.pc = 0x50;
+ info.offset = 0x10000;
+ this->eh_frame_->TestSetFdeInfo(0, info);
+ info.pc = 0x150;
+ info.offset = 0x10100;
+ this->eh_frame_->TestSetFdeInfo(1, info);
+ info.pc = 0x250;
+ info.offset = 0x10200;
+ this->eh_frame_->TestSetFdeInfo(2, info);
+
+ uint64_t fde_offset;
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset));
+ EXPECT_EQ(0x10100U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) {
+ // CIE 32 information.
+ this->memory_.SetData32(0xf000, 0x100);
+ this->memory_.SetData32(0xf004, 0);
+ this->memory_.SetData8(0xf008, 0x1);
+ this->memory_.SetData8(0xf009, '\0');
+ this->memory_.SetData8(0xf00a, 4);
+ this->memory_.SetData8(0xf00b, 8);
+ this->memory_.SetData8(0xf00c, 0x20);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x14000, 0x20);
+ this->memory_.SetData32(0x14004, 0x5004);
+ this->memory_.SetData32(0x14008, 0x9000);
+ this->memory_.SetData32(0x1400c, 0x100);
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x14000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x1d00cU, fde->pc_start);
+ EXPECT_EQ(0x1d10cU, fde->pc_end);
+ EXPECT_EQ(0xf000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) {
+ // CIE 64 information.
+ this->memory_.SetData32(0x6000, 0xffffffff);
+ this->memory_.SetData64(0x6004, 0x100);
+ this->memory_.SetData64(0x600c, 0);
+ this->memory_.SetData8(0x6014, 0x1);
+ this->memory_.SetData8(0x6015, '\0');
+ this->memory_.SetData8(0x6016, 4);
+ this->memory_.SetData8(0x6017, 8);
+ this->memory_.SetData8(0x6018, 0x20);
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x8000, 0xffffffff);
+ this->memory_.SetData64(0x8004, 0x200);
+ this->memory_.SetData64(0x800c, 0x200c);
+ this->memory_.SetData64(0x8014, 0x5000);
+ this->memory_.SetData64(0x801c, 0x300);
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x8000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+ EXPECT_EQ(0xd01cU, fde->pc_start);
+ EXPECT_EQ(0xd31cU, fde->pc_end);
+ EXPECT_EQ(0x6000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init, GetFdeInfoFromIndex_expect_cache_fail,
+ GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel,
+ GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify,
+ GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
+ GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
+ GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
+ GetCieFde32, GetCieFde64);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
new file mode 100644
index 0000000..f12d2fe
--- /dev/null
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -0,0 +1,522 @@
+/*
+ * 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 <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfMemory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class DwarfMemoryTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ dwarf_mem_.reset(new DwarfMemory(&memory_));
+ }
+
+ template <typename AddressType>
+ void GetEncodedSizeTest(uint8_t value, size_t expected);
+ template <typename AddressType>
+ void ReadEncodedValue_omit();
+ template <typename AddressType>
+ void ReadEncodedValue_leb128();
+ template <typename AddressType>
+ void ReadEncodedValue_data1();
+ template <typename AddressType>
+ void ReadEncodedValue_data2();
+ template <typename AddressType>
+ void ReadEncodedValue_data4();
+ template <typename AddressType>
+ void ReadEncodedValue_data8();
+ template <typename AddressType>
+ void ReadEncodedValue_non_zero_adjust();
+ template <typename AddressType>
+ void ReadEncodedValue_overflow();
+ template <typename AddressType>
+ void ReadEncodedValue_high_bit_set();
+
+ MemoryFake memory_;
+ std::unique_ptr<DwarfMemory> dwarf_mem_;
+};
+
+TEST_F(DwarfMemoryTest, ReadBytes) {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x10, 0x18, 0xff, 0xfe});
+
+ uint8_t byte;
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0x10U, byte);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0x18U, byte);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0xffU, byte);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0xfeU, byte);
+ ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+
+ dwarf_mem_->set_cur_offset(2);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0xffU, byte);
+ ASSERT_EQ(3U, dwarf_mem_->cur_offset());
+}
+
+TEST_F(DwarfMemoryTest, ReadSigned_check) {
+ uint64_t value;
+
+ // Signed 8 byte reads.
+ memory_.SetData8(0, static_cast<uint8_t>(-10));
+ memory_.SetData8(1, 200);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int8_t>(&value));
+ ASSERT_EQ(static_cast<int8_t>(-10), static_cast<int8_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int8_t>(&value));
+ ASSERT_EQ(static_cast<int8_t>(200), static_cast<int8_t>(value));
+
+ // Signed 16 byte reads.
+ memory_.SetData16(0x10, static_cast<uint16_t>(-1000));
+ memory_.SetData16(0x12, 50100);
+ dwarf_mem_->set_cur_offset(0x10);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int16_t>(&value));
+ ASSERT_EQ(static_cast<int16_t>(-1000), static_cast<int16_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int16_t>(&value));
+ ASSERT_EQ(static_cast<int16_t>(50100), static_cast<int16_t>(value));
+
+ // Signed 32 byte reads.
+ memory_.SetData32(0x100, static_cast<uint32_t>(-1000000000));
+ memory_.SetData32(0x104, 3000000000);
+ dwarf_mem_->set_cur_offset(0x100);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int32_t>(&value));
+ ASSERT_EQ(static_cast<int32_t>(-1000000000), static_cast<int32_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int32_t>(&value));
+ ASSERT_EQ(static_cast<int32_t>(3000000000), static_cast<int32_t>(value));
+
+ // Signed 64 byte reads.
+ memory_.SetData64(0x200, static_cast<uint64_t>(-2000000000000LL));
+ memory_.SetData64(0x208, 5000000000000LL);
+ dwarf_mem_->set_cur_offset(0x200);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int64_t>(&value));
+ ASSERT_EQ(static_cast<int64_t>(-2000000000000), static_cast<int64_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int64_t>(&value));
+ ASSERT_EQ(static_cast<int64_t>(5000000000000), static_cast<int64_t>(value));
+}
+
+TEST_F(DwarfMemoryTest, ReadULEB128) {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x01, 0x80, 0x24, 0xff, 0xc3, 0xff, 0x7f});
+
+ uint64_t value;
+ ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+ ASSERT_EQ(1U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(1U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+ ASSERT_EQ(3U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x1200U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+ ASSERT_EQ(7U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xfffe1ffU, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadSLEB128) {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x06, 0x40, 0x82, 0x34, 0x89, 0x64, 0xf9, 0xc3, 0x8f,
+ 0x2f, 0xbf, 0xc3, 0xf7, 0x5f});
+
+ int64_t value;
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(1U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(6U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(2U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xffffffffffffffc0ULL, static_cast<uint64_t>(value));
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x1a02U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(6U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xfffffffffffff209ULL, static_cast<uint64_t>(value));
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(10U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x5e3e1f9U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(14U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xfffffffffbfde1bfULL, static_cast<uint64_t>(value));
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::GetEncodedSizeTest(uint8_t value, size_t expected) {
+ for (size_t i = 0; i < 16; i++) {
+ uint8_t encoding = (i << 4) | value;
+ ASSERT_EQ(expected, dwarf_mem_->GetEncodedSize<AddressType>(encoding))
+ << "encoding 0x" << std::hex << static_cast<uint32_t>(encoding) << " test value 0x"
+ << static_cast<size_t>(value);
+ }
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint32_t) {
+ GetEncodedSizeTest<uint32_t>(0, sizeof(uint32_t));
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint64_t) {
+ GetEncodedSizeTest<uint64_t>(0, sizeof(uint64_t));
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data1) {
+ // udata1
+ GetEncodedSizeTest<uint32_t>(0x0d, 1);
+ GetEncodedSizeTest<uint64_t>(0x0d, 1);
+
+ // sdata1
+ GetEncodedSizeTest<uint32_t>(0x0e, 1);
+ GetEncodedSizeTest<uint64_t>(0x0e, 1);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data2) {
+ // udata2
+ GetEncodedSizeTest<uint32_t>(0x02, 2);
+ GetEncodedSizeTest<uint64_t>(0x02, 2);
+
+ // sdata2
+ GetEncodedSizeTest<uint32_t>(0x0a, 2);
+ GetEncodedSizeTest<uint64_t>(0x0a, 2);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data4) {
+ // udata4
+ GetEncodedSizeTest<uint32_t>(0x03, 4);
+ GetEncodedSizeTest<uint64_t>(0x03, 4);
+
+ // sdata4
+ GetEncodedSizeTest<uint32_t>(0x0b, 4);
+ GetEncodedSizeTest<uint64_t>(0x0b, 4);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data8) {
+ // udata8
+ GetEncodedSizeTest<uint32_t>(0x04, 8);
+ GetEncodedSizeTest<uint64_t>(0x04, 8);
+
+ // sdata8
+ GetEncodedSizeTest<uint32_t>(0x0c, 8);
+ GetEncodedSizeTest<uint64_t>(0x0c, 8);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_unknown) {
+ GetEncodedSizeTest<uint32_t>(0x01, 0);
+ GetEncodedSizeTest<uint64_t>(0x01, 0);
+
+ GetEncodedSizeTest<uint32_t>(0x09, 0);
+ GetEncodedSizeTest<uint64_t>(0x09, 0);
+
+ GetEncodedSizeTest<uint32_t>(0x0f, 0);
+ GetEncodedSizeTest<uint64_t>(0x0f, 0);
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_omit() {
+ uint64_t value = 123;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0xff, &value));
+ ASSERT_EQ(0U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) {
+ ReadEncodedValue_omit<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) {
+ ReadEncodedValue_omit<uint64_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint32_t) {
+ uint64_t value = 100;
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x00, &value));
+
+ memory_.SetData32(0, 0x12345678);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x00, &value));
+ ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint64_t) {
+ uint64_t value = 100;
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x00, &value));
+
+ memory_.SetData64(0, 0x12345678f1f2f3f4ULL);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x00, &value));
+ ASSERT_EQ(8U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678f1f2f3f4ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint32_t) {
+ uint64_t value = 100;
+ dwarf_mem_->set_cur_offset(1);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x50, &value));
+
+ memory_.SetData32(4, 0x12345678);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x50, &value));
+ ASSERT_EQ(8U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint64_t) {
+ uint64_t value = 100;
+ dwarf_mem_->set_cur_offset(1);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x50, &value));
+
+ memory_.SetData64(8, 0x12345678f1f2f3f4ULL);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x50, &value));
+ ASSERT_EQ(16U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678f1f2f3f4ULL, value);
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_leb128() {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x80, 0x42});
+
+ uint64_t value = 100;
+ // uleb128
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x01, &value));
+ ASSERT_EQ(0x2100U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ // sleb128
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x09, &value));
+ ASSERT_EQ(0xffffffffffffe100ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) {
+ ReadEncodedValue_leb128<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) {
+ ReadEncodedValue_leb128<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data1() {
+ memory_.SetData8(0, 0xe0);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0d, &value));
+ ASSERT_EQ(0xe0U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0e, &value));
+ ASSERT_EQ(0xffffffffffffffe0ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) {
+ ReadEncodedValue_data1<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) {
+ ReadEncodedValue_data1<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data2() {
+ memory_.SetData16(0, 0xe000);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x02, &value));
+ ASSERT_EQ(0xe000U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0a, &value));
+ ASSERT_EQ(0xffffffffffffe000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) {
+ ReadEncodedValue_data2<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) {
+ ReadEncodedValue_data2<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data4() {
+ memory_.SetData32(0, 0xe0000000);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x03, &value));
+ ASSERT_EQ(0xe0000000U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0b, &value));
+ ASSERT_EQ(0xffffffffe0000000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) {
+ ReadEncodedValue_data4<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) {
+ ReadEncodedValue_data4<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data8() {
+ memory_.SetData64(0, 0xe000000000000000ULL);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x04, &value));
+ ASSERT_EQ(0xe000000000000000ULL, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0c, &value));
+ ASSERT_EQ(0xe000000000000000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) {
+ ReadEncodedValue_data8<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) {
+ ReadEncodedValue_data8<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_non_zero_adjust() {
+ memory_.SetData64(0, 0xe000000000000000ULL);
+
+ uint64_t value = 0;
+ dwarf_mem_->set_pc_offset(0x2000);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x14, &value));
+ ASSERT_EQ(0xe000000000002000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint32_t) {
+ ReadEncodedValue_non_zero_adjust<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint64_t) {
+ ReadEncodedValue_non_zero_adjust<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_overflow() {
+ memory_.SetData64(0, 0);
+
+ uint64_t value = 0;
+ dwarf_mem_->set_cur_offset(UINT64_MAX);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<AddressType>(0x50, &value));
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint32_t) {
+ ReadEncodedValue_overflow<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint64_t) {
+ ReadEncodedValue_overflow<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_high_bit_set() {
+ uint64_t value;
+ memory_.SetData32(0, 0x15234);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<AddressType>(0xc3, &value));
+
+ dwarf_mem_->set_func_offset(0x60000);
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0xc3, &value));
+ ASSERT_EQ(0x75234U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_high_bit_set_uint32_t) {
+ ReadEncodedValue_high_bit_set<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_high_bit_set_uint64_t) {
+ ReadEncodedValue_high_bit_set<uint64_t>();
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_absptr) {
+ uint64_t value = 0x1234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x00, &value));
+ ASSERT_EQ(0x1234U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_pcrel) {
+ uint64_t value = 0x1234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+
+ dwarf_mem_->set_pc_offset(0x2000);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+ ASSERT_EQ(0x3234U, value);
+
+ dwarf_mem_->set_pc_offset(static_cast<uint64_t>(-4));
+ value = 0x1234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+ ASSERT_EQ(0x1230U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_textrel) {
+ uint64_t value = 0x8234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+
+ dwarf_mem_->set_text_offset(0x1000);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+ ASSERT_EQ(0x9234U, value);
+
+ dwarf_mem_->set_text_offset(static_cast<uint64_t>(-16));
+ value = 0x8234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+ ASSERT_EQ(0x8224U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_datarel) {
+ uint64_t value = 0xb234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+
+ dwarf_mem_->set_data_offset(0x1200);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+ ASSERT_EQ(0xc434U, value);
+
+ dwarf_mem_->set_data_offset(static_cast<uint64_t>(-256));
+ value = 0xb234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+ ASSERT_EQ(0xb134U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_funcrel) {
+ uint64_t value = 0x15234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+
+ dwarf_mem_->set_func_offset(0x60000);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+ ASSERT_EQ(0x75234U, value);
+
+ dwarf_mem_->set_func_offset(static_cast<uint64_t>(-4096));
+ value = 0x15234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+ ASSERT_EQ(0x14234U, value);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
new file mode 100644
index 0000000..234d1c9
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 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 <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Regs.h>
+
+#include "DwarfError.h"
+#include "DwarfOp.h"
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class DwarfOpLogTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ op_memory_.Clear();
+ regular_memory_.Clear();
+ mem_.reset(new DwarfMemory(&op_memory_));
+ op_.reset(new DwarfOp<TypeParam>(mem_.get(), ®ular_memory_));
+ }
+
+ MemoryFake op_memory_;
+ MemoryFake regular_memory_;
+
+ std::unique_ptr<DwarfMemory> mem_;
+ std::unique_ptr<DwarfOp<TypeParam>> op_;
+};
+TYPED_TEST_CASE_P(DwarfOpLogTest);
+
+TYPED_TEST_P(DwarfOpLogTest, multiple_ops) {
+ // Multi operation opcodes.
+ std::vector<uint8_t> opcode_buffer = {
+ 0x0a, 0x20, 0x10, 0x08, 0x03, 0x12, 0x27,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ std::vector<std::string> lines;
+ this->op_->GetLogInfo(0, opcode_buffer.size(), &lines);
+ std::vector<std::string> expected{
+ "DW_OP_const2u 4128", "Raw Data: 0x0a 0x20 0x10", "DW_OP_const1u 3", "Raw Data: 0x08 0x03",
+ "DW_OP_dup", "Raw Data: 0x12", "DW_OP_xor", "Raw Data: 0x27"};
+ ASSERT_EQ(expected, lines);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfOpLogTest, multiple_ops);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
new file mode 100644
index 0000000..47a40cf
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -0,0 +1,1586 @@
+/*
+ * Copyright (C) 2016 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 <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+
+#include "DwarfError.h"
+#include "DwarfOp.h"
+
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class DwarfOpTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ op_memory_.Clear();
+ regular_memory_.Clear();
+ mem_.reset(new DwarfMemory(&op_memory_));
+ op_.reset(new DwarfOp<TypeParam>(mem_.get(), ®ular_memory_));
+ }
+
+ MemoryFake op_memory_;
+ MemoryFake regular_memory_;
+
+ std::unique_ptr<DwarfMemory> mem_;
+ std::unique_ptr<DwarfOp<TypeParam>> op_;
+};
+TYPED_TEST_CASE_P(DwarfOpTest);
+
+TYPED_TEST_P(DwarfOpTest, decode) {
+ // Memory error.
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+
+ // No error.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
+ this->mem_->set_cur_offset(0);
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+ ASSERT_EQ(0x96U, this->op_->cur_op());
+ ASSERT_EQ(1U, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, eval) {
+ // Memory error.
+ ASSERT_FALSE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+
+ // Register set.
+ // Do this first, to verify that subsequent calls reset the value.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x50});
+ ASSERT_TRUE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+ ASSERT_TRUE(this->op_->is_register());
+ ASSERT_EQ(1U, this->mem_->cur_offset());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ // Multi operation opcodes.
+ std::vector<uint8_t> opcode_buffer = {
+ 0x08, 0x04, 0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Eval(0, 8, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+ ASSERT_FALSE(this->op_->is_register());
+ ASSERT_EQ(8U, this->mem_->cur_offset());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(1U, this->op_->StackAt(0));
+ ASSERT_EQ(2U, this->op_->StackAt(1));
+ ASSERT_EQ(3U, this->op_->StackAt(2));
+ ASSERT_EQ(4U, this->op_->StackAt(3));
+
+ // Infinite loop.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x2f, 0xfd, 0xff});
+ ASSERT_FALSE(this->op_->Eval(0, 4, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->last_error());
+ ASSERT_FALSE(this->op_->is_register());
+ ASSERT_EQ(0U, this->op_->StackSize());
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_opcode) {
+ // Fill the buffer with all of the illegal opcodes.
+ std::vector<uint8_t> opcode_buffer = {0x00, 0x01, 0x02, 0x04, 0x05, 0x07};
+ for (size_t opcode = 0xa0; opcode < 256; opcode++) {
+ opcode_buffer.push_back(opcode);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+ ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_in_version3) {
+ std::vector<uint8_t> opcode_buffer = {0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d};
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ ASSERT_FALSE(this->op_->Decode(2));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+ ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_in_version4) {
+ std::vector<uint8_t> opcode_buffer = {0x9e, 0x9f};
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ ASSERT_FALSE(this->op_->Decode(3));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+ ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, not_implemented) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push values so that any not implemented ops will return the right error.
+ 0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
+ // xderef
+ 0x18,
+ // fbreg
+ 0x91, 0x01,
+ // piece
+ 0x93, 0x01,
+ // xderef_size
+ 0x95, 0x01,
+ // push_object_address
+ 0x97,
+ // call2
+ 0x98, 0x01, 0x02,
+ // call4
+ 0x99, 0x01, 0x02, 0x03, 0x04,
+ // call_ref
+ 0x9a,
+ // form_tls_address
+ 0x9b,
+ // call_frame_cfa
+ 0x9c,
+ // bit_piece
+ 0x9d, 0x01, 0x01,
+ // implicit_value
+ 0x9e, 0x01,
+ // stack_value
+ 0x9f,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // Push the stack values.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+
+ while (this->mem_->cur_offset() < opcode_buffer.size()) {
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->last_error());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_addr) {
+ std::vector<uint8_t> opcode_buffer = {0x03, 0x12, 0x23, 0x34, 0x45};
+ if (sizeof(TypeParam) == 8) {
+ opcode_buffer.push_back(0x56);
+ opcode_buffer.push_back(0x67);
+ opcode_buffer.push_back(0x78);
+ opcode_buffer.push_back(0x89);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x03, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x8978675645342312UL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_deref) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Try a dereference with nothing on the stack.
+ 0x06,
+ // Add an address, then dereference.
+ 0x0a, 0x10, 0x20, 0x06,
+ // Now do another dereference that should fail in memory.
+ 0x06,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+ TypeParam value = 0x12345678;
+ this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x06, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(value, this->op_->StackAt(0));
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_deref_size) {
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x94});
+ TypeParam value = 0x12345678;
+ this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ // Read all byte sizes up to the sizeof the type.
+ for (size_t i = 1; i < sizeof(TypeParam); i++) {
+ this->op_memory_.SetMemory(
+ 0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, static_cast<uint8_t>(i)});
+ ASSERT_TRUE(this->op_->Eval(0, 5, DWARF_VERSION_MAX)) << "Failed at size " << i;
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed at size " << i;
+ ASSERT_EQ(0x94, this->op_->cur_op()) << "Failed at size " << i;
+ TypeParam expected_value = 0;
+ memcpy(&expected_value, &value, i);
+ ASSERT_EQ(expected_value, this->op_->StackAt(0)) << "Failed at size " << i;
+ }
+
+ // Zero byte read.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, 0x00});
+ ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+
+ // Read too many bytes.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, sizeof(TypeParam) + 1});
+ ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+
+ // Force bad memory read.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x40, 0x94, 0x01});
+ ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, const_unsigned) {
+ std::vector<uint8_t> opcode_buffer = {
+ // const1u
+ 0x08, 0x12, 0x08, 0xff,
+ // const2u
+ 0x0a, 0x45, 0x12, 0x0a, 0x00, 0xff,
+ // const4u
+ 0x0c, 0x12, 0x23, 0x34, 0x45, 0x0c, 0x03, 0x02, 0x01, 0xff,
+ // const8u
+ 0x0e, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x0e, 0x87, 0x98, 0xa9, 0xba, 0xcb,
+ 0xdc, 0xed, 0xfe,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // const1u
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x08, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x12U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x08, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0xffU, this->op_->StackAt(0));
+
+ // const2u
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0a, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1245U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0a, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0xff00U, this->op_->StackAt(0));
+
+ // const4u
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0c, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0c, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ ASSERT_EQ(0xff010203U, this->op_->StackAt(0));
+
+ // const8u
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0e, this->op_->cur_op());
+ ASSERT_EQ(7U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x05060708U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x0102030405060708ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0e, this->op_->cur_op());
+ ASSERT_EQ(8U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0xbaa99887UL, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0xfeeddccbbaa99887ULL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_signed) {
+ std::vector<uint8_t> opcode_buffer = {
+ // const1s
+ 0x09, 0x12, 0x09, 0xff,
+ // const2s
+ 0x0b, 0x21, 0x32, 0x0b, 0x08, 0xff,
+ // const4s
+ 0x0d, 0x45, 0x34, 0x23, 0x12, 0x0d, 0x01, 0x02, 0x03, 0xff,
+ // const8s
+ 0x0f, 0x89, 0x78, 0x67, 0x56, 0x45, 0x34, 0x23, 0x12, 0x0f, 0x04, 0x03, 0x02, 0x01, 0xef,
+ 0xef, 0xef, 0xff,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // const1s
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x09, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x12U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x09, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
+
+ // const2s
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0b, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x3221U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0b, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-248), this->op_->StackAt(0));
+
+ // const4s
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0d, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ ASSERT_EQ(0x12233445U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0d, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-16580095), this->op_->StackAt(0));
+
+ // const8s
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0f, this->op_->cur_op());
+ ASSERT_EQ(7U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x56677889ULL, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x1223344556677889ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0f, this->op_->cur_op());
+ ASSERT_EQ(8U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x01020304U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(static_cast<TypeParam>(-4521264810949884LL), this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_uleb) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Single byte ULEB128
+ 0x10, 0x22, 0x10, 0x7f,
+ // Multi byte ULEB128
+ 0x10, 0xa2, 0x22, 0x10, 0xa2, 0x74, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x09, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x79,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // Single byte ULEB128
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x22U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x7fU, this->op_->StackAt(0));
+
+ // Multi byte ULEB128
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1122U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0x3a22U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x79101c305080c101ULL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_sleb) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Single byte SLEB128
+ 0x11, 0x22, 0x11, 0x7f,
+ // Multi byte SLEB128
+ 0x11, 0xa2, 0x22, 0x11, 0xa2, 0x74, 0x11, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x09, 0x11,
+ };
+ if (sizeof(TypeParam) == 4) {
+ opcode_buffer.push_back(0xb8);
+ opcode_buffer.push_back(0xd3);
+ opcode_buffer.push_back(0x63);
+ } else {
+ opcode_buffer.push_back(0x81);
+ opcode_buffer.push_back(0x82);
+ opcode_buffer.push_back(0x83);
+ opcode_buffer.push_back(0x84);
+ opcode_buffer.push_back(0x85);
+ opcode_buffer.push_back(0x86);
+ opcode_buffer.push_back(0x87);
+ opcode_buffer.push_back(0x88);
+ opcode_buffer.push_back(0x79);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // Single byte SLEB128
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x22U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
+
+ // Multi byte SLEB128
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1122U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-1502), this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(static_cast<TypeParam>(-464456), this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(static_cast<TypeParam>(-499868564803501823LL), this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_dup) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Should fail since nothing is on the stack.
+ 0x12,
+ // Push on a value and dup.
+ 0x08, 0x15, 0x12,
+ // Do it again.
+ 0x08, 0x23, 0x12,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x12, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x12, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x15U, this->op_->StackAt(0));
+ ASSERT_EQ(0x15U, this->op_->StackAt(1));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x12, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0x23U, this->op_->StackAt(0));
+ ASSERT_EQ(0x23U, this->op_->StackAt(1));
+ ASSERT_EQ(0x15U, this->op_->StackAt(2));
+ ASSERT_EQ(0x15U, this->op_->StackAt(3));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_drop) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a couple of values.
+ 0x08, 0x10, 0x08, 0x20,
+ // Drop the values.
+ 0x13, 0x13,
+ // Attempt to drop empty stack.
+ 0x13,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x13, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x13, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x13, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_over) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a couple of values.
+ 0x08, 0x1a, 0x08, 0xed,
+ // Copy a value.
+ 0x14,
+ // Remove all but one element.
+ 0x13, 0x13,
+ // Provoke a failure with this opcode.
+ 0x14,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x14, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1aU, this->op_->StackAt(0));
+ ASSERT_EQ(0xedU, this->op_->StackAt(1));
+ ASSERT_EQ(0x1aU, this->op_->StackAt(2));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x14, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_pick) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a few values.
+ 0x08, 0x1a, 0x08, 0xed, 0x08, 0x34,
+ // Copy the value at offset 2.
+ 0x15, 0x01,
+ // Copy the last value in the stack.
+ 0x15, 0x03,
+ // Choose an invalid index.
+ 0x15, 0x10,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x15, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0xedU, this->op_->StackAt(0));
+ ASSERT_EQ(0x34U, this->op_->StackAt(1));
+ ASSERT_EQ(0xedU, this->op_->StackAt(2));
+ ASSERT_EQ(0x1aU, this->op_->StackAt(3));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x15, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ ASSERT_EQ(0x1aU, this->op_->StackAt(0));
+ ASSERT_EQ(0xedU, this->op_->StackAt(1));
+ ASSERT_EQ(0x34U, this->op_->StackAt(2));
+ ASSERT_EQ(0xedU, this->op_->StackAt(3));
+ ASSERT_EQ(0x1aU, this->op_->StackAt(4));
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x15, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_swap) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a couple of values.
+ 0x08, 0x26, 0x08, 0xab,
+ // Swap values.
+ 0x16,
+ // Pop a value to cause a failure.
+ 0x13, 0x16,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0xabU, this->op_->StackAt(0));
+ ASSERT_EQ(0x26U, this->op_->StackAt(1));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x16, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x26U, this->op_->StackAt(0));
+ ASSERT_EQ(0xabU, this->op_->StackAt(1));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x16, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_rot) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Rotate that should cause a failure.
+ 0x17, 0x08, 0x10,
+ // Only 1 value on stack, should fail.
+ 0x17, 0x08, 0x20,
+ // Only 2 values on stack, should fail.
+ 0x17, 0x08, 0x30,
+ // Should rotate properly.
+ 0x17,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x30U, this->op_->StackAt(0));
+ ASSERT_EQ(0x20U, this->op_->StackAt(1));
+ ASSERT_EQ(0x10U, this->op_->StackAt(2));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x17, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x20U, this->op_->StackAt(0));
+ ASSERT_EQ(0x10U, this->op_->StackAt(1));
+ ASSERT_EQ(0x30U, this->op_->StackAt(2));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_abs) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Abs that should fail.
+ 0x19,
+ // A value that is already positive.
+ 0x08, 0x10, 0x19,
+ // A value that is negative.
+ 0x11, 0x7f, 0x19,
+ // A value that is large and negative.
+ 0x11, 0x81, 0x80, 0x80, 0x80,
+ };
+ if (sizeof(TypeParam) == 4) {
+ opcode_buffer.push_back(0x08);
+ } else {
+ opcode_buffer.push_back(0x80);
+ opcode_buffer.push_back(0x80);
+ opcode_buffer.push_back(0x01);
+ }
+ opcode_buffer.push_back(0x19);
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x19, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x19, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x1U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x19, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(2147483647U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(4398046511105UL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_and) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1b,
+ // Push a single value.
+ 0x08, 0x20,
+ // One element stack, and op will fail.
+ 0x1b,
+ // Push another value.
+ 0x08, 0x02, 0x1b,
+ // Push on two negative values.
+ 0x11, 0x7c, 0x11, 0x7f, 0x1b,
+ // Push one negative, one positive.
+ 0x11, 0x10, 0x11, 0x7c, 0x1b,
+ // Divide by zero.
+ 0x11, 0x10, 0x11, 0x00, 0x1b,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ // Two positive values.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1b, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ // Two negative values.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1b, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x04U, this->op_->StackAt(0));
+
+ // One negative value, one positive value.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(4U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1b, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-4), this->op_->StackAt(0));
+
+ // Divide by zero.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(4U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(5U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_div) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1a,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x1a,
+ // Push another value.
+ 0x08, 0xf0, 0x1a,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1a, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x40U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_minus) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1c,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x1c,
+ // Push another value.
+ 0x08, 0x04, 0x1c,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1c, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x44U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_mod) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1d,
+ // Push a single value.
+ 0x08, 0x47,
+ // One element stack, and op will fail.
+ 0x1d,
+ // Push another value.
+ 0x08, 0x04, 0x1d,
+ // Try a mod of zero.
+ 0x08, 0x01, 0x08, 0x00, 0x1d,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1d, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x03U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_mul) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1e,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x1e,
+ // Push another value.
+ 0x08, 0x04, 0x1e,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1e, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x120U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_neg) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1f,
+ // Push a single value.
+ 0x08, 0x48, 0x1f,
+ // Push a negative value.
+ 0x11, 0x7f, 0x1f,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1f, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-72), this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1f, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x01U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_not) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x20,
+ // Push a single value.
+ 0x08, 0x4, 0x20,
+ // Push a negative value.
+ 0x11, 0x7c, 0x20,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x20, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-5), this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x20, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x03U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_or) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x21,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x21,
+ // Push another value.
+ 0x08, 0xf4, 0x21,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x21, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0xfcU, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_plus) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x22,
+ // Push a single value.
+ 0x08, 0xff,
+ // One element stack, and op will fail.
+ 0x22,
+ // Push another value.
+ 0x08, 0xf2, 0x22,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x22, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x1f1U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_plus_uconst) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x23,
+ // Push a single value.
+ 0x08, 0x50, 0x23, 0x80, 0x51,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x23, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x28d0U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shl) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x24,
+ // Push a single value.
+ 0x08, 0x67,
+ // One element stack, and op will fail.
+ 0x24,
+ // Push another value.
+ 0x08, 0x03, 0x24,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x24, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x338U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shr) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x25,
+ // Push a single value.
+ 0x11, 0x70,
+ // One element stack, and op will fail.
+ 0x25,
+ // Push another value.
+ 0x08, 0x03, 0x25,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x25, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x1ffffffeU, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x1ffffffffffffffeULL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shra) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x26,
+ // Push a single value.
+ 0x11, 0x70,
+ // One element stack, and op will fail.
+ 0x26,
+ // Push another value.
+ 0x08, 0x03, 0x26,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x26, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-2), this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_xor) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x27,
+ // Push a single value.
+ 0x08, 0x11,
+ // One element stack, and op will fail.
+ 0x27,
+ // Push another value.
+ 0x08, 0x41, 0x27,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x27, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x50U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_bra) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x28,
+ // Push on a non-zero value with a positive branch.
+ 0x08, 0x11, 0x28, 0x02, 0x01,
+ // Push on a zero value with a positive branch.
+ 0x08, 0x00, 0x28, 0x05, 0x00,
+ // Push on a non-zero value with a negative branch.
+ 0x08, 0x11, 0x28, 0xfc, 0xff,
+ // Push on a zero value with a negative branch.
+ 0x08, 0x00, 0x28, 0xf0, 0xff,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ // Push on a non-zero value with a positive branch.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ uint64_t offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset + 0x102, this->mem_->cur_offset());
+
+ // Push on a zero value with a positive branch.
+ this->mem_->set_cur_offset(offset);
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset - 5, this->mem_->cur_offset());
+
+ // Push on a non-zero value with a negative branch.
+ this->mem_->set_cur_offset(offset);
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset - 4, this->mem_->cur_offset());
+
+ // Push on a zero value with a negative branch.
+ this->mem_->set_cur_offset(offset);
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset + 16, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, compare_opcode_stack_error) {
+ // All of the ops require two stack elements. Loop through all of these
+ // ops with potential errors.
+ std::vector<uint8_t> opcode_buffer = {
+ 0xff, // Place holder for compare op.
+ 0x08, 0x11,
+ 0xff, // Place holder for compare op.
+ };
+
+ for (uint8_t opcode = 0x29; opcode <= 0x2e; opcode++) {
+ opcode_buffer[0] = opcode;
+ opcode_buffer[3] = opcode;
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+ ASSERT_EQ(opcode, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_FALSE(this->op_->Eval(1, 4, DWARF_VERSION_MAX));
+ ASSERT_EQ(opcode, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, compare_opcodes) {
+ // Have three different checks for each compare op:
+ // - Both values the same.
+ // - The first value larger than the second.
+ // - The second value larger than the first.
+ std::vector<uint8_t> opcode_buffer = {
+ // Values the same.
+ 0x08, 0x11, 0x08, 0x11,
+ 0xff, // Placeholder.
+ // First value larger.
+ 0x08, 0x12, 0x08, 0x10,
+ 0xff, // Placeholder.
+ // Second value larger.
+ 0x08, 0x10, 0x08, 0x12,
+ 0xff, // Placeholder.
+ };
+
+ // Opcode followed by the expected values on the stack.
+ std::vector<uint8_t> expected = {
+ 0x29, 1, 0, 0, // eq
+ 0x2a, 1, 1, 0, // ge
+ 0x2b, 0, 1, 0, // gt
+ 0x2c, 1, 0, 1, // le
+ 0x2d, 0, 0, 1, // lt
+ 0x2e, 0, 1, 1, // ne
+ };
+ for (size_t i = 0; i < expected.size(); i += 4) {
+ opcode_buffer[4] = expected[i];
+ opcode_buffer[9] = expected[i];
+ opcode_buffer[14] = expected[i];
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Eval(0, 15, DWARF_VERSION_MAX))
+ << "Op: 0x" << std::hex << static_cast<uint32_t>(expected[i]) << " failed";
+
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(expected[i + 1], this->op_->StackAt(2));
+ ASSERT_EQ(expected[i + 2], this->op_->StackAt(1));
+ ASSERT_EQ(expected[i + 3], this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_skip) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Positive value.
+ 0x2f, 0x10, 0x20,
+ // Negative value.
+ 0x2f, 0xfd, 0xff,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ uint64_t offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x2f, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset + 0x2010, this->mem_->cur_offset());
+
+ this->mem_->set_cur_offset(offset);
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x2f, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset - 3, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_lit) {
+ std::vector<uint8_t> opcode_buffer;
+
+ // Verify every lit opcode.
+ for (uint8_t op = 0x30; op <= 0x4f; op++) {
+ opcode_buffer.push_back(op);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ uint32_t op = opcode_buffer[i];
+ ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x30U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_reg) {
+ std::vector<uint8_t> opcode_buffer;
+
+ // Verify every reg opcode.
+ for (uint8_t op = 0x50; op <= 0x6f; op++) {
+ opcode_buffer.push_back(op);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ uint32_t op = opcode_buffer[i];
+ ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_TRUE(this->op_->is_register()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x50U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_regx) {
+ std::vector<uint8_t> opcode_buffer = {
+ 0x90, 0x02, 0x90, 0x80, 0x15,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x90, this->op_->cur_op());
+ ASSERT_TRUE(this->op_->is_register());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x02U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Eval(2, 5, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x90, this->op_->cur_op());
+ ASSERT_TRUE(this->op_->is_register());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0xa80U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_breg) {
+ std::vector<uint8_t> opcode_buffer;
+
+ // Verify every reg opcode.
+ for (uint8_t op = 0x70; op <= 0x8f; op++) {
+ // Positive value added to register.
+ opcode_buffer.push_back(op);
+ opcode_buffer.push_back(0x12);
+ // Negative value added to register.
+ opcode_buffer.push_back(op);
+ opcode_buffer.push_back(0x7e);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ RegsFake<TypeParam> regs(32, 10);
+ for (size_t i = 0; i < 32; i++) {
+ regs[i] = i + 10;
+ }
+ this->op_->set_regs(®s);
+
+ uint64_t offset = 0;
+ for (uint32_t op = 0x70; op <= 0x8f; op++) {
+ // Positive value added to register.
+ ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
+ << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x70 + 10 + 0x12, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ offset += 2;
+
+ // Negative value added to register.
+ ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
+ << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x70 + 10 - 2, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ offset += 2;
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_breg_invalid_register) {
+ std::vector<uint8_t> opcode_buffer = {
+ 0x7f, 0x12, 0x80, 0x12,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ RegsFake<TypeParam> regs(16, 10);
+ for (size_t i = 0; i < 16; i++) {
+ regs[i] = i + 10;
+ }
+ this->op_->set_regs(®s);
+
+ // Should pass since this references the last regsister.
+ ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x7fU, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x2bU, this->op_->StackAt(0));
+
+ // Should fail since this references a non-existent register.
+ ASSERT_FALSE(this->op_->Eval(2, 4, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_bregx) {
+ std::vector<uint8_t> opcode_buffer = {// Positive value added to register.
+ 0x92, 0x05, 0x20,
+ // Negative value added to register.
+ 0x92, 0x06, 0x80, 0x7e,
+ // Illegal register.
+ 0x92, 0x80, 0x15, 0x80, 0x02};
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ RegsFake<TypeParam> regs(10, 10);
+ regs[5] = 0x45;
+ regs[6] = 0x190;
+ this->op_->set_regs(®s);
+
+ ASSERT_TRUE(this->op_->Eval(0, 3, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x92, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x65U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Eval(3, 7, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x92, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x90U, this->op_->StackAt(0));
+
+ ASSERT_FALSE(this->op_->Eval(7, 12, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_nop) {
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x96, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, illegal_in_version3,
+ illegal_in_version4, not_implemented, op_addr, op_deref, op_deref_size,
+ const_unsigned, const_signed, const_uleb, const_sleb, op_dup, op_drop,
+ op_over, op_pick, op_swap, op_rot, op_abs, op_and, op_div, op_minus,
+ op_mod, op_mul, op_neg, op_not, op_or, op_plus, op_plus_uconst, op_shl,
+ op_shr, op_shra, op_xor, op_bra, compare_opcode_stack_error,
+ compare_opcodes, op_skip, op_lit, op_reg, op_regx, op_breg,
+ op_breg_invalid_register, op_bregx, op_nop);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
new file mode 100644
index 0000000..b871539
--- /dev/null
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -0,0 +1,839 @@
+/*
+ * Copyright (C) 2016 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfSection.h>
+
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class MockDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
+ public:
+ MockDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
+ virtual ~MockDwarfSectionImpl() = default;
+
+ MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+
+ MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
+
+ MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
+
+ MOCK_METHOD1(IsCie32, bool(uint32_t));
+
+ MOCK_METHOD1(IsCie64, bool(uint64_t));
+
+ MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
+
+ MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
+
+ MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
+
+ void TestSetCachedCieEntry(uint64_t offset, const DwarfCie& cie) {
+ this->cie_entries_[offset] = cie;
+ }
+ void TestClearCachedCieEntry() { this->cie_entries_.clear(); }
+
+ void TestSetCachedFdeEntry(uint64_t offset, const DwarfFde& fde) {
+ this->fde_entries_[offset] = fde;
+ }
+ void TestClearCachedFdeEntry() { this->fde_entries_.clear(); }
+
+ void TestSetCachedCieLocRegs(uint64_t offset, const dwarf_loc_regs_t& loc_regs) {
+ this->cie_loc_regs_[offset] = loc_regs;
+ }
+ void TestClearCachedCieLocRegs() { this->cie_loc_regs_.clear(); }
+
+ void TestClearError() { this->last_error_ = DWARF_ERROR_NONE; }
+};
+
+template <typename TypeParam>
+class DwarfSectionImplTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ section_ = new MockDwarfSectionImpl<TypeParam>(&memory_);
+ ResetLogs();
+ }
+
+ void TearDown() override { delete section_; }
+
+ MemoryFake memory_;
+ MockDwarfSectionImpl<TypeParam>* section_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfSectionImplTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ TypeParam cfa_value = 0x12345;
+ this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x12345U, regs.sp());
+ EXPECT_EQ(0x20U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x80000000U, regs.sp());
+ EXPECT_EQ(0x20U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
+ DwarfCie cie{.return_address_register = 60};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+ this->section_->TestClearError();
+ loc_regs.erase(CFA_REG);
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+ this->section_->TestClearError();
+ loc_regs.erase(CFA_REG);
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+ this->section_->TestClearError();
+ loc_regs.erase(CFA_REG);
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {9, 0}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x20U, regs.pc());
+ EXPECT_EQ(0x2000U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[6] = 0x4000;
+ regs[9] = 0x3000;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {6, 0}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x20U, regs.pc());
+ EXPECT_EQ(0x4000U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 0}};
+ loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {10, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ if (sizeof(TypeParam) == sizeof(uint64_t)) {
+ this->memory_.SetData64(0x2150, 0x12345678abcdef00ULL);
+ } else {
+ this->memory_.SetData32(0x2150, 0x12345678);
+ }
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[3] = 0x234;
+ regs[5] = 0x10;
+ regs[8] = 0x2100;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x100, 0}};
+ loc_regs[2] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x50, 0}};
+ loc_regs[3] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x10U, regs.pc());
+ EXPECT_EQ(0x2100U, regs.sp());
+ EXPECT_EQ(0x2200U, regs[1]);
+ EXPECT_EQ(0x234U, regs[3]);
+ if (sizeof(TypeParam) == sizeof(uint64_t)) {
+ EXPECT_EQ(0x12345678abcdef00ULL, regs[2]);
+ } else {
+ EXPECT_EQ(0x12345678U, regs[2]);
+ }
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[5] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0U, regs.pc());
+ EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x20U, regs.pc());
+ EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ // This should not result in any errors.
+ loc_regs[20] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x20U, regs.pc());
+ EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[8] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ TypeParam cfa_value = 0x12345;
+ this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x3000U, regs.sp());
+ EXPECT_EQ(0x12345U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[8] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x3000U, regs.sp());
+ EXPECT_EQ(0x80000000U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x100;
+ regs[8] = 0x2000;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x2000U, regs.sp());
+ EXPECT_EQ(0x100U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_fail_should_not_cache) {
+ ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+ this->section_->TestClearError();
+ ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_32_version_check) {
+ this->memory_.SetData32(0x5000, 0x100);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 0x1);
+ this->memory_.SetData8(0x5009, '\0');
+ this->memory_.SetData8(0x500a, 4);
+ this->memory_.SetData8(0x500b, 8);
+ this->memory_.SetData8(0x500c, 0x20);
+
+ EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+ const DwarfCie* cie = this->section_->GetCie(0x5000);
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(1U, cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+ EXPECT_EQ(0U, cie->segment_size);
+ EXPECT_EQ(1U, cie->augmentation_string.size());
+ EXPECT_EQ('\0', cie->augmentation_string[0]);
+ EXPECT_EQ(0U, cie->personality_handler);
+ EXPECT_EQ(0x500dU, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(8, cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, cie->return_address_register);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->section_->last_error());
+
+ this->section_->TestClearCachedCieEntry();
+ // Set version to 0, 2, 5 and verify we fail.
+ this->memory_.SetData8(0x5008, 0x0);
+ this->section_->TestClearError();
+ ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+
+ this->memory_.SetData8(0x5008, 0x2);
+ this->section_->TestClearError();
+ ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+
+ this->memory_.SetData8(0x5008, 0x5);
+ this->section_->TestClearError();
+ ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_negative_data_alignment_factor) {
+ this->memory_.SetData32(0x5000, 0x100);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 0x1);
+ this->memory_.SetData8(0x5009, '\0');
+ this->memory_.SetData8(0x500a, 4);
+ this->memory_.SetMemory(0x500b, std::vector<uint8_t>{0xfc, 0xff, 0xff, 0xff, 0x7f});
+ this->memory_.SetData8(0x5010, 0x20);
+
+ EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+ const DwarfCie* cie = this->section_->GetCie(0x5000);
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(1U, cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+ EXPECT_EQ(0U, cie->segment_size);
+ EXPECT_EQ(1U, cie->augmentation_string.size());
+ EXPECT_EQ('\0', cie->augmentation_string[0]);
+ EXPECT_EQ(0U, cie->personality_handler);
+ EXPECT_EQ(0x5011U, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(-4, cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_64_no_augment) {
+ this->memory_.SetData32(0x8000, 0xffffffff);
+ this->memory_.SetData64(0x8004, 0x200);
+ this->memory_.SetData64(0x800c, 0xffffffff);
+ this->memory_.SetData8(0x8014, 0x1);
+ this->memory_.SetData8(0x8015, '\0');
+ this->memory_.SetData8(0x8016, 4);
+ this->memory_.SetData8(0x8017, 8);
+ this->memory_.SetData8(0x8018, 0x20);
+
+ EXPECT_CALL(*this->section_, IsCie64(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+ const DwarfCie* cie = this->section_->GetCie(0x8000);
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(1U, cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+ EXPECT_EQ(0U, cie->segment_size);
+ EXPECT_EQ(1U, cie->augmentation_string.size());
+ EXPECT_EQ('\0', cie->augmentation_string[0]);
+ EXPECT_EQ(0U, cie->personality_handler);
+ EXPECT_EQ(0x8019U, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x820cU, cie->cfa_instructions_end);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(8, cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_augment) {
+ this->memory_.SetData32(0x5000, 0x100);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 0x1);
+ this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
+ this->memory_.SetData8(0x500e, 4);
+ this->memory_.SetData8(0x500f, 8);
+ this->memory_.SetData8(0x5010, 0x10);
+ // Augment length.
+ this->memory_.SetData8(0x5011, 0xf);
+ // L data.
+ this->memory_.SetData8(0x5012, DW_EH_PE_textrel | DW_EH_PE_udata2);
+ // P data.
+ this->memory_.SetData8(0x5013, DW_EH_PE_udata4);
+ this->memory_.SetData32(0x5014, 0x12345678);
+ // R data.
+ this->memory_.SetData8(0x5018, DW_EH_PE_udata2);
+
+ EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+ const DwarfCie* cie = this->section_->GetCie(0x5000);
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(1U, cie->version);
+ EXPECT_EQ(DW_EH_PE_udata2, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_textrel | DW_EH_PE_udata2, cie->lsda_encoding);
+ EXPECT_EQ(0U, cie->segment_size);
+ EXPECT_EQ(5U, cie->augmentation_string.size());
+ EXPECT_EQ('z', cie->augmentation_string[0]);
+ EXPECT_EQ('L', cie->augmentation_string[1]);
+ EXPECT_EQ('P', cie->augmentation_string[2]);
+ EXPECT_EQ('R', cie->augmentation_string[3]);
+ EXPECT_EQ('\0', cie->augmentation_string[4]);
+ EXPECT_EQ(0x12345678U, cie->personality_handler);
+ EXPECT_EQ(0x5021U, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(8, cie->data_alignment_factor);
+ EXPECT_EQ(0x10U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_3) {
+ this->memory_.SetData32(0x5000, 0x100);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 0x3);
+ this->memory_.SetData8(0x5009, '\0');
+ this->memory_.SetData8(0x500a, 4);
+ this->memory_.SetData8(0x500b, 8);
+ this->memory_.SetMemory(0x500c, std::vector<uint8_t>{0x81, 0x03});
+
+ EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+ const DwarfCie* cie = this->section_->GetCie(0x5000);
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(3U, cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+ EXPECT_EQ(0U, cie->segment_size);
+ EXPECT_EQ(1U, cie->augmentation_string.size());
+ EXPECT_EQ('\0', cie->augmentation_string[0]);
+ EXPECT_EQ(0U, cie->personality_handler);
+ EXPECT_EQ(0x500eU, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(8, cie->data_alignment_factor);
+ EXPECT_EQ(0x181U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_4) {
+ this->memory_.SetData32(0x5000, 0x100);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 0x4);
+ this->memory_.SetData8(0x5009, '\0');
+ this->memory_.SetData8(0x500a, 4);
+ this->memory_.SetData8(0x500b, 0x13);
+ this->memory_.SetData8(0x500c, 4);
+ this->memory_.SetData8(0x500d, 8);
+ this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x81, 0x03});
+
+ EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+ const DwarfCie* cie = this->section_->GetCie(0x5000);
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(4U, cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+ EXPECT_EQ(0x13U, cie->segment_size);
+ EXPECT_EQ(1U, cie->augmentation_string.size());
+ EXPECT_EQ('\0', cie->augmentation_string[0]);
+ EXPECT_EQ(0U, cie->personality_handler);
+ EXPECT_EQ(0x5010U, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(8, cie->data_alignment_factor);
+ EXPECT_EQ(0x181U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_fail_should_not_cache) {
+ ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+ this->section_->TestClearError();
+ ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment) {
+ this->memory_.SetData32(0x4000, 0x20);
+ this->memory_.SetData32(0x4004, 0x8000);
+ this->memory_.SetData32(0x4008, 0x5000);
+ this->memory_.SetData32(0x400c, 0x100);
+
+ EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
+ DwarfCie cie{};
+ cie.fde_address_encoding = DW_EH_PE_udata4;
+ this->section_->TestSetCachedCieEntry(0x8000, cie);
+ EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+ const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(0x4010U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x4024U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x5000U, fde->pc_start);
+ EXPECT_EQ(0x5100U, fde->pc_end);
+ EXPECT_EQ(0x8000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment_non_zero_segment_size) {
+ this->memory_.SetData32(0x4000, 0x30);
+ this->memory_.SetData32(0x4004, 0x8000);
+ this->memory_.SetData32(0x4018, 0x5000);
+ this->memory_.SetData32(0x401c, 0x100);
+
+ EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
+ DwarfCie cie{};
+ cie.fde_address_encoding = DW_EH_PE_udata4;
+ cie.segment_size = 0x10;
+ this->section_->TestSetCachedCieEntry(0x8000, cie);
+ EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+ const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(0x4020U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x4034U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x5000U, fde->pc_start);
+ EXPECT_EQ(0x5100U, fde->pc_end);
+ EXPECT_EQ(0x8000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_augment) {
+ this->memory_.SetData32(0x4000, 0x100);
+ this->memory_.SetData32(0x4004, 0x8000);
+ this->memory_.SetData32(0x4008, 0x5000);
+ this->memory_.SetData32(0x400c, 0x100);
+ this->memory_.SetMemory(0x4010, std::vector<uint8_t>{0x82, 0x01});
+ this->memory_.SetData16(0x4012, 0x1234);
+
+ EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
+ DwarfCie cie{};
+ cie.fde_address_encoding = DW_EH_PE_udata4;
+ cie.augmentation_string.push_back('z');
+ cie.lsda_encoding = DW_EH_PE_udata2;
+ this->section_->TestSetCachedCieEntry(0x8000, cie);
+ EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+ const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(0x4094U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x4104U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x5000U, fde->pc_start);
+ EXPECT_EQ(0x5100U, fde->pc_end);
+ EXPECT_EQ(0x8000U, fde->cie_offset);
+ EXPECT_EQ(0x1234U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_64_no_augment) {
+ this->memory_.SetData32(0x4000, 0xffffffff);
+ this->memory_.SetData64(0x4004, 0x100);
+ this->memory_.SetData64(0x400c, 0x12345678);
+ this->memory_.SetData32(0x4014, 0x5000);
+ this->memory_.SetData32(0x4018, 0x100);
+
+ EXPECT_CALL(*this->section_, IsCie64(0x12345678)).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*this->section_, GetCieOffsetFromFde64(0x12345678))
+ .WillOnce(::testing::Return(0x12345678));
+ DwarfCie cie{};
+ cie.fde_address_encoding = DW_EH_PE_udata4;
+ this->section_->TestSetCachedCieEntry(0x12345678, cie);
+ EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+ const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(0x401cU, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x410cU, fde->cfa_instructions_end);
+ EXPECT_EQ(0x5000U, fde->pc_start);
+ EXPECT_EQ(0x5100U, fde->pc_end);
+ EXPECT_EQ(0x12345678U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_cached) {
+ DwarfCie cie{};
+ cie.fde_address_encoding = DW_EH_PE_udata4;
+ cie.augmentation_string.push_back('z');
+ cie.lsda_encoding = DW_EH_PE_udata2;
+
+ DwarfFde fde_cached{};
+ fde_cached.cfa_instructions_offset = 0x1000;
+ fde_cached.cfa_instructions_end = 0x1100;
+ fde_cached.pc_start = 0x9000;
+ fde_cached.pc_end = 0x9400;
+ fde_cached.cie_offset = 0x30000;
+ fde_cached.cie = &cie;
+ this->section_->TestSetCachedFdeEntry(0x6000, fde_cached);
+
+ const DwarfFde* fde = this->section_->GetFdeFromOffset(0x6000);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_EQ(&cie, fde->cie);
+ EXPECT_EQ(0x1000U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x1100U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x9000U, fde->pc_start);
+ EXPECT_EQ(0x9400U, fde->pc_end);
+ EXPECT_EQ(0x30000U, fde->cie_offset);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_not_cached) {
+ DwarfCie cie{};
+ cie.cfa_instructions_offset = 0x3000;
+ cie.cfa_instructions_end = 0x3002;
+ DwarfFde fde{};
+ fde.cie = &cie;
+ fde.cie_offset = 0x8000;
+ fde.cfa_instructions_offset = 0x6000;
+ fde.cfa_instructions_end = 0x6002;
+
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x09, 0x02, 0x01});
+ this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
+
+ dwarf_loc_regs_t loc_regs;
+ ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+ ASSERT_EQ(2U, loc_regs.size());
+
+ auto entry = loc_regs.find(2);
+ ASSERT_NE(entry, loc_regs.end());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+ ASSERT_EQ(1U, entry->second.values[0]);
+
+ entry = loc_regs.find(4);
+ ASSERT_NE(entry, loc_regs.end());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+ ASSERT_EQ(3U, entry->second.values[0]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_cached) {
+ DwarfCie cie{};
+ cie.cfa_instructions_offset = 0x3000;
+ cie.cfa_instructions_end = 0x3002;
+ DwarfFde fde{};
+ fde.cie = &cie;
+ fde.cie_offset = 0x8000;
+ fde.cfa_instructions_offset = 0x6000;
+ fde.cfa_instructions_end = 0x6002;
+
+ dwarf_loc_regs_t cie_loc_regs{{6, {DWARF_LOCATION_REGISTER, {4, 0}}}};
+ this->section_->TestSetCachedCieLocRegs(0x8000, cie_loc_regs);
+ this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
+
+ dwarf_loc_regs_t loc_regs;
+ ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+ ASSERT_EQ(2U, loc_regs.size());
+
+ auto entry = loc_regs.find(6);
+ ASSERT_NE(entry, loc_regs.end());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+ ASSERT_EQ(4U, entry->second.values[0]);
+
+ entry = loc_regs.find(4);
+ ASSERT_NE(entry, loc_regs.end());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+ ASSERT_EQ(3U, entry->second.values[0]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Log) {
+ DwarfCie cie{};
+ cie.cfa_instructions_offset = 0x5000;
+ cie.cfa_instructions_end = 0x5001;
+ DwarfFde fde{};
+ fde.cie = &cie;
+ fde.cfa_instructions_offset = 0x6000;
+ fde.cfa_instructions_end = 0x6001;
+
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
+ this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
+ ASSERT_TRUE(this->section_->Log(2, 0x1000, 0x1000, &fde));
+
+ ASSERT_EQ(
+ "4 unwind DW_CFA_nop\n"
+ "4 unwind Raw Data: 0x00\n"
+ "4 unwind DW_CFA_restore register(2)\n"
+ "4 unwind Raw Data: 0xc2\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_CASE_P(
+ DwarfSectionImplTest, Eval_cfa_expr_eval_fail, Eval_cfa_expr_no_stack,
+ Eval_cfa_expr_is_register, Eval_cfa_expr, Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa,
+ Eval_cfa_bad, Eval_cfa_register_prev, Eval_cfa_register_from_value, Eval_double_indirection,
+ Eval_invalid_register, Eval_different_reg_locations, Eval_return_address_undefined,
+ Eval_return_address, Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+ Eval_same_cfa_same_pc, GetCie_fail_should_not_cache, GetCie_32_version_check,
+ GetCie_negative_data_alignment_factor, GetCie_64_no_augment, GetCie_augment, GetCie_version_3,
+ GetCie_version_4, GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
+ GetFdeFromOffset_32_no_augment_non_zero_segment_size, GetFdeFromOffset_32_augment,
+ GetFdeFromOffset_64_no_augment, GetFdeFromOffset_cached, GetCfaLocationInfo_cie_not_cached,
+ GetCfaLocationInfo_cie_cached, Log);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
new file mode 100644
index 0000000..fc67063
--- /dev/null
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2016 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfSection.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MockDwarfSection : public DwarfSection {
+ public:
+ MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
+ virtual ~MockDwarfSection() = default;
+
+ MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
+
+ MOCK_METHOD4(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*));
+
+ MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
+
+ MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+
+ MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
+
+ MOCK_METHOD1(GetFdeFromOffset, const DwarfFde*(uint64_t));
+
+ MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
+
+ MOCK_METHOD1(IsCie32, bool(uint32_t));
+
+ MOCK_METHOD1(IsCie64, bool(uint64_t));
+
+ MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
+
+ MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
+
+ MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
+};
+
+class DwarfSectionTest : public ::testing::Test {
+ protected:
+ MemoryFake memory_;
+};
+
+TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_from_pc) {
+ MockDwarfSection mock_section(&memory_);
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(false));
+
+ // Verify nullptr when GetFdeOffsetFromPc fails.
+ ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
+}
+
+TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_fde_pc_end) {
+ MockDwarfSection mock_section(&memory_);
+
+ DwarfFde fde{};
+ fde.pc_end = 0x500;
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(true));
+ EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+ // Verify nullptr when GetFdeOffsetFromPc fails.
+ ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
+}
+
+TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_pass) {
+ MockDwarfSection mock_section(&memory_);
+
+ DwarfFde fde{};
+ fde.pc_end = 0x2000;
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(true));
+ EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+ // Verify nullptr when GetFdeOffsetFromPc fails.
+ ASSERT_EQ(&fde, mock_section.GetFdeFromPc(0x1000));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_fde) {
+ MockDwarfSection mock_section(&memory_);
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(false));
+
+ ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_cie_null) {
+ MockDwarfSection mock_section(&memory_);
+
+ DwarfFde fde{};
+ fde.pc_end = 0x2000;
+ fde.cie = nullptr;
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(true));
+ EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+ ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
+ MockDwarfSection mock_section(&memory_);
+
+ DwarfCie cie{};
+ DwarfFde fde{};
+ fde.pc_end = 0x2000;
+ fde.cie = &cie;
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(true));
+ EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+ EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ .WillOnce(::testing::Return(false));
+
+ ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+TEST_F(DwarfSectionTest, Step_pass) {
+ MockDwarfSection mock_section(&memory_);
+
+ DwarfCie cie{};
+ DwarfFde fde{};
+ fde.pc_end = 0x2000;
+ fde.cie = &cie;
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(true));
+ EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+ EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ .WillOnce(::testing::Return(true));
+
+ MemoryFake process;
+ EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr))
+ .WillOnce(::testing::Return(true));
+
+ ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 67c9a6b..c7ef4a1 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -20,12 +20,15 @@
#include <vector>
+#include <unwindstack/Regs.h>
+
#include "ElfInterfaceArm.h"
#include "Machine.h"
-#include "Regs.h"
#include "MemoryFake.h"
+namespace unwindstack {
+
class ElfInterfaceArmTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -370,3 +373,5 @@
ASSERT_EQ(0x10U, regs.pc());
ASSERT_EQ(0x10U, regs[ARM_REG_PC]);
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index c31903d..acb7320 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -20,7 +20,9 @@
#include <gtest/gtest.h>
-#include "ElfInterface.h"
+#include <unwindstack/ElfInterface.h>
+
+#include "DwarfEncoding.h"
#include "ElfInterfaceArm.h"
#include "MemoryFake.h"
@@ -33,6 +35,8 @@
#define EM_AARCH64 183
#endif
+namespace unwindstack {
+
class ElfInterfaceTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -67,9 +71,49 @@
template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
void SonameSize();
+ template <typename ElfType>
+ void InitHeadersEhFrameTest();
+
+ template <typename ElfType>
+ void InitHeadersDebugFrame();
+
+ template <typename ElfType>
+ void InitHeadersEhFrameFail();
+
+ template <typename ElfType>
+ void InitHeadersDebugFrameFail();
+
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersMalformed();
+
+ template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
+ void InitSectionHeaders(uint64_t entry_size);
+
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersOffsets();
+
+ template <typename Sym>
+ void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
+ uint64_t sym_offset, const char* name);
+
MemoryFake memory_;
};
+template <typename Sym>
+void ElfInterfaceTest::InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
+ uint64_t sym_offset, const char* name) {
+ Sym sym;
+ memset(&sym, 0, sizeof(sym));
+ sym.st_info = STT_FUNC;
+ sym.st_value = value;
+ sym.st_size = size;
+ sym.st_name = name_offset;
+ sym.st_shndx = SHN_COMMON;
+
+ memory_.SetMemory(offset, &sym, sizeof(sym));
+ memory_.SetMemory(sym_offset + name_offset, name, strlen(name) + 1);
+}
+
template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
void ElfInterfaceTest::SinglePtLoad() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
@@ -571,3 +615,315 @@
TEST_F(ElfInterfaceTest, elf64_soname_size) {
SonameSize<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
}
+
+class MockElfInterface32 : public ElfInterface32 {
+ public:
+ MockElfInterface32(Memory* memory) : ElfInterface32(memory) {}
+ virtual ~MockElfInterface32() = default;
+
+ void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+ void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+
+ void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+ void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+class MockElfInterface64 : public ElfInterface64 {
+ public:
+ MockElfInterface64(Memory* memory) : ElfInterface64(memory) {}
+ virtual ~MockElfInterface64() = default;
+
+ void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+ void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+
+ void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+ void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersEhFrameTest() {
+ ElfType elf(&memory_);
+
+ elf.TestSetEhFrameOffset(0x10000);
+ elf.TestSetEhFrameSize(0);
+ elf.TestSetDebugFrameOffset(0);
+ elf.TestSetDebugFrameSize(0);
+
+ memory_.SetMemory(0x10000,
+ std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata2, DW_EH_PE_udata2});
+ memory_.SetData32(0x10004, 0x500);
+ memory_.SetData32(0x10008, 250);
+
+ elf.InitHeaders();
+
+ EXPECT_FALSE(elf.eh_frame() == nullptr);
+ EXPECT_TRUE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame32) {
+ InitHeadersEhFrameTest<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame64) {
+ InitHeadersEhFrameTest<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersDebugFrame() {
+ ElfType elf(&memory_);
+
+ elf.TestSetEhFrameOffset(0);
+ elf.TestSetEhFrameSize(0);
+ elf.TestSetDebugFrameOffset(0x5000);
+ elf.TestSetDebugFrameSize(0x200);
+
+ memory_.SetData32(0x5000, 0xfc);
+ memory_.SetData32(0x5004, 0xffffffff);
+ memory_.SetData8(0x5008, 1);
+ memory_.SetData8(0x5009, '\0');
+
+ memory_.SetData32(0x5100, 0xfc);
+ memory_.SetData32(0x5104, 0);
+ memory_.SetData32(0x5108, 0x1500);
+ memory_.SetData32(0x510c, 0x200);
+
+ elf.InitHeaders();
+
+ EXPECT_TRUE(elf.eh_frame() == nullptr);
+ EXPECT_FALSE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame32) {
+ InitHeadersDebugFrame<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame64) {
+ InitHeadersDebugFrame<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersEhFrameFail() {
+ ElfType elf(&memory_);
+
+ elf.TestSetEhFrameOffset(0x1000);
+ elf.TestSetEhFrameSize(0x100);
+ elf.TestSetDebugFrameOffset(0);
+ elf.TestSetDebugFrameSize(0);
+
+ elf.InitHeaders();
+
+ EXPECT_TRUE(elf.eh_frame() == nullptr);
+ EXPECT_EQ(0U, elf.eh_frame_offset());
+ EXPECT_EQ(static_cast<uint64_t>(-1), elf.eh_frame_size());
+ EXPECT_TRUE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame32_fail) {
+ InitHeadersEhFrameFail<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame64_fail) {
+ InitHeadersEhFrameFail<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersDebugFrameFail() {
+ ElfType elf(&memory_);
+
+ elf.TestSetEhFrameOffset(0);
+ elf.TestSetEhFrameSize(0);
+ elf.TestSetDebugFrameOffset(0x1000);
+ elf.TestSetDebugFrameSize(0x100);
+
+ elf.InitHeaders();
+
+ EXPECT_TRUE(elf.eh_frame() == nullptr);
+ EXPECT_TRUE(elf.debug_frame() == nullptr);
+ EXPECT_EQ(0U, elf.debug_frame_offset());
+ EXPECT_EQ(static_cast<uint64_t>(-1), elf.debug_frame_size());
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame32_fail) {
+ InitHeadersDebugFrameFail<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) {
+ InitHeadersDebugFrameFail<MockElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersMalformed() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_shoff = 0x1000;
+ ehdr.e_shnum = 10;
+ ehdr.e_shentsize = sizeof(Shdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ ASSERT_TRUE(elf->Init());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed32) {
+ InitSectionHeadersMalformed<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed64) {
+ InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x1000;
+
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 10;
+ ehdr.e_shentsize = entry_size;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_SYMTAB;
+ shdr.sh_link = 4;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = sizeof(Sym);
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_DYNSYM;
+ shdr.sh_link = 4;
+ shdr.sh_addr = 0x6000;
+ shdr.sh_offset = 0x6000;
+ shdr.sh_entsize = sizeof(Sym);
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0xa000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for the entries.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ InitSym<Sym>(0x5000, 0x90000, 0x1000, 0x100, 0xf000, "function_one");
+ InitSym<Sym>(0x6000, 0xd0000, 0x1000, 0x300, 0xf000, "function_two");
+
+ ASSERT_TRUE(elf->Init());
+ EXPECT_EQ(0U, elf->debug_frame_offset());
+ EXPECT_EQ(0U, elf->debug_frame_size());
+ EXPECT_EQ(0U, elf->gnu_debugdata_offset());
+ EXPECT_EQ(0U, elf->gnu_debugdata_size());
+
+ // Look in the first symbol table.
+ std::string name;
+ uint64_t name_offset;
+ ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset));
+ EXPECT_EQ("function_one", name);
+ EXPECT_EQ(16U, name_offset);
+ ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset));
+ EXPECT_EQ("function_two", name);
+ EXPECT_EQ(32U, name_offset);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers32) {
+ InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(sizeof(Elf32_Shdr));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers64) {
+ InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(sizeof(Elf64_Shdr));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size32) {
+ InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(0x100);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size64) {
+ InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(0x100);
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersOffsets() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 10;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x200;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x800;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x100;
+ shdr.sh_addr = 0x6000;
+ shdr.sh_offset = 0x6000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x500;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
+ memory_.SetMemory(0xf200, ".gnu_debugdata", sizeof(".gnu_debugdata"));
+
+ ASSERT_TRUE(elf->Init());
+ EXPECT_EQ(0x6000U, elf->debug_frame_offset());
+ EXPECT_EQ(0x500U, elf->debug_frame_size());
+ EXPECT_EQ(0x5000U, elf->gnu_debugdata_offset());
+ EXPECT_EQ(0x800U, elf->gnu_debugdata_size());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets32) {
+ InitSectionHeadersOffsets<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets64) {
+ InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 25fec8e..ed1be3b 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -15,20 +15,25 @@
*/
#include <elf.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <gtest/gtest.h>
-#include "Elf.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include "ElfTestUtils.h"
+#include "LogFake.h"
#include "MemoryFake.h"
#if !defined(PT_ARM_EXIDX)
#define PT_ARM_EXIDX 0x70000001
#endif
-#if !defined(EM_AARCH64)
-#define EM_AARCH64 183
-#endif
+namespace unwindstack {
class ElfTest : public ::testing::Test {
protected:
@@ -36,35 +41,16 @@
memory_ = new MemoryFake;
}
- template <typename Ehdr>
- void InitEhdr(Ehdr* ehdr) {
- memset(ehdr, 0, sizeof(Ehdr));
- memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
- ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
- ehdr->e_ident[EI_VERSION] = EV_CURRENT;
- ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
- }
-
- void InitElf32(uint32_t type) {
+ void InitElf32(uint32_t machine_type) {
Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, machine_type);
- InitEhdr<Elf32_Ehdr>(&ehdr);
- ehdr.e_ident[EI_CLASS] = ELFCLASS32;
-
- ehdr.e_type = ET_DYN;
- ehdr.e_machine = type;
- ehdr.e_version = EV_CURRENT;
- ehdr.e_entry = 0;
ehdr.e_phoff = 0x100;
- ehdr.e_shoff = 0;
- ehdr.e_flags = 0;
ehdr.e_ehsize = sizeof(ehdr);
ehdr.e_phentsize = sizeof(Elf32_Phdr);
ehdr.e_phnum = 1;
ehdr.e_shentsize = sizeof(Elf32_Shdr);
- ehdr.e_shnum = 0;
- ehdr.e_shstrndx = 0;
- if (type == EM_ARM) {
+ if (machine_type == EM_ARM) {
ehdr.e_flags = 0x5000200;
ehdr.e_phnum = 2;
}
@@ -73,16 +59,13 @@
Elf32_Phdr phdr;
memset(&phdr, 0, sizeof(phdr));
phdr.p_type = PT_LOAD;
- phdr.p_offset = 0;
- phdr.p_vaddr = 0;
- phdr.p_paddr = 0;
phdr.p_filesz = 0x10000;
phdr.p_memsz = 0x10000;
phdr.p_flags = PF_R | PF_X;
phdr.p_align = 0x1000;
memory_->SetMemory(0x100, &phdr, sizeof(phdr));
- if (type == EM_ARM) {
+ if (machine_type == EM_ARM) {
memset(&phdr, 0, sizeof(phdr));
phdr.p_type = PT_ARM_EXIDX;
phdr.p_offset = 0x30000;
@@ -96,33 +79,21 @@
}
}
- void InitElf64(uint32_t type) {
+ void InitElf64(uint32_t machine_type) {
Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, machine_type);
- InitEhdr<Elf64_Ehdr>(&ehdr);
- ehdr.e_ident[EI_CLASS] = ELFCLASS64;
-
- ehdr.e_type = ET_DYN;
- ehdr.e_machine = type;
- ehdr.e_version = EV_CURRENT;
- ehdr.e_entry = 0;
ehdr.e_phoff = 0x100;
- ehdr.e_shoff = 0;
ehdr.e_flags = 0x5000200;
ehdr.e_ehsize = sizeof(ehdr);
ehdr.e_phentsize = sizeof(Elf64_Phdr);
ehdr.e_phnum = 1;
ehdr.e_shentsize = sizeof(Elf64_Shdr);
- ehdr.e_shnum = 0;
- ehdr.e_shstrndx = 0;
memory_->SetMemory(0, &ehdr, sizeof(ehdr));
Elf64_Phdr phdr;
memset(&phdr, 0, sizeof(phdr));
phdr.p_type = PT_LOAD;
- phdr.p_offset = 0;
- phdr.p_vaddr = 0;
- phdr.p_paddr = 0;
phdr.p_filesz = 0x10000;
phdr.p_memsz = 0x10000;
phdr.p_flags = PF_R | PF_X;
@@ -161,6 +132,32 @@
ASSERT_FALSE(elf.Step(0, nullptr, nullptr));
}
+TEST_F(ElfTest, elf32_invalid_machine) {
+ Elf elf(memory_);
+
+ InitElf32(EM_PPC);
+
+ ResetLogs();
+ ASSERT_FALSE(elf.Init());
+
+ ASSERT_EQ("", GetFakeLogBuf());
+ ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86: e_machine = 20\n\n",
+ GetFakeLogPrint());
+}
+
+TEST_F(ElfTest, elf64_invalid_machine) {
+ Elf elf(memory_);
+
+ InitElf64(EM_PPC64);
+
+ ResetLogs();
+ ASSERT_FALSE(elf.Init());
+
+ ASSERT_EQ("", GetFakeLogBuf());
+ ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64: e_machine = 21\n\n",
+ GetFakeLogPrint());
+}
+
TEST_F(ElfTest, elf_arm) {
Elf elf(memory_);
@@ -208,3 +205,95 @@
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) {
+ memory_->SetMemory(offset, ptr, size);
+ });
+
+ Elf elf(memory_);
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+ EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
+}
+
+TEST_F(ElfTest, gnu_debugdata_init_fail64) {
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(offset, ptr, size);
+ });
+
+ Elf elf(memory_);
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+ EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
+}
+
+TEST_F(ElfTest, gnu_debugdata_init32) {
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(offset, ptr, size);
+ });
+
+ Elf elf(memory_);
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+ EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x8cU, elf.interface()->gnu_debugdata_size());
+
+ elf.InitGnuDebugdata();
+ ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(ElfTest, gnu_debugdata_init64) {
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(offset, ptr, size);
+ });
+
+ Elf elf(memory_);
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+ EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x90U, elf.interface()->gnu_debugdata_size());
+
+ elf.InitGnuDebugdata();
+ ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
+}
+
+class MockElf : public Elf {
+ public:
+ MockElf(Memory* memory) : Elf(memory) {}
+ virtual ~MockElf() = default;
+
+ void set_valid(bool valid) { valid_ = valid; }
+ void set_elf_interface(ElfInterface* interface) { interface_.reset(interface); }
+};
+
+TEST_F(ElfTest, rel_pc) {
+ MockElf elf(memory_);
+
+ ElfInterface* interface = new ElfInterface32(memory_);
+ elf.set_elf_interface(interface);
+
+ elf.set_valid(true);
+ interface->set_load_bias(0);
+ MapInfo map_info{.start = 0x1000, .end = 0x2000};
+
+ ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+
+ interface->set_load_bias(0x3000);
+ ASSERT_EQ(0x3101U, elf.GetRelPc(0x1101, &map_info));
+
+ elf.set_valid(false);
+ ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.cpp b/libunwindstack/tests/ElfTestUtils.cpp
new file mode 100644
index 0000000..069386b
--- /dev/null
+++ b/libunwindstack/tests/ElfTestUtils.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 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 <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "ElfTestUtils.h"
+
+namespace unwindstack {
+
+template <typename Ehdr>
+void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type) {
+ memset(ehdr, 0, sizeof(Ehdr));
+ memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
+ ehdr->e_ident[EI_CLASS] = elf_class;
+ ehdr->e_type = ET_DYN;
+ ehdr->e_machine = machine_type;
+ ehdr->e_version = EV_CURRENT;
+ ehdr->e_ehsize = sizeof(Ehdr);
+}
+
+static std::string GetTestFileDirectory() {
+ std::string exec(testing::internal::GetArgvs()[0]);
+ auto const value = exec.find_last_of('/');
+ if (value == std::string::npos) {
+ return "tests/files/";
+ }
+ return exec.substr(0, value + 1) + "tests/files/";
+}
+
+template <typename Ehdr, typename Shdr>
+void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine, bool init_gnu_debugdata,
+ TestCopyFuncType copy_func) {
+ Ehdr ehdr;
+
+ TestInitEhdr(&ehdr, elf_class, machine);
+
+ uint64_t offset = sizeof(Ehdr);
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ copy_func(0, &ehdr, sizeof(ehdr));
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ copy_func(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // Skip this header, it will contain the gnu_debugdata information.
+ uint64_t gnu_offset = offset;
+ offset += ehdr.e_shentsize;
+
+ uint64_t symtab_offset = sizeof(ehdr) + ehdr.e_shnum * ehdr.e_shentsize;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_name = 1;
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_offset = symtab_offset;
+ shdr.sh_size = 0x100;
+ copy_func(offset, &shdr, sizeof(shdr));
+
+ char value = '\0';
+ uint64_t symname_offset = symtab_offset;
+ copy_func(symname_offset, &value, 1);
+ symname_offset++;
+ std::string name(".shstrtab");
+ copy_func(symname_offset, name.c_str(), name.size() + 1);
+ symname_offset += name.size() + 1;
+ name = ".gnu_debugdata";
+ copy_func(symname_offset, name.c_str(), name.size() + 1);
+
+ ssize_t bytes = 0x100;
+ offset = symtab_offset + 0x100;
+ if (init_gnu_debugdata) {
+ // Read in the compressed elf data and copy it in.
+ name = GetTestFileDirectory();
+ if (elf_class == ELFCLASS32) {
+ name += "elf32.xz";
+ } else {
+ name += "elf64.xz";
+ }
+ int fd = TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY));
+ ASSERT_NE(-1, fd) << "Cannot open " + name;
+ // Assumes the file is less than 1024 bytes.
+ std::vector<uint8_t> buf(1024);
+ bytes = TEMP_FAILURE_RETRY(read(fd, buf.data(), buf.size()));
+ ASSERT_GT(bytes, 0);
+ // Make sure the file isn't too big.
+ ASSERT_NE(static_cast<size_t>(bytes), buf.size())
+ << "File " + name + " is too big, increase buffer size.";
+ close(fd);
+ buf.resize(bytes);
+ copy_func(offset, buf.data(), buf.size());
+ }
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = symname_offset - symtab_offset;
+ shdr.sh_addr = offset;
+ shdr.sh_offset = offset;
+ shdr.sh_size = bytes;
+ copy_func(gnu_offset, &shdr, sizeof(shdr));
+}
+
+template void TestInitEhdr<Elf32_Ehdr>(Elf32_Ehdr*, uint32_t, uint32_t);
+template void TestInitEhdr<Elf64_Ehdr>(Elf64_Ehdr*, uint32_t, uint32_t);
+
+template void TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(uint32_t, uint32_t, bool,
+ TestCopyFuncType);
+template void TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(uint32_t, uint32_t, bool,
+ TestCopyFuncType);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h
new file mode 100644
index 0000000..6ef00e1
--- /dev/null
+++ b/libunwindstack/tests/ElfTestUtils.h
@@ -0,0 +1,35 @@
+/*
+ * 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_TESTS_ELF_TEST_UTILS_H
+#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
+
+#include <functional>
+
+namespace unwindstack {
+
+typedef std::function<void(uint64_t, const void*, size_t)> TestCopyFuncType;
+
+template <typename Ehdr>
+void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type);
+
+template <typename Ehdr, typename Shdr>
+void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata,
+ TestCopyFuncType copy_func);
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
diff --git a/libunwindstack/tests/GenGnuDebugdata.cpp b/libunwindstack/tests/GenGnuDebugdata.cpp
new file mode 100644
index 0000000..2644582
--- /dev/null
+++ b/libunwindstack/tests/GenGnuDebugdata.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 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 <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#if !defined(EM_AARCH64)
+#define EM_AARCH64 183
+#endif
+
+template <typename Ehdr>
+void InitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine) {
+ memset(ehdr, 0, sizeof(Ehdr));
+ memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
+ ehdr->e_ident[EI_CLASS] = elf_class;
+ ehdr->e_type = ET_DYN;
+ ehdr->e_machine = machine;
+ ehdr->e_version = EV_CURRENT;
+ ehdr->e_ehsize = sizeof(Ehdr);
+}
+
+template <typename Ehdr, typename Shdr>
+void GenElf(Ehdr* ehdr, int fd) {
+ uint64_t offset = sizeof(Ehdr);
+ ehdr->e_shoff = offset;
+ ehdr->e_shnum = 3;
+ ehdr->e_shentsize = sizeof(Shdr);
+ ehdr->e_shstrndx = 2;
+ TEMP_FAILURE_RETRY(write(fd, ehdr, sizeof(Ehdr)));
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_name = 0;
+ shdr.sh_type = SHT_NULL;
+ TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+ offset += ehdr->e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 11;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x800;
+ TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+ offset += ehdr->e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x200;
+ shdr.sh_size = 24;
+ TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+
+ // Write out the name entries information.
+ lseek(fd, 0x200, SEEK_SET);
+ std::string name;
+ TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+ name = ".shstrtab";
+ TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+ name = ".debug_frame";
+ TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+}
+
+int main() {
+ int elf32_fd = TEMP_FAILURE_RETRY(open("elf32", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+ if (elf32_fd == -1) {
+ printf("Failed to create elf32: %s\n", strerror(errno));
+ return 1;
+ }
+
+ int elf64_fd = TEMP_FAILURE_RETRY(open("elf64", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+ if (elf64_fd == -1) {
+ printf("Failed to create elf64: %s\n", strerror(errno));
+ return 1;
+ }
+
+ Elf32_Ehdr ehdr32;
+ InitEhdr<Elf32_Ehdr>(&ehdr32, ELFCLASS32, EM_ARM);
+ GenElf<Elf32_Ehdr, Elf32_Shdr>(&ehdr32, elf32_fd);
+ close(elf32_fd);
+
+ Elf64_Ehdr ehdr64;
+ InitEhdr<Elf64_Ehdr>(&ehdr64, ELFCLASS64, EM_AARCH64);
+ GenElf<Elf64_Ehdr, Elf64_Shdr>(&ehdr64, elf64_fd);
+ close(elf64_fd);
+}
diff --git a/libunwindstack/tests/LogFake.cpp b/libunwindstack/tests/LogFake.cpp
index 411594a..537ccaf 100644
--- a/libunwindstack/tests/LogFake.cpp
+++ b/libunwindstack/tests/LogFake.cpp
@@ -25,7 +25,6 @@
#include "LogFake.h"
// Forward declarations.
-class Backtrace;
struct EventTagMap;
struct AndroidLogEntry;
@@ -33,6 +32,8 @@
std::string g_fake_log_print;
+namespace unwindstack {
+
void ResetLogs() {
g_fake_log_buf = "";
g_fake_log_print = "";
@@ -46,6 +47,8 @@
return g_fake_log_print;
}
+} // namespace unwindstack
+
extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
g_fake_log_buf += tag;
diff --git a/libunwindstack/tests/LogFake.h b/libunwindstack/tests/LogFake.h
index 006d393..e1dc50d 100644
--- a/libunwindstack/tests/LogFake.h
+++ b/libunwindstack/tests/LogFake.h
@@ -19,8 +19,12 @@
#include <string>
+namespace unwindstack {
+
void ResetLogs();
std::string GetFakeLogBuf();
std::string GetFakeLogPrint();
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
new file mode 100644
index 0000000..d2aad49
--- /dev/null
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2016 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 <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MapInfoCreateMemoryTest : public ::testing::Test {
+ protected:
+ template <typename Ehdr, typename Shdr>
+ static void InitElf(int fd, uint64_t file_offset, uint64_t sh_offset, uint8_t class_type) {
+ std::vector<uint8_t> buffer(20000);
+ memset(buffer.data(), 0, buffer.size());
+
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+ ehdr.e_ident[EI_CLASS] = class_type;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memcpy(&buffer[file_offset], &ehdr, sizeof(ehdr));
+
+ ASSERT_TRUE(android::base::WriteFully(fd, buffer.data(), buffer.size()));
+ }
+
+ static void SetUpTestCase() {
+ std::vector<uint8_t> buffer(1024);
+ memset(buffer.data(), 0, buffer.size());
+ memcpy(buffer.data(), ELFMAG, SELFMAG);
+ buffer[EI_CLASS] = ELFCLASS32;
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ memset(buffer.data(), 0, buffer.size());
+ memcpy(&buffer[0x100], ELFMAG, SELFMAG);
+ buffer[0x100 + EI_CLASS] = ELFCLASS64;
+ ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
+
+ InitElf<Elf32_Ehdr, Elf32_Shdr>(elf32_at_map_.fd, 0x1000, 0x2000, ELFCLASS32);
+ InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64);
+ }
+
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+ }
+
+ MemoryFake* memory_;
+ std::shared_ptr<Memory> process_memory_;
+
+ static TemporaryFile elf_;
+
+ static TemporaryFile elf_at_100_;
+
+ static TemporaryFile elf32_at_map_;
+ static TemporaryFile elf64_at_map_;
+};
+TemporaryFile MapInfoCreateMemoryTest::elf_;
+TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
+TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
+TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
+
+TEST_F(MapInfoCreateMemoryTest, end_le_start) {
+ MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() == nullptr);
+
+ info.end = 0xff;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() == nullptr);
+
+ // Make sure this test is valid.
+ info.end = 0x101;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+}
+
+// Verify that if the offset is non-zero but there is no elf at the offset,
+// that the full file is used.
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
+ MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0x100U, info.elf_offset);
+
+ // Read the entire file.
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
+ ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+ ASSERT_EQ(ELFCLASS32, buffer[EI_CLASS]);
+ for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(memory->Read(1024, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used.
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
+ MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Read the valid part of the file.
+ std::vector<uint8_t> buffer(0x100);
+ ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
+ ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+ ASSERT_EQ(ELFCLASS64, buffer[EI_CLASS]);
+ for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used. Further verify that if the
+// embedded elf is bigger than the initial map, the new object is larger
+// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
+ MapInfo info{.start = 0x5000, .end = 0x6000, .offset = 0x1000, .name = elf32_at_map_.path};
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Verify the memory is a valid elf.
+ uint8_t e_ident[SELFMAG + 1];
+ ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG));
+ ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(memory->Read(0x1000, e_ident, 1));
+}
+
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
+ MapInfo info{.start = 0x7000, .end = 0x8000, .offset = 0x2000, .name = elf64_at_map_.path};
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Verify the memory is a valid elf.
+ uint8_t e_ident[SELFMAG + 1];
+ ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG));
+ ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(memory->Read(0x1000, e_ident, 1));
+}
+
+// Verify that device file names will never result in Memory object creation.
+TEST_F(MapInfoCreateMemoryTest, check_device_maps) {
+ // Set up some memory so that a valid local memory object would
+ // be returned if the file mapping fails, but the device check is incorrect.
+ std::vector<uint8_t> buffer(1024);
+ MapInfo info;
+ info.start = reinterpret_cast<uint64_t>(buffer.data());
+ info.end = info.start + buffer.size();
+ info.offset = 0;
+
+ info.flags = 0x8000;
+ info.name = "/dev/something";
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() == nullptr);
+}
+
+TEST_F(MapInfoCreateMemoryTest, process_memory) {
+ MapInfo info;
+ info.start = 0x2000;
+ info.end = 0x3000;
+ info.offset = 0;
+
+ // Verify that the the process_memory object is used, so seed it
+ // with memory.
+ std::vector<uint8_t> buffer(1024);
+ for (size_t i = 0; i < buffer.size(); i++) {
+ buffer[i] = i % 256;
+ }
+ memory_->SetMemory(info.start, buffer.data(), buffer.size());
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+
+ memset(buffer.data(), 0, buffer.size());
+ ASSERT_TRUE(memory->Read(0, buffer.data(), buffer.size()));
+ for (size_t i = 0; i < buffer.size(); i++) {
+ ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i;
+ }
+
+ // Try to read outside of the map size.
+ ASSERT_FALSE(memory->Read(buffer.size(), buffer.data(), 1));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
new file mode 100644
index 0000000..0b70d13
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2016 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 <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfTestUtils.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MapInfoGetElfTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+ }
+
+ template <typename Ehdr, typename Shdr>
+ static void InitElf(uint64_t sh_offset, Ehdr* ehdr, uint8_t class_type, uint8_t machine_type) {
+ memset(ehdr, 0, sizeof(*ehdr));
+ memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_CLASS] = class_type;
+ ehdr->e_machine = machine_type;
+ ehdr->e_shoff = sh_offset;
+ ehdr->e_shentsize = sizeof(Shdr) + 100;
+ ehdr->e_shnum = 4;
+ }
+
+ const size_t kMapSize = 4096;
+
+ std::shared_ptr<Memory> process_memory_;
+ MemoryFake* memory_;
+
+ TemporaryFile elf_;
+};
+
+TEST_F(MapInfoGetElfTest, invalid) {
+ MapInfo info{.start = 0x1000, .end = 0x2000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+ // The map is empty, but this should still create an invalid elf object.
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_FALSE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, valid32) {
+ MapInfo info{.start = 0x3000, .end = 0x4000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+ EXPECT_EQ(ELFCLASS32, elf->class_type());
+}
+
+TEST_F(MapInfoGetElfTest, valid64) {
+ MapInfo info{.start = 0x8000, .end = 0x9000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+ memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+ EXPECT_EQ(ELFCLASS64, elf->class_type());
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
+ MapInfo info{.start = 0x4000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(0x4000 + offset, ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+ EXPECT_EQ(ELFCLASS32, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
+ MapInfo info{.start = 0x6000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(0x6000 + offset, ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+ EXPECT_EQ(ELFCLASS64, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
+ MapInfo info{.start = 0x2000, .end = 0x3000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(0x2000 + offset, ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, true));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+ EXPECT_EQ(ELFCLASS32, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
+ MapInfo info{.start = 0x5000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(0x5000 + offset, ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, true));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+ EXPECT_EQ(ELFCLASS64, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, end_le_start) {
+ MapInfo info{.start = 0x1000, .end = 0x1000, .offset = 0, .flags = PROT_READ, .name = elf_.path};
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_FALSE(elf->valid());
+
+ info.elf = nullptr;
+ info.end = 0xfff;
+ elf.reset(info.GetElf(process_memory_, false));
+ ASSERT_FALSE(elf->valid());
+
+ // Make sure this test is valid.
+ info.elf = nullptr;
+ info.end = 0x2000;
+ elf.reset(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+}
+
+// Verify that if the offset is non-zero but there is no elf at the offset,
+// that the full file is used.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
+ MapInfo info{
+ .start = 0x1000, .end = 0x2000, .offset = 0x100, .flags = PROT_READ, .name = elf_.path};
+
+ std::vector<uint8_t> buffer(0x1000);
+ memset(buffer.data(), 0, buffer.size());
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memcpy(buffer.data(), &ehdr, sizeof(ehdr));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+ ASSERT_TRUE(elf->memory() != nullptr);
+ ASSERT_EQ(0x100U, info.elf_offset);
+
+ // Read the entire file.
+ memset(buffer.data(), 0, buffer.size());
+ ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), buffer.size()));
+ ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(elf->memory()->Read(buffer.size(), buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
+ MapInfo info{
+ .start = 0x1000, .end = 0x2000, .offset = 0x2000, .flags = PROT_READ, .name = elf_.path};
+
+ std::vector<uint8_t> buffer(0x4000);
+ memset(buffer.data(), 0, buffer.size());
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+ ASSERT_TRUE(elf->memory() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Read the valid part of the file.
+ ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
+ ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < 0x1000; i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(elf->memory()->Read(0x1000, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used. Further verify that if the
+// embedded elf is bigger than the initial map, the new object is larger
+// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
+ MapInfo info{
+ .start = 0x5000, .end = 0x6000, .offset = 0x1000, .flags = PROT_READ, .name = elf_.path};
+
+ std::vector<uint8_t> buffer(0x4000);
+ memset(buffer.data(), 0, buffer.size());
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+ ASSERT_TRUE(elf->memory() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Verify the memory is a valid elf.
+ memset(buffer.data(), 0, buffer.size());
+ ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
+ ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(elf->memory()->Read(0x1000, buffer.data(), 1));
+}
+
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
+ MapInfo info{
+ .start = 0x7000, .end = 0x8000, .offset = 0x1000, .flags = PROT_READ, .name = elf_.path};
+
+ std::vector<uint8_t> buffer(0x4000);
+ memset(buffer.data(), 0, buffer.size());
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+ ASSERT_TRUE(elf->memory() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Verify the memory is a valid elf.
+ memset(buffer.data(), 0, buffer.size());
+ ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
+ ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(elf->memory()->Read(0x1000, buffer.data(), 1));
+}
+
+TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
+ MapInfo info{.start = 0x9000, .end = 0xa000, .offset = 0x1000, .flags = 0, .name = ""};
+
+ // Create valid elf data in process memory only.
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_FALSE(elf->valid());
+
+ info.elf = nullptr;
+ info.flags = PROT_READ;
+ elf.reset(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, check_device_maps) {
+ MapInfo info{.start = 0x7000,
+ .end = 0x8000,
+ .offset = 0x1000,
+ .flags = PROT_READ | MAPS_FLAGS_DEVICE_MAP,
+ .name = "/dev/something"};
+
+ // Create valid elf data in process memory for this to verify that only
+ // the name is causing invalid elf data.
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_FALSE(elf->valid());
+
+ // Set the name to nothing to verify that it still fails.
+ info.elf = nullptr;
+ info.name = "";
+ elf.reset(info.GetElf(process_memory_, false));
+ ASSERT_FALSE(elf->valid());
+
+ // Change the flags and verify the elf is valid now.
+ info.elf = nullptr;
+ info.flags = PROT_READ;
+ elf.reset(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
new file mode 100644
index 0000000..2d15a4e
--- /dev/null
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2016 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 <inttypes.h>
+#include <sys/mman.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Maps.h>
+
+namespace unwindstack {
+
+static void VerifyLine(std::string line, MapInfo* info) {
+ BufferMaps maps(line.c_str());
+
+ if (info == nullptr) {
+ ASSERT_FALSE(maps.Parse()) << "Failed on: " + line;
+ } else {
+ ASSERT_TRUE(maps.Parse()) << "Failed on: " + line;
+ MapInfo* element = maps.Get(0);
+ ASSERT_TRUE(element != nullptr) << "Failed on: " + line;
+ *info = *element;
+ }
+}
+
+TEST(MapsTest, verify_parse_line) {
+ MapInfo info;
+
+ VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
+ EXPECT_EQ(1U, info.start);
+ EXPECT_EQ(2U, info.end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+ EXPECT_EQ(3U, info.offset);
+ EXPECT_EQ("", info.name);
+
+ VerifyLine("0a-0b ---s 0c 0d:0e 06 /fake/name\n", &info);
+ EXPECT_EQ(0xaU, info.start);
+ EXPECT_EQ(0xbU, info.end);
+ EXPECT_EQ(0U, info.flags);
+ EXPECT_EQ(0xcU, info.offset);
+ EXPECT_EQ("/fake/name", info.name);
+
+ VerifyLine("01-02 rwxp 03 04:05 06 /fake/name/again\n", &info);
+ EXPECT_EQ(1U, info.start);
+ EXPECT_EQ(2U, info.end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+ EXPECT_EQ(3U, info.offset);
+ EXPECT_EQ("/fake/name/again", info.name);
+
+ VerifyLine("-00 rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00- rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00-00 rwxp 00 :00 0\n", nullptr);
+ VerifyLine("00-00 rwxp 00 00:00 \n", nullptr);
+ VerifyLine("x-00 rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00 -00 rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00-x rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00-x rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00-00x rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00-00 rwxp0 00 00:00 0\n", nullptr);
+ VerifyLine("00-00 rwxp0 00 00:00 0\n", nullptr);
+ VerifyLine("00-00 rwp 00 00:00 0\n", nullptr);
+ VerifyLine("00-00 rwxp 0000:00 0\n", nullptr);
+ VerifyLine("00-00 rwxp 00 00 :00 0\n", nullptr);
+ VerifyLine("00-00 rwxp 00 00: 00 0\n", nullptr);
+ VerifyLine("00-00 rwxp 00 00:000\n", nullptr);
+ VerifyLine("00-00 rwxp 00 00:00 0/fake\n", nullptr);
+ VerifyLine("00-00 xxxx 00 00:00 0 /fake\n", nullptr);
+ VerifyLine("00-00 ywxp 00 00:00 0 /fake\n", nullptr);
+ VerifyLine("00-00 ryxp 00 00:00 0 /fake\n", nullptr);
+ VerifyLine("00-00 rwyp 00 00:00 0 /fake\n", nullptr);
+ VerifyLine("00-00 rwx- 00 00:00 0 /fake\n", nullptr);
+ VerifyLine("0\n", nullptr);
+ VerifyLine("00\n", nullptr);
+ VerifyLine("00-\n", nullptr);
+ VerifyLine("00-0\n", nullptr);
+ VerifyLine("00-00\n", nullptr);
+ VerifyLine("00-00 \n", nullptr);
+ VerifyLine("00-00 -\n", nullptr);
+ VerifyLine("00-00 r\n", nullptr);
+ VerifyLine("00-00 --\n", nullptr);
+ VerifyLine("00-00 rw\n", nullptr);
+ VerifyLine("00-00 ---\n", nullptr);
+ VerifyLine("00-00 rwx\n", nullptr);
+ VerifyLine("00-00 ---s\n", nullptr);
+ VerifyLine("00-00 ---p\n", nullptr);
+ VerifyLine("00-00 ---s 0\n", nullptr);
+ VerifyLine("00-00 ---p 0 \n", nullptr);
+ VerifyLine("00-00 ---p 0 0\n", nullptr);
+ VerifyLine("00-00 ---p 0 0:\n", nullptr);
+ VerifyLine("00-00 ---p 0 0:0\n", nullptr);
+ VerifyLine("00-00 ---p 0 0:0 \n", nullptr);
+
+ // Line to verify that the parser will detect a completely malformed line
+ // properly.
+ VerifyLine("7ffff7dda000-7ffff7dfd7ffff7ff3000-7ffff7ff4000 ---p 0000f000 fc:02 44171565\n",
+ nullptr);
+}
+
+TEST(MapsTest, verify_large_values) {
+ MapInfo info;
+#if defined(__LP64__)
+ VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
+ EXPECT_EQ(0xfabcdef012345678UL, info.start);
+ EXPECT_EQ(0xf12345678abcdef8UL, info.end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+ EXPECT_EQ(0xf0b0d0f010305070UL, info.offset);
+#else
+ VerifyLine("f2345678-fabcdef8 rwxp f0305070 00:00 0\n", &info);
+ EXPECT_EQ(0xf2345678UL, info.start);
+ EXPECT_EQ(0xfabcdef8UL, info.end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+ EXPECT_EQ(0xf0305070UL, info.offset);
+#endif
+}
+
+TEST(MapsTest, parse_permissions) {
+ BufferMaps maps(
+ "1000-2000 ---s 00000000 00:00 0\n"
+ "2000-3000 r--s 00000000 00:00 0\n"
+ "3000-4000 -w-s 00000000 00:00 0\n"
+ "4000-5000 --xp 00000000 00:00 0\n"
+ "5000-6000 rwxp 00000000 00:00 0\n");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(5U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_EQ(PROT_NONE, it->flags);
+ ASSERT_EQ(0x1000U, it->start);
+ ASSERT_EQ(0x2000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(PROT_READ, it->flags);
+ ASSERT_EQ(0x2000U, it->start);
+ ASSERT_EQ(0x3000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(PROT_WRITE, it->flags);
+ ASSERT_EQ(0x3000U, it->start);
+ ASSERT_EQ(0x4000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(PROT_EXEC, it->flags);
+ ASSERT_EQ(0x4000U, it->start);
+ ASSERT_EQ(0x5000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, it->flags);
+ ASSERT_EQ(0x5000U, it->start);
+ ASSERT_EQ(0x6000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, parse_name) {
+ BufferMaps maps(
+ "7b29b000-7b29e000 rw-p 00000000 00:00 0\n"
+ "7b29e000-7b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+ "7b29f000-7b2a0000 rw-p 00000000 00:00 0");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(3U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_EQ("", it->name);
+ ASSERT_EQ(0x7b29b000U, it->start);
+ ASSERT_EQ(0x7b29e000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ++it;
+ ASSERT_EQ("/system/lib/fake.so", it->name);
+ ASSERT_EQ(0x7b29e000U, it->start);
+ ASSERT_EQ(0x7b29f000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ++it;
+ ASSERT_EQ("", it->name);
+ ASSERT_EQ(0x7b29f000U, it->start);
+ ASSERT_EQ(0x7b2a0000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ++it;
+ ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, parse_offset) {
+ BufferMaps maps(
+ "a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+ "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(2U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ(0xa000U, it->start);
+ ASSERT_EQ(0xe000U, it->end);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ASSERT_EQ("/system/lib/fake.so", it->name);
+ ++it;
+ ASSERT_EQ(0xa12345U, it->offset);
+ ASSERT_EQ(0xe000U, it->start);
+ ASSERT_EQ(0xf000U, it->end);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ASSERT_EQ("/system/lib/fake.so", it->name);
+ ++it;
+ ASSERT_EQ(maps.end(), it);
+}
+
+TEST(MapsTest, device) {
+ BufferMaps maps(
+ "a000-e000 rw-p 00000000 00:00 0 /dev/\n"
+ "f000-f100 rw-p 00000000 00:00 0 /dev/does_not_exist\n"
+ "f100-f200 rw-p 00000000 00:00 0 /dev/ashmem/does_not_exist\n"
+ "f200-f300 rw-p 00000000 00:00 0 /devsomething/does_not_exist\n");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(4U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_TRUE(it->flags & 0x8000);
+ ASSERT_EQ("/dev/", it->name);
+ ++it;
+ ASSERT_TRUE(it->flags & 0x8000);
+ ASSERT_EQ("/dev/does_not_exist", it->name);
+ ++it;
+ ASSERT_FALSE(it->flags & 0x8000);
+ ASSERT_EQ("/dev/ashmem/does_not_exist", it->name);
+ ++it;
+ ASSERT_FALSE(it->flags & 0x8000);
+ ASSERT_EQ("/devsomething/does_not_exist", it->name);
+}
+
+TEST(MapsTest, file_smoke) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("7b29b000-7b29e000 r-xp a0000000 00:00 0 /fake.so\n"
+ "7b2b0000-7b2e0000 r-xp b0000000 00:00 0 /fake2.so\n"
+ "7b2e0000-7b2f0000 r-xp c0000000 00:00 0 /fake3.so\n",
+ tf.path, 0660, getuid(), getgid()));
+
+ FileMaps maps(tf.path);
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(3U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_EQ(0x7b29b000U, it->start);
+ ASSERT_EQ(0x7b29e000U, it->end);
+ ASSERT_EQ(0xa0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("/fake.so", it->name);
+ ++it;
+ ASSERT_EQ(0x7b2b0000U, it->start);
+ ASSERT_EQ(0x7b2e0000U, it->end);
+ ASSERT_EQ(0xb0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("/fake2.so", it->name);
+ ++it;
+ ASSERT_EQ(0x7b2e0000U, it->start);
+ ASSERT_EQ(0x7b2f0000U, it->end);
+ ASSERT_EQ(0xc0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("/fake3.so", it->name);
+ ++it;
+ ASSERT_EQ(it, maps.end());
+}
+
+TEST(MapsTest, file_no_map_name) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("7b29b000-7b29e000 r-xp a0000000 00:00 0\n"
+ "7b2b0000-7b2e0000 r-xp b0000000 00:00 0 /fake2.so\n"
+ "7b2e0000-7b2f0000 r-xp c0000000 00:00 0 \n",
+ tf.path, 0660, getuid(), getgid()));
+
+ FileMaps maps(tf.path);
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(3U, maps.Total());
+ auto it = maps.begin();
+ ASSERT_EQ(0x7b29b000U, it->start);
+ ASSERT_EQ(0x7b29e000U, it->end);
+ ASSERT_EQ(0xa0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(0x7b2b0000U, it->start);
+ ASSERT_EQ(0x7b2e0000U, it->end);
+ ASSERT_EQ(0xb0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("/fake2.so", it->name);
+ ++it;
+ ASSERT_EQ(0x7b2e0000U, it->start);
+ ASSERT_EQ(0x7b2f0000U, it->end);
+ ASSERT_EQ(0xc0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("", it->name);
+ ++it;
+ ASSERT_EQ(it, maps.end());
+}
+
+// Verify that a file that crosses a buffer is parsed correctly.
+static std::string CreateEntry(size_t index) {
+ return android::base::StringPrintf("%08zx-%08zx rwxp 0000 00:00 0\n", index * 4096,
+ (index + 1) * 4096);
+}
+
+TEST(MapsTest, file_buffer_cross) {
+ constexpr size_t kBufferSize = 2048;
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ // Compute how many to add in the first buffer.
+ size_t entry_len = CreateEntry(0).size();
+ size_t index;
+ std::string file_data;
+ for (index = 0; index < kBufferSize / entry_len; index++) {
+ file_data += CreateEntry(index);
+ }
+ // Add a long name to make sure that the first buffer does not contain a
+ // complete line.
+ // Remove the last newline.
+ size_t extra = 0;
+ size_t leftover = kBufferSize % entry_len;
+ size_t overlap1_index = 0;
+ std::string overlap1_name;
+ if (leftover == 0) {
+ // Exact match, add a long name to cross over the value.
+ overlap1_name = "/fake/name/is/long/on/purpose";
+ file_data.erase(file_data.size() - 1);
+ file_data += ' ' + overlap1_name + '\n';
+ extra = entry_len + overlap1_name.size() + 1;
+ overlap1_index = index;
+ }
+
+ // Compute how many need to go in to hit the buffer boundary exactly.
+ size_t bytes_left_in_buffer = kBufferSize - extra;
+ size_t entries_to_add = bytes_left_in_buffer / entry_len + index;
+ for (; index < entries_to_add; index++) {
+ file_data += CreateEntry(index);
+ }
+
+ // Now figure out how many bytes to add to get exactly to the buffer boundary.
+ leftover = bytes_left_in_buffer % entry_len;
+ std::string overlap2_name;
+ size_t overlap2_index = 0;
+ if (leftover != 0) {
+ file_data.erase(file_data.size() - 1);
+ file_data += ' ';
+ overlap2_name = std::string(leftover - 1, 'x');
+ file_data += overlap2_name + '\n';
+ overlap2_index = index - 1;
+ }
+
+ // Now add a few entries on the next page.
+ for (size_t start = index; index < start + 10; index++) {
+ file_data += CreateEntry(index);
+ }
+
+ ASSERT_TRUE(android::base::WriteStringToFile(file_data, tf.path, 0660, getuid(), getgid()));
+
+ FileMaps maps(tf.path);
+ ASSERT_TRUE(maps.Parse());
+ EXPECT_EQ(index, maps.Total());
+ // Verify all of the maps.
+ for (size_t i = 0; i < index; i++) {
+ MapInfo* info = maps.Get(i);
+ ASSERT_TRUE(info != nullptr) << "Failed verifying index " + std::to_string(i);
+ EXPECT_EQ(i * 4096, info->start) << "Failed verifying index " + std::to_string(i);
+ EXPECT_EQ((i + 1) * 4096, info->end) << "Failed verifying index " + std::to_string(i);
+ EXPECT_EQ(0U, info->offset) << "Failed verifying index " + std::to_string(i);
+ if (overlap1_index != 0 && i == overlap1_index) {
+ EXPECT_EQ(overlap1_name, info->name) << "Failed verifying overlap1 name " + std::to_string(i);
+ } else if (overlap2_index != 0 && i == overlap2_index) {
+ EXPECT_EQ(overlap2_name, info->name) << "Failed verifying overlap2 name " + std::to_string(i);
+ } else {
+ EXPECT_EQ("", info->name) << "Failed verifying index " + std::to_string(i);
+ }
+ }
+}
+
+TEST(MapsTest, file_should_fail) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "7ffff7dda000-7ffff7dfd7ffff7ff3000-7ffff7ff4000 ---p 0000f000 fc:02 44171565\n", tf.path,
+ 0660, getuid(), getgid()));
+
+ FileMaps maps(tf.path);
+
+ ASSERT_FALSE(maps.Parse());
+}
+
+// Create a maps file that is extremely large.
+TEST(MapsTest, large_file) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ std::string file_data;
+ uint64_t start = 0x700000;
+ for (size_t i = 0; i < 5000; i++) {
+ file_data +=
+ android::base::StringPrintf("%" PRIx64 "-%" PRIx64 " r-xp 1000 00:0 0 /fake%zu.so\n",
+ start + i * 4096, start + (i + 1) * 4096, i);
+ }
+
+ ASSERT_TRUE(android::base::WriteStringToFile(file_data, tf.path, 0660, getuid(), getgid()));
+
+ FileMaps maps(tf.path);
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(5000U, maps.Total());
+ for (size_t i = 0; i < 5000; i++) {
+ MapInfo* info = maps.Get(i);
+ ASSERT_EQ(start + i * 4096, info->start) << "Failed at map " + std::to_string(i);
+ ASSERT_EQ(start + (i + 1) * 4096, info->end) << "Failed at map " + std::to_string(i);
+ std::string name = "/fake" + std::to_string(i) + ".so";
+ ASSERT_EQ(name, info->name) << "Failed at map " + std::to_string(i);
+ }
+}
+
+TEST(MapsTest, find) {
+ BufferMaps maps(
+ "1000-2000 r--p 00000010 00:00 0 /system/lib/fake1.so\n"
+ "3000-4000 -w-p 00000020 00:00 0 /system/lib/fake2.so\n"
+ "6000-8000 --xp 00000030 00:00 0 /system/lib/fake3.so\n"
+ "a000-b000 rw-p 00000040 00:00 0 /system/lib/fake4.so\n"
+ "e000-f000 rwxp 00000050 00:00 0 /system/lib/fake5.so\n");
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(5U, maps.Total());
+
+ ASSERT_TRUE(maps.Find(0x500) == nullptr);
+ ASSERT_TRUE(maps.Find(0x2000) == nullptr);
+ ASSERT_TRUE(maps.Find(0x5010) == nullptr);
+ ASSERT_TRUE(maps.Find(0x9a00) == nullptr);
+ ASSERT_TRUE(maps.Find(0xf000) == nullptr);
+ ASSERT_TRUE(maps.Find(0xf010) == nullptr);
+
+ MapInfo* info = maps.Find(0x1000);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0x1000U, info->start);
+ ASSERT_EQ(0x2000U, info->end);
+ ASSERT_EQ(0x10U, info->offset);
+ ASSERT_EQ(PROT_READ, info->flags);
+ ASSERT_EQ("/system/lib/fake1.so", info->name);
+
+ info = maps.Find(0x3020);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0x3000U, info->start);
+ ASSERT_EQ(0x4000U, info->end);
+ ASSERT_EQ(0x20U, info->offset);
+ ASSERT_EQ(PROT_WRITE, info->flags);
+ ASSERT_EQ("/system/lib/fake2.so", info->name);
+
+ info = maps.Find(0x6020);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0x6000U, info->start);
+ ASSERT_EQ(0x8000U, info->end);
+ ASSERT_EQ(0x30U, info->offset);
+ ASSERT_EQ(PROT_EXEC, info->flags);
+ ASSERT_EQ("/system/lib/fake3.so", info->name);
+
+ info = maps.Find(0xafff);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0xa000U, info->start);
+ ASSERT_EQ(0xb000U, info->end);
+ ASSERT_EQ(0x40U, info->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, info->flags);
+ ASSERT_EQ("/system/lib/fake4.so", info->name);
+
+ info = maps.Find(0xe500);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_EQ(0xe000U, info->start);
+ ASSERT_EQ(0xf000U, info->end);
+ ASSERT_EQ(0x50U, info->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
+ ASSERT_EQ("/system/lib/fake5.so", info->name);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryBufferTest.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
new file mode 100644
index 0000000..50a8a1b
--- /dev/null
+++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "LogFake.h"
+
+namespace unwindstack {
+
+class MemoryBufferTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.reset(new MemoryBuffer);
+ }
+ std::unique_ptr<MemoryBuffer> memory_;
+};
+
+TEST_F(MemoryBufferTest, empty) {
+ ASSERT_EQ(0U, memory_->Size());
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_FALSE(memory_->Read(0, buffer.data(), 1));
+ ASSERT_EQ(nullptr, memory_->GetPtr(0));
+ ASSERT_EQ(nullptr, memory_->GetPtr(1));
+}
+
+TEST_F(MemoryBufferTest, write_read) {
+ memory_->Resize(256);
+ ASSERT_EQ(256U, memory_->Size());
+ ASSERT_TRUE(memory_->GetPtr(0) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(1) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(255) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(256) == nullptr);
+
+ uint8_t* data = memory_->GetPtr(0);
+ for (size_t i = 0; i < memory_->Size(); i++) {
+ data[i] = i;
+ }
+
+ std::vector<uint8_t> buffer(memory_->Size());
+ ASSERT_TRUE(memory_->Read(0, buffer.data(), buffer.size()));
+ for (size_t i = 0; i < buffer.size(); i++) {
+ ASSERT_EQ(i, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryBufferTest, read_failures) {
+ memory_->Resize(100);
+ std::vector<uint8_t> buffer(200);
+ ASSERT_FALSE(memory_->Read(0, buffer.data(), 101));
+ ASSERT_FALSE(memory_->Read(100, buffer.data(), 1));
+ ASSERT_FALSE(memory_->Read(101, buffer.data(), 2));
+ ASSERT_FALSE(memory_->Read(99, buffer.data(), 2));
+ ASSERT_TRUE(memory_->Read(99, buffer.data(), 1));
+}
+
+TEST_F(MemoryBufferTest, read_failure_overflow) {
+ memory_->Resize(100);
+ std::vector<uint8_t> buffer(200);
+
+ ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
index afb1029..2026acc 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -21,6 +21,8 @@
#include "MemoryFake.h"
+namespace unwindstack {
+
void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
for (size_t i = 0; i < length; i++, addr++) {
@@ -44,3 +46,5 @@
}
return true;
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index e05736b..d374261 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -23,7 +23,9 @@
#include <vector>
#include <unordered_map>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
class MemoryFake : public Memory {
public:
@@ -75,4 +77,18 @@
}
};
+class MemoryFakeRemote : public MemoryRemote {
+ public:
+ MemoryFakeRemote() : MemoryRemote(0) {}
+ virtual ~MemoryFakeRemote() = default;
+
+ protected:
+ bool PtraceRead(uint64_t, long* value) override {
+ *value = 0;
+ return true;
+ }
+};
+
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index 870ca19..a204bae 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -14,11 +14,16 @@
* limitations under the License.
*/
+#include <string>
+#include <vector>
+
#include <android-base/test_utils.h>
#include <android-base/file.h>
#include <gtest/gtest.h>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
class MemoryFileTest : public ::testing::Test {
protected:
@@ -39,7 +44,7 @@
TemporaryFile* tf_ = nullptr;
};
-TEST_F(MemoryFileTest, offset_0) {
+TEST_F(MemoryFileTest, init_offset_0) {
WriteTestData();
ASSERT_TRUE(memory_.Init(tf_->path, 0));
@@ -49,7 +54,7 @@
ASSERT_STREQ("0123456789", buffer.data());
}
-TEST_F(MemoryFileTest, offset_non_zero) {
+TEST_F(MemoryFileTest, init_offset_non_zero) {
WriteTestData();
ASSERT_TRUE(memory_.Init(tf_->path, 10));
@@ -59,7 +64,7 @@
ASSERT_STREQ("abcdefghij", buffer.data());
}
-TEST_F(MemoryFileTest, offset_non_zero_larger_than_pagesize) {
+TEST_F(MemoryFileTest, init_offset_non_zero_larger_than_pagesize) {
size_t pagesize = getpagesize();
std::string large_string;
for (size_t i = 0; i < pagesize; i++) {
@@ -75,7 +80,7 @@
ASSERT_STREQ("abcdefgh", buffer.data());
}
-TEST_F(MemoryFileTest, offset_pagesize_aligned) {
+TEST_F(MemoryFileTest, init_offset_pagesize_aligned) {
size_t pagesize = getpagesize();
std::string data;
for (size_t i = 0; i < 2 * pagesize; i++) {
@@ -96,7 +101,7 @@
ASSERT_STREQ(expected_str.c_str(), buffer.data());
}
-TEST_F(MemoryFileTest, offset_pagesize_aligned_plus_extra) {
+TEST_F(MemoryFileTest, init_offset_pagesize_aligned_plus_extra) {
size_t pagesize = getpagesize();
std::string data;
for (size_t i = 0; i < 2 * pagesize; i++) {
@@ -117,6 +122,23 @@
ASSERT_STREQ(expected_str.c_str(), buffer.data());
}
+TEST_F(MemoryFileTest, init_offset_greater_than_filesize) {
+ size_t pagesize = getpagesize();
+ std::string data;
+ uint64_t file_size = 2 * pagesize + pagesize / 2;
+ for (size_t i = 0; i < file_size; i++) {
+ data += static_cast<char>((i / pagesize) + '0');
+ }
+ ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+
+ // Check offset > file size fails and aligned_offset > file size.
+ ASSERT_FALSE(memory_.Init(tf_->path, file_size + 2 * pagesize));
+ // Check offset == filesize fails.
+ ASSERT_FALSE(memory_.Init(tf_->path, file_size));
+ // Check aligned_offset < filesize, but offset > filesize fails.
+ ASSERT_FALSE(memory_.Init(tf_->path, 2 * pagesize + pagesize / 2 + pagesize / 4));
+}
+
TEST_F(MemoryFileTest, read_error) {
std::string data;
for (size_t i = 0; i < 5000; i++) {
@@ -137,32 +159,9 @@
ASSERT_TRUE(memory_.Read(4990, buffer.data(), 10));
ASSERT_FALSE(memory_.Read(4999, buffer.data(), 2));
ASSERT_TRUE(memory_.Read(4999, buffer.data(), 1));
-}
-TEST_F(MemoryFileTest, read_string) {
- std::string value("name_in_file");
- ASSERT_TRUE(android::base::WriteFully(tf_->fd, value.c_str(), value.size() + 1));
-
- std::string name;
- ASSERT_TRUE(memory_.Init(tf_->path, 0));
- ASSERT_TRUE(memory_.ReadString(0, &name));
- ASSERT_EQ("name_in_file", name);
- ASSERT_TRUE(memory_.ReadString(5, &name));
- ASSERT_EQ("in_file", name);
-}
-
-TEST_F(MemoryFileTest, read_string_error) {
- std::vector<uint8_t> buffer = { 0x23, 0x32, 0x45 };
- ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size()));
-
- std::string name;
- ASSERT_TRUE(memory_.Init(tf_->path, 0));
-
- // Read from a non-existant address.
- ASSERT_FALSE(memory_.ReadString(100, &name));
-
- // This should fail because there is no terminating \0
- ASSERT_FALSE(memory_.ReadString(0, &name));
+ // Check that overflow fails properly.
+ ASSERT_FALSE(memory_.Read(UINT64_MAX - 100, buffer.data(), 200));
}
TEST_F(MemoryFileTest, read_past_file_within_mapping) {
@@ -272,3 +271,5 @@
ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i;
}
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index 0ba5f1c..73eebdd 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -21,7 +21,9 @@
#include <gtest/gtest.h>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
TEST(MemoryLocalTest, read) {
std::vector<uint8_t> src(1024);
@@ -47,25 +49,6 @@
}
}
-TEST(MemoryLocalTest, read_string) {
- std::string name("string_in_memory");
-
- MemoryLocal local;
-
- std::vector<uint8_t> dst(1024);
- std::string dst_name;
- ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(name.c_str()), &dst_name));
- ASSERT_EQ("string_in_memory", dst_name);
-
- ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name));
- ASSERT_EQ("in_memory", dst_name);
-
- ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 10));
- ASSERT_EQ("in_memory", dst_name);
-
- ASSERT_FALSE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 9));
-}
-
TEST(MemoryLocalTest, read_illegal) {
MemoryLocal local;
@@ -73,3 +56,15 @@
ASSERT_FALSE(local.Read(0, dst.data(), 1));
ASSERT_FALSE(local.Read(0, dst.data(), 100));
}
+
+TEST(MemoryLocalTest, read_overflow) {
+ MemoryLocal local;
+
+ // On 32 bit this test doesn't necessarily cause an overflow. The 64 bit
+ // version will always go through the overflow check.
+ std::vector<uint8_t> dst(100);
+ uint64_t value;
+ ASSERT_FALSE(local.Read(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index d636ec4..680fae9 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -17,29 +17,25 @@
#include <stdint.h>
#include <string.h>
+#include <memory>
#include <vector>
#include <gtest/gtest.h>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
#include "MemoryFake.h"
-class MemoryRangeTest : public ::testing::Test {
- protected:
- void SetUp() override {
- memory_ = new MemoryFake;
- }
+namespace unwindstack {
- MemoryFake* memory_;
-};
-
-TEST_F(MemoryRangeTest, read) {
+TEST(MemoryRangeTest, read) {
std::vector<uint8_t> src(1024);
memset(src.data(), 0x4c, 1024);
- memory_->SetMemory(9001, src);
+ MemoryFake* memory_fake = new MemoryFake;
+ std::shared_ptr<Memory> process_memory(memory_fake);
+ memory_fake->SetMemory(9001, src);
- MemoryRange range(memory_, 9001, 9001 + src.size());
+ MemoryRange range(process_memory, 9001, 9001 + src.size());
std::vector<uint8_t> dst(1024);
ASSERT_TRUE(range.Read(0, dst.data(), src.size()));
@@ -48,12 +44,14 @@
}
}
-TEST_F(MemoryRangeTest, read_near_limit) {
+TEST(MemoryRangeTest, read_near_limit) {
std::vector<uint8_t> src(4096);
memset(src.data(), 0x4c, 4096);
- memory_->SetMemory(1000, src);
+ MemoryFake* memory_fake = new MemoryFake;
+ std::shared_ptr<Memory> process_memory(memory_fake);
+ memory_fake->SetMemory(1000, src);
- MemoryRange range(memory_, 1000, 2024);
+ MemoryRange range(process_memory, 1000, 2024);
std::vector<uint8_t> dst(1024);
ASSERT_TRUE(range.Read(1020, dst.data(), 4));
@@ -65,35 +63,17 @@
ASSERT_FALSE(range.Read(1020, dst.data(), 5));
ASSERT_FALSE(range.Read(1024, dst.data(), 1));
ASSERT_FALSE(range.Read(1024, dst.data(), 1024));
+
+ // Verify that reading up to the end works.
+ ASSERT_TRUE(range.Read(1020, dst.data(), 4));
}
-TEST_F(MemoryRangeTest, read_string_past_end) {
- std::string name("0123456789");
- memory_->SetMemory(0, name);
+TEST(MemoryRangeTest, read_overflow) {
+ std::vector<uint8_t> buffer(100);
- // Verify a read past the range fails.
- MemoryRange range(memory_, 0, 5);
- std::string dst_name;
- ASSERT_FALSE(range.ReadString(0, &dst_name));
+ std::shared_ptr<Memory> process_memory(new MemoryFakeAlwaysReadZero);
+ std::unique_ptr<MemoryRange> overflow(new MemoryRange(process_memory, 100, 200));
+ ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100));
}
-TEST_F(MemoryRangeTest, read_string_to_end) {
- std::string name("0123456789");
- memory_->SetMemory(30, name);
-
- // Verify the range going to the end of the string works.
- MemoryRange range(memory_, 30, 30 + name.size() + 1);
- std::string dst_name;
- ASSERT_TRUE(range.ReadString(0, &dst_name));
- ASSERT_EQ("0123456789", dst_name);
-}
-
-TEST_F(MemoryRangeTest, read_string_fencepost) {
- std::string name("0123456789");
- memory_->SetMemory(10, name);
-
- // Verify the range set to one byte less than the end of the string fails.
- MemoryRange range(memory_, 10, 10 + name.size());
- std::string dst_name;
- ASSERT_FALSE(range.ReadString(0, &dst_name));
-}
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index 7664c3e..a66d0c5 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -22,7 +22,6 @@
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/types.h>
-#include <time.h>
#include <unistd.h>
#include <vector>
@@ -31,31 +30,21 @@
#include <android-base/file.h>
#include <gtest/gtest.h>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+#include "TestUtils.h"
+
+namespace unwindstack {
class MemoryRemoteTest : public ::testing::Test {
protected:
- static uint64_t NanoTime() {
- struct timespec t = { 0, 0 };
- clock_gettime(CLOCK_MONOTONIC, &t);
- return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
- }
-
static bool Attach(pid_t pid) {
if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
return false;
}
- uint64_t start = NanoTime();
- siginfo_t si;
- while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
- if ((NanoTime() - start) > 10 * NS_PER_SEC) {
- printf("%d: Failed to stop after 10 seconds.\n", pid);
- return false;
- }
- usleep(30);
- }
- return true;
+ return TestQuiescePid(pid);
}
static bool Detach(pid_t pid) {
@@ -75,6 +64,7 @@
exit(1);
}
ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
ASSERT_TRUE(Attach(pid));
@@ -87,8 +77,6 @@
}
ASSERT_TRUE(Detach(pid));
-
- kill(pid, SIGKILL);
}
TEST_F(MemoryRemoteTest, read_fail) {
@@ -106,6 +94,7 @@
exit(1);
}
ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
ASSERT_TRUE(Attach(pid));
@@ -121,11 +110,20 @@
ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 1, dst.data(), 1));
ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 4, dst.data(), 8));
+ // Check overflow condition is caught properly.
+ ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
+
ASSERT_EQ(0, munmap(src, pagesize));
ASSERT_TRUE(Detach(pid));
+}
- kill(pid, SIGKILL);
+TEST_F(MemoryRemoteTest, read_overflow) {
+ MemoryFakeRemote remote;
+
+ // Check overflow condition is caught properly.
+ std::vector<uint8_t> dst(200);
+ ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
}
TEST_F(MemoryRemoteTest, read_illegal) {
@@ -135,6 +133,7 @@
exit(1);
}
ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
ASSERT_TRUE(Attach(pid));
@@ -145,6 +144,6 @@
ASSERT_FALSE(remote.Read(0, dst.data(), 100));
ASSERT_TRUE(Detach(pid));
-
- kill(pid, SIGKILL);
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
new file mode 100644
index 0000000..4a9ed9f
--- /dev/null
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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 <string.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+TEST(MemoryTest, read32) {
+ MemoryFakeAlwaysReadZero memory;
+
+ uint32_t data = 0xffffffff;
+ ASSERT_TRUE(memory.Read32(0, &data));
+ ASSERT_EQ(0U, data);
+}
+
+TEST(MemoryTest, read64) {
+ MemoryFakeAlwaysReadZero memory;
+
+ uint64_t data = 0xffffffffffffffffULL;
+ ASSERT_TRUE(memory.Read64(0, &data));
+ ASSERT_EQ(0U, data);
+}
+
+struct FakeStruct {
+ int one;
+ bool two;
+ uint32_t three;
+ uint64_t four;
+};
+
+TEST(MemoryTest, read_field) {
+ MemoryFakeAlwaysReadZero memory;
+
+ FakeStruct data;
+ memset(&data, 0xff, sizeof(data));
+ ASSERT_TRUE(memory.ReadField(0, &data, &data.one, sizeof(data.one)));
+ ASSERT_EQ(0, data.one);
+
+ memset(&data, 0xff, sizeof(data));
+ ASSERT_TRUE(memory.ReadField(0, &data, &data.two, sizeof(data.two)));
+ ASSERT_FALSE(data.two);
+
+ memset(&data, 0xff, sizeof(data));
+ ASSERT_TRUE(memory.ReadField(0, &data, &data.three, sizeof(data.three)));
+ ASSERT_EQ(0U, data.three);
+
+ memset(&data, 0xff, sizeof(data));
+ ASSERT_TRUE(memory.ReadField(0, &data, &data.four, sizeof(data.four)));
+ ASSERT_EQ(0U, data.four);
+}
+
+TEST(MemoryTest, read_field_fails) {
+ MemoryFakeAlwaysReadZero memory;
+
+ FakeStruct data;
+ memset(&data, 0xff, sizeof(data));
+
+ ASSERT_FALSE(memory.ReadField(UINT64_MAX, &data, &data.three, sizeof(data.three)));
+
+ // Field and start reversed, should fail.
+ ASSERT_FALSE(memory.ReadField(100, &data.two, &data, sizeof(data.two)));
+ ASSERT_FALSE(memory.ReadField(0, &data.two, &data, sizeof(data.two)));
+}
+
+TEST(MemoryTest, read_string) {
+ std::string name("string_in_memory");
+
+ MemoryFake memory;
+
+ memory.SetMemory(100, name.c_str(), name.size() + 1);
+
+ std::string dst_name;
+ ASSERT_TRUE(memory.ReadString(100, &dst_name));
+ ASSERT_EQ("string_in_memory", dst_name);
+
+ ASSERT_TRUE(memory.ReadString(107, &dst_name));
+ ASSERT_EQ("in_memory", dst_name);
+
+ // Set size greater than string.
+ ASSERT_TRUE(memory.ReadString(107, &dst_name, 10));
+ ASSERT_EQ("in_memory", dst_name);
+
+ ASSERT_FALSE(memory.ReadString(107, &dst_name, 9));
+}
+
+TEST(MemoryTest, read_string_error) {
+ std::string name("short");
+
+ MemoryFake memory;
+
+ // Save everything except the terminating '\0'.
+ memory.SetMemory(0, name.c_str(), name.size());
+
+ std::string dst_name;
+ // Read from a non-existant address.
+ ASSERT_FALSE(memory.ReadString(100, &dst_name));
+
+ // This should fail because there is no terminating '\0'.
+ ASSERT_FALSE(memory.ReadString(0, &dst_name));
+
+ // This should pass because there is a terminating '\0'.
+ memory.SetData8(name.size(), '\0');
+ ASSERT_TRUE(memory.ReadString(0, &dst_name));
+ ASSERT_EQ("short", dst_name);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
new file mode 100644
index 0000000..c76ecaa
--- /dev/null
+++ b/libunwindstack/tests/RegsFake.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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_TESTS_REGS_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class RegsFake : public RegsImpl<TypeParam> {
+ public:
+ RegsFake(uint16_t total_regs, uint16_t sp_reg)
+ : RegsImpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+ virtual ~RegsFake() = default;
+
+ uint32_t MachineType() override { return 0; }
+
+ uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+ void SetFromRaw() override {}
+ bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
+ bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
new file mode 100644
index 0000000..85192d5
--- /dev/null
+++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+#include "Machine.h"
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class RegsStepIfSignalHandlerTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ elf_memory_ = new MemoryFake;
+ elf_.reset(new Elf(elf_memory_));
+ }
+
+ void ArmStepIfSignalHandlerNonRt(uint32_t pc_data);
+ void ArmStepIfSignalHandlerRt(uint32_t pc_data);
+
+ MemoryFake* elf_memory_;
+ MemoryFake process_memory_;
+ std::unique_ptr<Elf> elf_;
+};
+
+void RegsStepIfSignalHandlerTest::ArmStepIfSignalHandlerNonRt(uint32_t pc_data) {
+ uint64_t addr = 0x1000;
+ RegsArm regs;
+ regs[ARM_REG_PC] = 0x5000;
+ regs[ARM_REG_SP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData32(0x5000, pc_data);
+
+ for (uint64_t index = 0; index <= 30; index++) {
+ process_memory_.SetData32(addr + index * 4, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x5000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x100U, regs[ARM_REG_SP]);
+ EXPECT_EQ(0x120U, regs[ARM_REG_PC]);
+ EXPECT_EQ(0x100U, regs.sp());
+ EXPECT_EQ(0x120U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm_step_if_signal_handler_non_rt) {
+ // Form 1
+ ArmStepIfSignalHandlerNonRt(0xe3a07077);
+
+ // Form 2
+ ArmStepIfSignalHandlerNonRt(0xef900077);
+
+ // Form 3
+ ArmStepIfSignalHandlerNonRt(0xdf002777);
+}
+
+void RegsStepIfSignalHandlerTest::ArmStepIfSignalHandlerRt(uint32_t pc_data) {
+ uint64_t addr = 0x1000;
+ RegsArm regs;
+ regs[ARM_REG_PC] = 0x5000;
+ regs[ARM_REG_SP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData32(0x5000, pc_data);
+
+ for (uint64_t index = 0; index <= 100; index++) {
+ process_memory_.SetData32(addr + index * 4, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x5000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x350U, regs[ARM_REG_SP]);
+ EXPECT_EQ(0x370U, regs[ARM_REG_PC]);
+ EXPECT_EQ(0x350U, regs.sp());
+ EXPECT_EQ(0x370U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm_step_if_signal_handler_rt) {
+ // Form 1
+ ArmStepIfSignalHandlerRt(0xe3a070ad);
+
+ // Form 2
+ ArmStepIfSignalHandlerRt(0xef9000ad);
+
+ // Form 3
+ ArmStepIfSignalHandlerRt(0xdf0027ad);
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm64_step_if_signal_handler) {
+ uint64_t addr = 0x1000;
+ RegsArm64 regs;
+ regs[ARM64_REG_PC] = 0x8000;
+ regs[ARM64_REG_SP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData64(0x8000, 0xd4000001d2801168ULL);
+
+ 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(0x460U, regs[ARM64_REG_SP]);
+ EXPECT_EQ(0x470U, regs[ARM64_REG_PC]);
+ EXPECT_EQ(0x460U, regs.sp());
+ EXPECT_EQ(0x470U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_step_if_signal_handler_no_siginfo) {
+ uint64_t addr = 0xa00;
+ RegsX86 regs;
+ regs[X86_REG_EIP] = 0x4100;
+ regs[X86_REG_ESP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData64(0x4100, 0x80cd00000077b858ULL);
+ for (uint64_t index = 0; index <= 25; index++) {
+ process_memory_.SetData32(addr + index * 4, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x4100, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x70U, regs[X86_REG_EBP]);
+ EXPECT_EQ(0x80U, regs[X86_REG_ESP]);
+ EXPECT_EQ(0x90U, regs[X86_REG_EBX]);
+ EXPECT_EQ(0xa0U, regs[X86_REG_EDX]);
+ EXPECT_EQ(0xb0U, regs[X86_REG_ECX]);
+ EXPECT_EQ(0xc0U, regs[X86_REG_EAX]);
+ EXPECT_EQ(0xf0U, regs[X86_REG_EIP]);
+ EXPECT_EQ(0x80U, regs.sp());
+ EXPECT_EQ(0xf0U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_step_if_signal_handler_siginfo) {
+ uint64_t addr = 0xa00;
+ RegsX86 regs;
+ regs[X86_REG_EIP] = 0x4100;
+ regs[X86_REG_ESP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData64(0x4100, 0x0080cd000000adb8ULL);
+ addr += 8;
+ // Pointer to ucontext data.
+ process_memory_.SetData32(addr, 0x8100);
+
+ addr = 0x8100;
+ for (uint64_t index = 0; index <= 30; index++) {
+ process_memory_.SetData32(addr + index * 4, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x4100, elf_.get(), &process_memory_));
+ EXPECT_EQ(0xb0U, regs[X86_REG_EBP]);
+ EXPECT_EQ(0xc0U, regs[X86_REG_ESP]);
+ EXPECT_EQ(0xd0U, regs[X86_REG_EBX]);
+ EXPECT_EQ(0xe0U, regs[X86_REG_EDX]);
+ EXPECT_EQ(0xf0U, regs[X86_REG_ECX]);
+ EXPECT_EQ(0x100U, regs[X86_REG_EAX]);
+ EXPECT_EQ(0x130U, regs[X86_REG_EIP]);
+ EXPECT_EQ(0xc0U, regs.sp());
+ EXPECT_EQ(0x130U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_64_step_if_signal_handler) {
+ uint64_t addr = 0x500;
+ RegsX86_64 regs;
+ regs[X86_64_REG_RIP] = 0x7000;
+ regs[X86_64_REG_RSP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData64(0x7000, 0x0f0000000fc0c748);
+ elf_memory_->SetData16(0x7008, 0x0f05);
+
+ for (uint64_t index = 0; index <= 30; index++) {
+ process_memory_.SetData64(addr + index * 8, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x7000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x140U, regs[X86_64_REG_RSP]);
+ EXPECT_EQ(0x150U, regs[X86_64_REG_RIP]);
+ EXPECT_EQ(0x140U, regs.sp());
+ EXPECT_EQ(0x150U, regs.pc());
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 0dac278..f549a50 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -18,13 +18,15 @@
#include <gtest/gtest.h>
-#include "Elf.h"
-#include "ElfInterface.h"
-#include "MapInfo.h"
-#include "Regs.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Regs.h>
#include "MemoryFake.h"
+namespace unwindstack {
+
class ElfFake : public Elf {
public:
ElfFake(Memory* memory) : Elf(memory) { valid_ = true; }
@@ -48,15 +50,19 @@
};
template <typename TypeParam>
-class RegsTestTmpl : public RegsTmpl<TypeParam> {
+class RegsTestImpl : public RegsImpl<TypeParam> {
public:
- RegsTestTmpl(uint16_t total_regs, uint16_t regs_sp)
- : RegsTmpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
- RegsTestTmpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
- : RegsTmpl<TypeParam>(total_regs, regs_sp, return_loc) {}
- virtual ~RegsTestTmpl() = default;
+ RegsTestImpl(uint16_t total_regs, uint16_t regs_sp)
+ : RegsImpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+ RegsTestImpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
+ : RegsImpl<TypeParam>(total_regs, regs_sp, return_loc) {}
+ virtual ~RegsTestImpl() = default;
- uint64_t GetAdjustedPc(uint64_t, Elf*) { return 0; }
+ uint32_t MachineType() override { return 0; }
+
+ uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+ void SetFromRaw() override {}
+ bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
};
class RegsTest : public ::testing::Test {
@@ -69,10 +75,7 @@
}
template <typename AddressType>
- void regs_rel_pc();
-
- template <typename AddressType>
- void regs_return_address_register();
+ void RegsReturnAddressRegister();
ElfInterfaceFake* elf_interface_;
MemoryFake* memory_;
@@ -80,7 +83,7 @@
};
TEST_F(RegsTest, regs32) {
- RegsTestTmpl<uint32_t> regs32(50, 10);
+ RegsTestImpl<uint32_t> regs32(50, 10);
ASSERT_EQ(50U, regs32.total_regs());
ASSERT_EQ(10U, regs32.sp_reg());
@@ -103,7 +106,7 @@
}
TEST_F(RegsTest, regs64) {
- RegsTestTmpl<uint64_t> regs64(30, 12);
+ RegsTestImpl<uint64_t> regs64(30, 12);
ASSERT_EQ(30U, regs64.total_regs());
ASSERT_EQ(12U, regs64.sp_reg());
@@ -126,28 +129,8 @@
}
template <typename AddressType>
-void RegsTest::regs_rel_pc() {
- RegsTestTmpl<AddressType> regs(30, 12);
-
- elf_interface_->set_load_bias(0);
- MapInfo map_info{.start = 0x1000, .end = 0x2000};
- regs.set_pc(0x1101);
- ASSERT_EQ(0x101U, regs.GetRelPc(elf_.get(), &map_info));
- elf_interface_->set_load_bias(0x3000);
- ASSERT_EQ(0x3101U, regs.GetRelPc(elf_.get(), &map_info));
-}
-
-TEST_F(RegsTest, regs32_rel_pc) {
- regs_rel_pc<uint32_t>();
-}
-
-TEST_F(RegsTest, regs64_rel_pc) {
- regs_rel_pc<uint64_t>();
-}
-
-template <typename AddressType>
-void RegsTest::regs_return_address_register() {
- RegsTestTmpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
+void RegsTest::RegsReturnAddressRegister() {
+ RegsTestImpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
regs[5] = 0x12345;
uint64_t value;
@@ -156,15 +139,15 @@
}
TEST_F(RegsTest, regs32_return_address_register) {
- regs_return_address_register<uint32_t>();
+ RegsReturnAddressRegister<uint32_t>();
}
TEST_F(RegsTest, regs64_return_address_register) {
- regs_return_address_register<uint64_t>();
+ RegsReturnAddressRegister<uint64_t>();
}
TEST_F(RegsTest, regs32_return_address_sp_offset) {
- RegsTestTmpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
+ RegsTestImpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
regs.set_sp(0x2002);
memory_->SetData32(0x2000, 0x12345678);
@@ -174,7 +157,7 @@
}
TEST_F(RegsTest, regs64_return_address_sp_offset) {
- RegsTestTmpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
+ RegsTestImpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
regs.set_sp(0x2008);
memory_->SetData64(0x2000, 0x12345678aabbccddULL);
@@ -249,18 +232,60 @@
MapInfo map_info{.start = 0x1000, .end = 0x2000};
regs_arm.set_pc(0x1500);
- ASSERT_EQ(0x500U, regs_arm.GetRelPc(&invalid_elf, &map_info));
+ ASSERT_EQ(0x500U, invalid_elf.GetRelPc(regs_arm.pc(), &map_info));
ASSERT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, &invalid_elf));
regs_arm64.set_pc(0x1600);
- ASSERT_EQ(0x600U, regs_arm64.GetRelPc(&invalid_elf, &map_info));
+ ASSERT_EQ(0x600U, invalid_elf.GetRelPc(regs_arm64.pc(), &map_info));
ASSERT_EQ(0x600U, regs_arm64.GetAdjustedPc(0x600U, &invalid_elf));
regs_x86.set_pc(0x1700);
- ASSERT_EQ(0x700U, regs_x86.GetRelPc(&invalid_elf, &map_info));
+ ASSERT_EQ(0x700U, invalid_elf.GetRelPc(regs_x86.pc(), &map_info));
ASSERT_EQ(0x700U, regs_x86.GetAdjustedPc(0x700U, &invalid_elf));
regs_x86_64.set_pc(0x1800);
- ASSERT_EQ(0x800U, regs_x86_64.GetRelPc(&invalid_elf, &map_info));
+ ASSERT_EQ(0x800U, invalid_elf.GetRelPc(regs_x86_64.pc(), &map_info));
ASSERT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, &invalid_elf));
}
+
+TEST_F(RegsTest, arm_set_from_raw) {
+ RegsArm arm;
+ uint32_t* regs = reinterpret_cast<uint32_t*>(arm.RawData());
+ regs[13] = 0x100;
+ regs[15] = 0x200;
+ arm.SetFromRaw();
+ EXPECT_EQ(0x100U, arm.sp());
+ EXPECT_EQ(0x200U, arm.pc());
+}
+
+TEST_F(RegsTest, arm64_set_from_raw) {
+ RegsArm64 arm64;
+ uint64_t* regs = reinterpret_cast<uint64_t*>(arm64.RawData());
+ regs[31] = 0xb100000000ULL;
+ regs[32] = 0xc200000000ULL;
+ arm64.SetFromRaw();
+ EXPECT_EQ(0xb100000000U, arm64.sp());
+ EXPECT_EQ(0xc200000000U, arm64.pc());
+}
+
+TEST_F(RegsTest, x86_set_from_raw) {
+ RegsX86 x86;
+ uint32_t* regs = reinterpret_cast<uint32_t*>(x86.RawData());
+ regs[4] = 0x23450000;
+ regs[8] = 0xabcd0000;
+ x86.SetFromRaw();
+ EXPECT_EQ(0x23450000U, x86.sp());
+ EXPECT_EQ(0xabcd0000U, x86.pc());
+}
+
+TEST_F(RegsTest, x86_64_set_from_raw) {
+ RegsX86_64 x86_64;
+ uint64_t* regs = reinterpret_cast<uint64_t*>(x86_64.RawData());
+ regs[7] = 0x1200000000ULL;
+ regs[16] = 0x4900000000ULL;
+ x86_64.SetFromRaw();
+ EXPECT_EQ(0x1200000000U, x86_64.sp());
+ EXPECT_EQ(0x4900000000U, x86_64.pc());
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
new file mode 100644
index 0000000..da258a6
--- /dev/null
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2016 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 <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+#include "Symbols.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class SymbolsTest : public ::testing::Test {
+ protected:
+ void SetUp() override { memory_.Clear(); }
+
+ void InitSym(TypeParam* sym, uint32_t st_value, uint32_t st_size, uint32_t st_name) {
+ memset(sym, 0, sizeof(*sym));
+ sym->st_info = STT_FUNC;
+ sym->st_value = st_value;
+ sym->st_size = st_size;
+ sym->st_name = st_name;
+ sym->st_shndx = SHN_COMMON;
+ }
+
+ MemoryFake memory_;
+};
+TYPED_TEST_CASE_P(SymbolsTest);
+
+TYPED_TEST_P(SymbolsTest, function_bounds_check) {
+ Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+ TypeParam sym;
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ uint64_t offset = 0x1000;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0xfU, func_offset);
+
+ // Check one before and one after the function.
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, 0, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, no_symbol) {
+ Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+ TypeParam sym;
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ uint64_t offset = 0x1000;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+ // First verify that we can get the name.
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0U, func_offset);
+
+ // Now modify the info field so it's no longer a function.
+ sym.st_info = 0;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ // Clear the cache to force the symbol data to be re-read.
+ symbols.ClearCache();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+
+ // Set the function back, and set the shndx to UNDEF.
+ sym.st_info = STT_FUNC;
+ sym.st_shndx = SHN_UNDEF;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ // Clear the cache to force the symbol data to be re-read.
+ symbols.ClearCache();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, multiple_entries) {
+ Symbols symbols(0x1000, sizeof(TypeParam) * 3, sizeof(TypeParam), 0x2000, 0x500);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+ std::string fake_name;
+
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_one";
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x3004, 0x200, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_two";
+ this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0xa010, 0x20, 0x230);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_three";
+ this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_two", name);
+ ASSERT_EQ(1U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_one", name);
+ ASSERT_EQ(4U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_three", name);
+ ASSERT_EQ(1U, func_offset);
+
+ // Reget some of the others to verify getting one function name doesn't
+ // affect any of the next calls.
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_one", name);
+ ASSERT_EQ(8U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_two", name);
+ ASSERT_EQ(4U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_three", name);
+ ASSERT_EQ(0xaU, func_offset);
+}
+
+TYPED_TEST_P(SymbolsTest, multiple_entries_nonstandard_size) {
+ uint64_t entry_size = sizeof(TypeParam) + 5;
+ Symbols symbols(0x1000, entry_size * 3, entry_size, 0x2000, 0x500);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+ std::string fake_name;
+
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_one";
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+ offset += entry_size;
+
+ this->InitSym(&sym, 0x3004, 0x200, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_two";
+ this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
+ offset += entry_size;
+
+ this->InitSym(&sym, 0xa010, 0x20, 0x230);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_three";
+ this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_two", name);
+ ASSERT_EQ(1U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_one", name);
+ ASSERT_EQ(4U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_three", name);
+ ASSERT_EQ(1U, func_offset);
+}
+
+TYPED_TEST_P(SymbolsTest, load_bias) {
+ Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+ TypeParam sym;
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ uint64_t offset = 0x1000;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+ // Set a non-zero load_bias that should be a valid function offset.
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(4U, func_offset);
+
+ // Set a flag that should cause the load_bias to be ignored.
+ sym.st_shndx = SHN_ABS;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ // Clear the cache to force the symbol data to be re-read.
+ symbols.ClearCache();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, symtab_value_out_of_bounds) {
+ Symbols symbols_end_at_100(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x100);
+ Symbols symbols_end_at_200(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x200);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+
+ this->InitSym(&sym, 0x5000, 0x10, 0xfb);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x3000, 0x10, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ // Put the name across the end of the tab.
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x20fb, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ // Verify that we can get the function name properly for both entries.
+ ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0U, func_offset);
+ ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function", name);
+ ASSERT_EQ(0U, func_offset);
+
+ // Now use the symbol table that ends at 0x100.
+ ASSERT_FALSE(
+ symbols_end_at_100.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(
+ symbols_end_at_100.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+}
+
+// Verify the entire func table is cached.
+TYPED_TEST_P(SymbolsTest, symtab_read_cached) {
+ Symbols symbols(0x1000, 3 * sizeof(TypeParam), sizeof(TypeParam), 0xa000, 0x1000);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+
+ // Make sure that these entries are not in ascending order.
+ this->InitSym(&sym, 0x5000, 0x10, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x2000, 0x300, 0x200);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x1000, 0x100, 0x300);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ // Do call that should cache all of the entries (except the string data).
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+ this->memory_.Clear();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+
+ // Clear the memory and only put the symbol data string data in memory.
+ this->memory_.Clear();
+
+ std::string fake_name;
+ fake_name = "first_entry";
+ this->memory_.SetMemory(0xa100, fake_name.c_str(), fake_name.size() + 1);
+ fake_name = "second_entry";
+ this->memory_.SetMemory(0xa200, fake_name.c_str(), fake_name.size() + 1);
+ fake_name = "third_entry";
+ this->memory_.SetMemory(0xa300, fake_name.c_str(), fake_name.size() + 1);
+
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("first_entry", name);
+ ASSERT_EQ(1U, func_offset);
+
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("second_entry", name);
+ ASSERT_EQ(2U, func_offset);
+
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("third_entry", name);
+ ASSERT_EQ(3U, func_offset);
+}
+
+REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
+ multiple_entries_nonstandard_size, load_bias, symtab_value_out_of_bounds,
+ symtab_read_cached);
+
+typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
new file mode 100644
index 0000000..8c31aa6
--- /dev/null
+++ b/libunwindstack/tests/TestUtils.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 _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+#define _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+
+#include <signal.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+namespace unwindstack {
+
+class TestScopedPidReaper {
+ public:
+ TestScopedPidReaper(pid_t pid) : pid_(pid) {}
+ ~TestScopedPidReaper() {
+ kill(pid_, SIGKILL);
+ waitpid(pid_, nullptr, 0);
+ }
+
+ private:
+ pid_t pid_;
+};
+
+inline bool TestQuiescePid(pid_t pid) {
+ siginfo_t si;
+ bool ready = false;
+ // Wait for up to 5 seconds.
+ for (size_t i = 0; i < 5000; i++) {
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ ready = true;
+ break;
+ }
+ usleep(1000);
+ }
+ return ready;
+}
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
new file mode 100644
index 0000000..a4f920a
--- /dev/null
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -0,0 +1,317 @@
+/*
+ * 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 <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <atomic>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+#include "TestUtils.h"
+
+namespace unwindstack {
+
+static std::atomic_bool g_ready;
+static volatile bool g_ready_for_remote;
+static volatile bool g_signal_ready_for_remote;
+static std::atomic_bool g_finish;
+static std::atomic_uintptr_t g_ucontext;
+
+static void ResetGlobals() {
+ g_ready = false;
+ g_ready_for_remote = false;
+ g_signal_ready_for_remote = false;
+ g_finish = false;
+ g_ucontext = 0;
+}
+
+static std::vector<const char*> kFunctionOrder{"InnerFunction", "MiddleFunction", "OuterFunction"};
+
+static std::vector<const char*> kFunctionSignalOrder{"SignalInnerFunction", "SignalMiddleFunction",
+ "SignalOuterFunction", "InnerFunction",
+ "MiddleFunction", "OuterFunction"};
+
+static void SignalHandler(int, siginfo_t*, void* sigcontext) {
+ g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
+ while (!g_finish.load()) {
+ }
+}
+
+extern "C" void SignalInnerFunction() {
+ g_signal_ready_for_remote = true;
+ while (!g_finish.load()) {
+ }
+}
+
+extern "C" void SignalMiddleFunction() {
+ SignalInnerFunction();
+}
+
+extern "C" void SignalOuterFunction() {
+ SignalMiddleFunction();
+}
+
+static void SignalCallerHandler(int, siginfo_t*, void*) {
+ SignalOuterFunction();
+}
+
+static std::string ErrorMsg(const std::vector<const char*>& function_names, size_t index,
+ std::stringstream& unwind_stream) {
+ return std::string(
+ "Unwind completed without finding all frames\n"
+ " Looking for function: ") +
+ function_names[index] + "\n" + "Unwind data:\n" + unwind_stream.str();
+}
+
+static void VerifyUnwind(pid_t pid, Maps* maps, Regs* regs,
+ std::vector<const char*>& function_names) {
+ size_t function_name_index = 0;
+
+ auto process_memory = Memory::CreateProcessMemory(pid);
+ std::stringstream unwind_stream;
+ unwind_stream << std::hex;
+ for (size_t frame_num = 0; frame_num < 64; frame_num++) {
+ ASSERT_NE(0U, regs->pc()) << ErrorMsg(function_names, function_name_index, unwind_stream);
+ MapInfo* map_info = maps->Find(regs->pc());
+ ASSERT_TRUE(map_info != nullptr) << ErrorMsg(function_names, function_name_index, unwind_stream);
+
+ Elf* elf = map_info->GetElf(process_memory, true);
+ uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
+ uint64_t adjusted_rel_pc = rel_pc;
+ if (frame_num != 0) {
+ adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
+ }
+ unwind_stream << " PC: 0x" << regs->pc() << " Rel: 0x" << adjusted_rel_pc;
+ unwind_stream << " Map: ";
+ if (!map_info->name.empty()) {
+ unwind_stream << map_info->name;
+ } else {
+ unwind_stream << " anonymous";
+ }
+ unwind_stream << "<" << map_info->start << "-" << map_info->end << ">";
+
+ std::string name;
+ uint64_t func_offset;
+ if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
+ if (name == function_names[function_name_index]) {
+ if (++function_name_index == function_names.size()) {
+ return;
+ }
+ }
+ unwind_stream << " " << name;
+ }
+ unwind_stream << "\n";
+ ASSERT_TRUE(elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get()))
+ << ErrorMsg(function_names, function_name_index, unwind_stream);
+ }
+ ASSERT_TRUE(false) << ErrorMsg(function_names, function_name_index, unwind_stream);
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void InnerFunction(bool local) {
+ if (local) {
+ LocalMaps maps;
+ ASSERT_TRUE(maps.Parse());
+ std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+ RegsGetLocal(regs.get());
+
+ VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
+ } else {
+ g_ready_for_remote = true;
+ g_ready = true;
+ while (!g_finish.load()) {
+ }
+ }
+}
+
+extern "C" void MiddleFunction(bool local) {
+ InnerFunction(local);
+}
+
+extern "C" void OuterFunction(bool local) {
+ MiddleFunction(local);
+}
+
+class UnwindTest : public ::testing::Test {
+ public:
+ void SetUp() override { ResetGlobals(); }
+};
+
+TEST_F(UnwindTest, local) {
+ OuterFunction(true);
+}
+
+void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
+ *completed = false;
+ // Need to sleep before attempting first ptrace. Without this, on the
+ // host it becomes impossible to attach and ptrace sets errno to EPERM.
+ usleep(1000);
+ for (size_t i = 0; i < 1000; i++) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
+ ASSERT_TRUE(TestQuiescePid(pid))
+ << "Waiting for process to quiesce failed: " << strerror(errno);
+
+ MemoryRemote memory(pid);
+ // Read the remote value to see if we are ready.
+ bool value;
+ if (memory.Read(addr, &value, sizeof(value)) && value) {
+ *completed = true;
+ }
+ if (!*completed || !leave_attached) {
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+ }
+ if (*completed) {
+ break;
+ }
+ } else {
+ ASSERT_EQ(ESRCH, errno) << "ptrace attach failed with unexpected error: " << strerror(errno);
+ }
+ usleep(5000);
+ }
+}
+
+TEST_F(UnwindTest, remote) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ OuterFunction(false);
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
+
+ bool completed;
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+
+ RemoteMaps maps(pid);
+ ASSERT_TRUE(maps.Parse());
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+ ASSERT_TRUE(regs.get() != nullptr);
+
+ VerifyUnwind(pid, &maps, regs.get(), kFunctionOrder);
+
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+ << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+TEST_F(UnwindTest, from_context) {
+ std::atomic_int tid(0);
+ std::thread thread([&]() {
+ tid = syscall(__NR_gettid);
+ OuterFunction(false);
+ });
+
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalHandler;
+ act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+ // Wait for the tid to get set.
+ for (size_t i = 0; i < 100; i++) {
+ if (tid.load() != 0) {
+ break;
+ }
+ usleep(1000);
+ }
+ ASSERT_NE(0, tid.load());
+ // Portable tgkill method.
+ ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
+
+ // Wait for context data.
+ void* ucontext;
+ for (size_t i = 0; i < 2000; i++) {
+ ucontext = reinterpret_cast<void*>(g_ucontext.load());
+ if (ucontext != nullptr) {
+ break;
+ }
+ usleep(1000);
+ }
+ ASSERT_TRUE(ucontext != nullptr) << "Timed out waiting for thread to respond to signal.";
+
+ LocalMaps maps;
+ ASSERT_TRUE(maps.Parse());
+ std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentMachineType(), ucontext));
+
+ VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
+
+ ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+ g_finish = true;
+ thread.join();
+}
+
+static void RemoteThroughSignal(unsigned int sa_flags) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalCallerHandler;
+ act.sa_flags = SA_RESTART | SA_ONSTACK | sa_flags;
+ ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+
+ OuterFunction(false);
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
+
+ bool completed;
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+ ASSERT_EQ(0, kill(pid, SIGUSR1));
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote), true, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be in signal handler.";
+
+ RemoteMaps maps(pid);
+ ASSERT_TRUE(maps.Parse());
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+ ASSERT_TRUE(regs.get() != nullptr);
+
+ VerifyUnwind(pid, &maps, regs.get(), kFunctionSignalOrder);
+
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+ << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+TEST_F(UnwindTest, remote_through_signal) {
+ RemoteThroughSignal(0);
+}
+
+TEST_F(UnwindTest, remote_through_signal_sa_siginfo) {
+ RemoteThroughSignal(SA_SIGINFO);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/files/elf32.xz b/libunwindstack/tests/files/elf32.xz
new file mode 100644
index 0000000..f25d433
--- /dev/null
+++ b/libunwindstack/tests/files/elf32.xz
Binary files differ
diff --git a/libunwindstack/tests/files/elf64.xz b/libunwindstack/tests/files/elf64.xz
new file mode 100644
index 0000000..eb1618e
--- /dev/null
+++ b/libunwindstack/tests/files/elf64.xz
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
new file mode 100644
index 0000000..3614198
--- /dev/null
+++ b/libunwindstack/tools/unwind.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 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 <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+static bool Attach(pid_t pid) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+ return false;
+ }
+
+ // Allow at least 1 second to attach properly.
+ for (size_t i = 0; i < 1000; i++) {
+ siginfo_t si;
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ return true;
+ }
+ usleep(1000);
+ }
+ printf("%d: Failed to stop.\n", pid);
+ return false;
+}
+
+static bool Detach(pid_t pid) {
+ return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
+}
+
+void DoUnwind(pid_t pid) {
+ unwindstack::RemoteMaps remote_maps(pid);
+ if (!remote_maps.Parse()) {
+ printf("Failed to parse map data.\n");
+ return;
+ }
+
+ unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
+ if (regs == nullptr) {
+ printf("Unable to get remote reg data\n");
+ return;
+ }
+
+ bool bits32 = true;
+ printf("ABI: ");
+ switch (regs->MachineType()) {
+ case EM_ARM:
+ printf("arm");
+ break;
+ case EM_386:
+ printf("x86");
+ break;
+ case EM_AARCH64:
+ printf("arm64");
+ bits32 = false;
+ break;
+ case EM_X86_64:
+ printf("x86_64");
+ bits32 = false;
+ break;
+ default:
+ printf("unknown\n");
+ return;
+ }
+ printf("\n");
+
+ auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+ for (size_t frame_num = 0; frame_num < 64; frame_num++) {
+ if (regs->pc() == 0) {
+ break;
+ }
+ unwindstack::MapInfo* map_info = remote_maps.Find(regs->pc());
+ if (map_info == nullptr) {
+ printf("Failed to find map data for the pc\n");
+ break;
+ }
+
+ unwindstack::Elf* elf = map_info->GetElf(process_memory, true);
+
+ uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
+ uint64_t adjusted_rel_pc = rel_pc;
+ // Don't need to adjust the first frame pc.
+ if (frame_num != 0) {
+ adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
+ }
+
+ std::string name;
+ if (bits32) {
+ printf(" #%02zu pc %08" PRIx64, frame_num, adjusted_rel_pc);
+ } else {
+ printf(" #%02zu pc %016" PRIx64, frame_num, adjusted_rel_pc);
+ }
+ if (!map_info->name.empty()) {
+ printf(" %s", map_info->name.c_str());
+ if (map_info->elf_offset != 0) {
+ printf(" (offset 0x%" PRIx64 ")", map_info->elf_offset);
+ }
+ } else {
+ printf(" <anonymous:%" PRIx64 ">", map_info->offset);
+ }
+ uint64_t func_offset;
+ if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
+ printf(" (%s", name.c_str());
+ if (func_offset != 0) {
+ printf("+%" PRId64, func_offset);
+ }
+ printf(")");
+ }
+ printf("\n");
+
+ if (!elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get())) {
+ break;
+ }
+ }
+}
+
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ printf("Usage: unwind <PID>\n");
+ return 1;
+ }
+
+ pid_t pid = atoi(argv[1]);
+ if (!Attach(pid)) {
+ printf("Failed to attach to pid %d: %s\n", pid, strerror(errno));
+ return 1;
+ }
+
+ DoUnwind(pid);
+
+ Detach(pid);
+
+ return 0;
+}
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
new file mode 100644
index 0000000..66a9439
--- /dev/null
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 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 <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+
+#include "ArmExidx.h"
+#include "ElfInterfaceArm.h"
+
+namespace unwindstack {
+
+void DumpArm(ElfInterfaceArm* interface) {
+ if (interface == nullptr) {
+ printf("No ARM Unwind Information.\n\n");
+ return;
+ }
+
+ printf("ARM Unwind Information:\n");
+ for (const auto& entry : interface->pt_loads()) {
+ uint64_t load_bias = entry.second.table_offset;
+ printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
+ entry.second.table_size + load_bias);
+ for (auto addr : *interface) {
+ std::string name;
+ printf(" PC 0x%" PRIx64, addr + load_bias);
+ uint64_t func_offset;
+ uint64_t pc = addr + load_bias;
+ // This might be a thumb function, so set the low bit.
+ if (interface->GetFunctionName(pc | 1, &name, &func_offset) && !name.empty()) {
+ printf(" <%s>", name.c_str());
+ }
+ printf("\n");
+ uint64_t entry;
+ if (!interface->FindEntry(pc, &entry)) {
+ printf(" Cannot find entry for address.\n");
+ continue;
+ }
+ ArmExidx arm(nullptr, interface->memory(), nullptr);
+ arm.set_log(true);
+ arm.set_log_skip_execution(true);
+ arm.set_log_indent(2);
+ if (!arm.ExtractEntryData(entry)) {
+ if (arm.status() != ARM_STATUS_NO_UNWIND) {
+ printf(" Error trying to extract data.\n");
+ }
+ continue;
+ }
+ if (arm.data()->size() > 0) {
+ if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) {
+ printf(" Error trying to evaluate dwarf data.\n");
+ }
+ }
+ }
+ }
+ printf("\n");
+}
+
+void DumpDwarfSection(ElfInterface* interface, DwarfSection* section, uint64_t load_bias) {
+ 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) {
+ continue;
+ }
+ printf("\n PC 0x%" PRIx64, fde->pc_start + load_bias);
+ std::string name;
+ uint64_t func_offset;
+ if (interface->GetFunctionName(fde->pc_start + load_bias, &name, &func_offset) &&
+ !name.empty()) {
+ printf(" <%s>", name.c_str());
+ }
+ printf("\n");
+ if (!section->Log(2, UINT64_MAX, load_bias, fde)) {
+ printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
+ }
+ }
+}
+
+int GetElfInfo(const char* file) {
+ // Send all log messages to stdout.
+ log_to_stdout(true);
+
+ MemoryFileAtOffset* memory = new MemoryFileAtOffset;
+ if (!memory->Init(file, 0)) {
+ // Initializatation failed.
+ printf("Failed to init\n");
+ return 1;
+ }
+
+ Elf elf(memory);
+ if (!elf.Init() || !elf.valid()) {
+ printf("%s is not a valid elf file.\n", file);
+ return 1;
+ }
+
+ ElfInterface* interface = elf.interface();
+ if (elf.machine_type() == EM_ARM) {
+ DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
+ printf("\n");
+ }
+
+ if (interface->eh_frame() != nullptr) {
+ printf("eh_frame information:\n");
+ DumpDwarfSection(interface, interface->eh_frame(), interface->load_bias());
+ printf("\n");
+ } else {
+ printf("\nno eh_frame information\n");
+ }
+
+ if (interface->debug_frame() != nullptr) {
+ printf("\ndebug_frame information:\n");
+ DumpDwarfSection(interface, interface->debug_frame(), interface->load_bias());
+ printf("\n");
+ } else {
+ printf("\nno debug_frame information\n");
+ }
+
+ // If there is a gnu_debugdata interface, dump the information for that.
+ ElfInterface* gnu_debugdata_interface = elf.gnu_debugdata_interface();
+ if (gnu_debugdata_interface != nullptr) {
+ if (gnu_debugdata_interface->eh_frame() != nullptr) {
+ printf("\ngnu_debugdata (eh_frame):\n");
+ DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->eh_frame(), 0);
+ printf("\n");
+ }
+ if (gnu_debugdata_interface->debug_frame() != nullptr) {
+ printf("\ngnu_debugdata (debug_frame):\n");
+ DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->debug_frame(), 0);
+ printf("\n");
+ }
+ } else {
+ printf("\nno valid gnu_debugdata information\n");
+ }
+
+ return 0;
+}
+
+} // namespace unwindstack
+
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ printf("Need to pass the name of an elf file to the program.\n");
+ return 1;
+ }
+
+ struct stat st;
+ if (stat(argv[1], &st) == -1) {
+ printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ printf("%s is not a regular file.\n", argv[1]);
+ return 1;
+ }
+
+ return unwindstack::GetElfInfo(argv[1]);
+}
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
new file mode 100644
index 0000000..dc9ae5a
--- /dev/null
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 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 <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+
+int main(int argc, char** argv) {
+ if (argc != 2 && argc != 3) {
+ printf("Usage: unwind_symbols <ELF_FILE> [<FUNC_ADDRESS>]\n");
+ printf(" Dump all function symbols in ELF_FILE. If FUNC_ADDRESS is\n");
+ printf(" specified, then get the function at that address.\n");
+ printf(" FUNC_ADDRESS must be a hex number.\n");
+ return 1;
+ }
+
+ struct stat st;
+ if (stat(argv[1], &st) == -1) {
+ printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ printf("%s is not a regular file.\n", argv[1]);
+ return 1;
+ }
+
+ uint64_t func_addr;
+ if (argc == 3) {
+ char* name;
+ func_addr = strtoull(argv[2], &name, 16);
+ if (*name != '\0') {
+ printf("%s is not a hex number.\n", argv[2]);
+ return 1;
+ }
+ }
+
+ // Send all log messages to stdout.
+ unwindstack::log_to_stdout(true);
+
+ unwindstack::MemoryFileAtOffset* memory = new unwindstack::MemoryFileAtOffset;
+ if (!memory->Init(argv[1], 0)) {
+ printf("Failed to init\n");
+ return 1;
+ }
+
+ unwindstack::Elf elf(memory);
+ if (!elf.Init() || !elf.valid()) {
+ printf("%s is not a valid elf file.\n", argv[1]);
+ return 1;
+ }
+
+ switch (elf.machine_type()) {
+ case EM_ARM:
+ printf("ABI: arm\n");
+ break;
+ case EM_AARCH64:
+ printf("ABI: arm64\n");
+ break;
+ case EM_386:
+ printf("ABI: x86\n");
+ break;
+ case EM_X86_64:
+ printf("ABI: x86_64\n");
+ break;
+ default:
+ printf("ABI: unknown\n");
+ return 1;
+ }
+
+ std::string name;
+ uint64_t load_bias = elf.interface()->load_bias();
+ if (argc == 3) {
+ std::string cur_name;
+ uint64_t func_offset;
+ if (!elf.GetFunctionName(func_addr, &cur_name, &func_offset)) {
+ printf("No known function at 0x%" PRIx64 "\n", func_addr);
+ return 1;
+ }
+ printf("<0x%" PRIx64 ">", func_addr - func_offset);
+ if (func_offset != 0) {
+ printf("+%" PRId64, func_offset);
+ }
+ printf(": %s\n", cur_name.c_str());
+ return 0;
+ }
+
+ // This is a crude way to get the symbols in order.
+ for (const auto& entry : elf.interface()->pt_loads()) {
+ uint64_t start = entry.second.offset + load_bias;
+ uint64_t end = entry.second.table_size + load_bias;
+ for (uint64_t addr = start; addr < end; addr += 4) {
+ std::string cur_name;
+ uint64_t func_offset;
+ if (elf.GetFunctionName(addr, &cur_name, &func_offset)) {
+ if (cur_name != name) {
+ printf("<0x%" PRIx64 "> Function: %s\n", addr - func_offset, cur_name.c_str());
+ }
+ name = cur_name;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/libunwindstack/unwind_info.cpp b/libunwindstack/unwind_info.cpp
deleted file mode 100644
index 6f158b0..0000000
--- a/libunwindstack/unwind_info.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2016 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 <elf.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "ArmExidx.h"
-#include "Elf.h"
-#include "ElfInterface.h"
-#include "ElfInterfaceArm.h"
-#include "Log.h"
-
-void DumpArm(ElfInterfaceArm* interface) {
- if (interface == nullptr) {
- printf("No ARM Unwind Information.\n\n");
- return;
- }
-
- printf("ARM Unwind Information:\n");
- for (const auto& entry : interface->pt_loads()) {
- uint64_t load_bias = entry.second.table_offset;
- printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
- entry.second.table_size + load_bias);
- for (auto addr : *interface) {
- std::string name;
- printf(" PC 0x%" PRIx64, addr + load_bias);
- uint64_t func_offset;
- if (interface->GetFunctionName(addr + load_bias + 1, &name, &func_offset) && !name.empty()) {
- printf(" <%s>", name.c_str());
- }
- printf("\n");
- uint64_t entry;
- if (!interface->FindEntry(addr + load_bias, &entry)) {
- printf(" Cannot find entry for address.\n");
- continue;
- }
- ArmExidx arm(nullptr, interface->memory(), nullptr);
- arm.set_log(true);
- arm.set_log_skip_execution(true);
- arm.set_log_indent(2);
- if (!arm.ExtractEntryData(entry)) {
- if (arm.status() != ARM_STATUS_NO_UNWIND) {
- printf(" Error trying to extract data.\n");
- }
- continue;
- }
- if (arm.data()->size() > 0) {
- if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) {
- printf(" Error trying to evaluate dwarf data.\n");
- }
- }
- }
- }
- printf("\n");
-}
-
-int main(int argc, char** argv) {
- if (argc != 2) {
- printf("Need to pass the name of an elf file to the program.\n");
- return 1;
- }
-
- struct stat st;
- if (stat(argv[1], &st) == -1) {
- printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
- return 1;
- }
- if (!S_ISREG(st.st_mode)) {
- printf("%s is not a regular file.\n", argv[1]);
- return 1;
- }
- if (S_ISDIR(st.st_mode)) {
- printf("%s is a directory.\n", argv[1]);
- return 1;
- }
-
- // Send all log messages to stdout.
- log_to_stdout(true);
-
- MemoryFileAtOffset* memory = new MemoryFileAtOffset;
- if (!memory->Init(argv[1], 0)) {
- // Initializatation failed.
- printf("Failed to init\n");
- return 1;
- }
-
- Elf elf(memory);
- if (!elf.Init() || !elf.valid()) {
- printf("%s is not a valid elf file.\n", argv[1]);
- return 1;
- }
-
- ElfInterface* interface = elf.interface();
- if (elf.machine_type() == EM_ARM) {
- DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
- printf("\n");
- }
-
- return 0;
-}
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
new file mode 100644
index 0000000..fc6f305
--- /dev/null
+++ b/libusbhost/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2010 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.
+//
+
+cc_library {
+ name: "libusbhost",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ host_supported: true,
+ srcs: ["usbhost.c"],
+ cflags: ["-Werror"],
+ export_include_dirs: ["include"],
+ target: {
+ android: {
+ cflags: [
+ "-g",
+ "-DUSE_LIBLOG",
+ ],
+ shared_libs: ["liblog"],
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/libusbhost/Android.mk b/libusbhost/Android.mk
deleted file mode 100644
index 9439846..0000000
--- a/libusbhost/Android.mk
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# Copyright (C) 2010 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# Static library for Linux host
-# ========================================================
-
-ifeq ($(HOST_OS),linux)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-endif
-
-# Shared library for target
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -g -DUSE_LIBLOG -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-# needed for logcat
-LOCAL_SHARED_LIBRARIES := libcutils
-include $(BUILD_SHARED_LIBRARY)
-
-# Static library for target
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-include $(BUILD_STATIC_LIBRARY)
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 7adb4f2..44b878d 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -23,7 +23,7 @@
#ifdef USE_LIBLOG
#define LOG_TAG "usbhost"
-#include "utils/Log.h"
+#include "log/log.h"
#define D ALOGD
#else
#define D printf
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 5e76279..038fd73 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -17,8 +17,16 @@
vendor_available: true,
host_supported: true,
- header_libs: ["libsystem_headers",],
- export_header_lib_headers: ["libsystem_headers",],
+ header_libs: [
+ "liblog_headers",
+ "libsystem_headers",
+ "libcutils_headers"
+ ],
+ export_header_lib_headers: [
+ "liblog_headers",
+ "libsystem_headers",
+ "libcutils_headers"
+ ],
export_include_dirs: ["include"],
target: {
@@ -38,13 +46,16 @@
cc_library {
name: "libutils",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
host_supported: true,
srcs: [
"CallStack.cpp",
"FileMap.cpp",
"JenkinsHash.cpp",
- "Log.cpp",
"NativeHandle.cpp",
"Printer.cpp",
"PropertyMap.cpp",
@@ -135,8 +146,6 @@
enabled: true,
},
},
-
- clang: true,
}
// Include subdirectory makefiles
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 1afa1ec..3c4d81c 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -48,8 +48,16 @@
// Constructor. Create an empty object.
FileMap::FileMap(void)
- : mFileName(NULL), mBasePtr(NULL), mBaseLength(0),
- mDataPtr(NULL), mDataLength(0)
+ : mFileName(NULL),
+ mBasePtr(NULL),
+ mBaseLength(0),
+ mDataPtr(NULL),
+ mDataLength(0)
+#if defined(__MINGW32__)
+ ,
+ mFileHandle(INVALID_HANDLE_VALUE),
+ mFileMapping(NULL)
+#endif
{
}
@@ -65,8 +73,8 @@
other.mBasePtr = NULL;
other.mDataPtr = NULL;
#if defined(__MINGW32__)
- other.mFileHandle = 0;
- other.mFileMapping = 0;
+ other.mFileHandle = INVALID_HANDLE_VALUE;
+ other.mFileMapping = NULL;
#endif
}
@@ -84,8 +92,8 @@
#if defined(__MINGW32__)
mFileHandle = other.mFileHandle;
mFileMapping = other.mFileMapping;
- other.mFileHandle = 0;
- other.mFileMapping = 0;
+ other.mFileHandle = INVALID_HANDLE_VALUE;
+ other.mFileMapping = NULL;
#endif
return *this;
}
@@ -101,7 +109,7 @@
ALOGD("UnmapViewOfFile(%p) failed, error = %lu\n", mBasePtr,
GetLastError() );
}
- if (mFileMapping != INVALID_HANDLE_VALUE) {
+ if (mFileMapping != NULL) {
CloseHandle(mFileMapping);
}
#else
@@ -156,7 +164,7 @@
ALOGE("MapViewOfFile(%" PRId64 ", %zu) failed with error %lu\n",
adjOffset, adjLength, GetLastError() );
CloseHandle(mFileMapping);
- mFileMapping = INVALID_HANDLE_VALUE;
+ mFileMapping = NULL;
return false;
}
#else // !defined(__MINGW32__)
diff --git a/libutils/Log.cpp b/libutils/Log.cpp
deleted file mode 100644
index 2c1fb86..0000000
--- a/libutils/Log.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- * 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 "Log"
-
-#include <utils/Log.h>
-#include <utils/Timers.h>
-
-namespace android {
-
-LogIfSlow::LogIfSlow(
- const char* tag, android_LogPriority priority, int timeoutMillis, const char* message)
- : mTag(tag), mPriority(priority), mTimeoutMillis(timeoutMillis), mMessage(message),
- mStart(systemTime(SYSTEM_TIME_BOOTTIME)) {
-}
-
-LogIfSlow::~LogIfSlow() {
- int durationMillis = (int)nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_BOOTTIME) - mStart);
- if (durationMillis > mTimeoutMillis) {
- LOG_PRI(mPriority, mTag, "%s: %dms", mMessage, durationMillis);
- }
-}
-
-} // namespace android
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index 12f77bb..cbf042e 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -45,9 +45,11 @@
#ifndef _WIN32
if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error
ALOGE("%s: Failed to format string", __FUNCTION__);
+ va_end(arglist);
return;
}
#else
+ va_end(arglist);
return;
#endif
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 28fc351..73ec1be 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -23,9 +23,9 @@
#include <utils/SystemClock.h>
-#include <sys/time.h>
#include <string.h>
#include <errno.h>
+#include <time.h>
#include <cutils/compiler.h>
diff --git a/libutils/include/utils/Log.h b/libutils/include/utils/Log.h
index 5276a49..42e03e7 100644
--- a/libutils/include/utils/Log.h
+++ b/libutils/include/utils/Log.h
@@ -1,72 +1,7 @@
-/*
- * Copyright (C) 2005 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.
- */
+// DO NOT INCLUDE ANYTHING NEW IN THIS FILE.
-//
-// C/C++ logging functions. See the logging documentation for API details.
-//
-// We'd like these to be available from C code (in case we import some from
-// somewhere), so this has a C interface.
-//
-// The output will be correct when the log file is shared between multiple
-// threads and/or multiple processes so long as the operating system
-// supports O_APPEND. These calls have mutex-protected data structures
-// and so are NOT reentrant. Do not use LOG in a signal handler.
-//
-#ifndef _LIBS_UTILS_LOG_H
-#define _LIBS_UTILS_LOG_H
-
-#include <sys/types.h>
+// <log/log.h> has replaced this file and all changes should go there instead.
+// This path remains strictly to include that header as there are thousands of
+// references to <utils/Log.h> in the tree.
#include <log/log.h>
-
-#ifdef __cplusplus
-
-namespace android {
-
-/*
- * A very simple utility that yells in the log when an operation takes too long.
- */
-class LogIfSlow {
-public:
- LogIfSlow(const char* tag, android_LogPriority priority,
- int timeoutMillis, const char* message);
- ~LogIfSlow();
-
-private:
- const char* const mTag;
- const android_LogPriority mPriority;
- const int mTimeoutMillis;
- const char* const mMessage;
- const int64_t mStart;
-};
-
-/*
- * Writes the specified debug log message if this block takes longer than the
- * specified number of milliseconds to run. Includes the time actually taken.
- *
- * {
- * ALOGD_IF_SLOW(50, "Excessive delay doing something.");
- * doSomething();
- * }
- */
-#define ALOGD_IF_SLOW(timeoutMillis, message) \
- android::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message);
-
-} // namespace android
-
-#endif // __cplusplus
-
-#endif // _LIBS_UTILS_LOG_H
diff --git a/libutils/include/utils/Mutex.h b/libutils/include/utils/Mutex.h
index d106185..af6076c 100644
--- a/libutils/include/utils/Mutex.h
+++ b/libutils/include/utils/Mutex.h
@@ -28,6 +28,53 @@
#include <utils/Errors.h>
#include <utils/Timers.h>
+// Enable thread safety attributes only with clang.
+// The attributes can be safely erased when compiling with other compilers.
+#if defined(__clang__) && (!defined(SWIG))
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
+#else
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
+#endif
+
+#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
+
+#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+
+#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+
+#define ACQUIRED_BEFORE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
+
+#define ACQUIRED_AFTER(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
+
+#define REQUIRES(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
+
+#define REQUIRES_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
+
+#define ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
+
+#define ACQUIRE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
+
+#define RELEASE(...) THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
+
+#define RELEASE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
+
+#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
+
+#define ASSERT_SHARED_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
+
+#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+#define NO_THREAD_SAFETY_ANALYSIS THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
// ---------------------------------------------------------------------------
namespace android {
// ---------------------------------------------------------------------------
@@ -44,24 +91,24 @@
* The mutex must be unlocked by the thread that locked it. They are not
* recursive, i.e. the same thread can't lock it multiple times.
*/
-class Mutex {
-public:
+class CAPABILITY("mutex") Mutex {
+ public:
enum {
PRIVATE = 0,
SHARED = 1
};
- Mutex();
- explicit Mutex(const char* name);
- explicit Mutex(int type, const char* name = NULL);
- ~Mutex();
+ Mutex();
+ explicit Mutex(const char* name);
+ explicit Mutex(int type, const char* name = NULL);
+ ~Mutex();
// lock or unlock the mutex
- status_t lock();
- void unlock();
+ status_t lock() ACQUIRE();
+ void unlock() RELEASE();
// lock if possible; returns 0 on success, error otherwise
- status_t tryLock();
+ status_t tryLock() TRY_ACQUIRE(true);
#if defined(__ANDROID__)
// Lock the mutex, but don't wait longer than timeoutNs (relative time).
@@ -75,32 +122,36 @@
// which is subject to NTP adjustments, and includes time during suspend,
// so a timeout may occur even though no processes could run.
// Not holding a partial wakelock may lead to a system suspend.
- status_t timedLock(nsecs_t timeoutNs);
+ status_t timedLock(nsecs_t timeoutNs) TRY_ACQUIRE(true);
#endif
// Manages the mutex automatically. It'll be locked when Autolock is
// constructed and released when Autolock goes out of scope.
- class Autolock {
- public:
- inline explicit Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); }
- inline explicit Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
- inline ~Autolock() { mLock.unlock(); }
- private:
+ class SCOPED_CAPABILITY Autolock {
+ public:
+ inline explicit Autolock(Mutex& mutex) ACQUIRE(mutex) : mLock(mutex) { mLock.lock(); }
+ inline explicit Autolock(Mutex* mutex) ACQUIRE(mutex) : mLock(*mutex) { mLock.lock(); }
+ inline ~Autolock() RELEASE() { mLock.unlock(); }
+
+ private:
Mutex& mLock;
+ // Cannot be copied or moved - declarations only
+ Autolock(const Autolock&);
+ Autolock& operator=(const Autolock&);
};
-private:
+ private:
friend class Condition;
// A mutex cannot be copied
- Mutex(const Mutex&);
- Mutex& operator = (const Mutex&);
+ Mutex(const Mutex&);
+ Mutex& operator=(const Mutex&);
#if !defined(_WIN32)
pthread_mutex_t mMutex;
#else
- void _init();
- void* mState;
+ void _init();
+ void* mState;
#endif
};
diff --git a/libutils/include/utils/Singleton.h b/libutils/include/utils/Singleton.h
index bdb2332..9afedd4 100644
--- a/libutils/include/utils/Singleton.h
+++ b/libutils/include/utils/Singleton.h
@@ -85,7 +85,7 @@
#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \
template<> ::android::Mutex \
(::android::Singleton< TYPE >::sLock)(::android::Mutex::PRIVATE); \
- template<> TYPE* ::android::Singleton< TYPE >::sInstance(0); \
+ template<> TYPE* ::android::Singleton< TYPE >::sInstance(0); /* NOLINT */ \
template class ::android::Singleton< TYPE >;
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index f6433a8..cb3d338 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -67,7 +67,6 @@
inline const char16_t* string() const;
-//TODO(b/35363681): remove
private:
static inline std::string std_string(const String16& str);
public:
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index f5f9219..1f3e5d8 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -67,7 +67,6 @@
inline const char* c_str() const;
inline const char* string() const;
-// TODO(b/35363681): remove
private:
static inline std::string std_string(const String8& str);
public:
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 0c20607..ae6d9c8 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -82,9 +82,10 @@
// Accessors
- inline T& operator* () const { return *m_ptr; }
- inline T* operator-> () const { return m_ptr; }
- inline T* get() const { return m_ptr; }
+ inline T& operator* () const { return *m_ptr; }
+ inline T* operator-> () const { return m_ptr; }
+ inline T* get() const { return m_ptr; }
+ inline explicit operator bool () const { return m_ptr != nullptr; }
// Operators
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
index 7b62c24..0869175 100644
--- a/libutils/tests/Android.bp
+++ b/libutils/tests/Android.bp
@@ -23,6 +23,7 @@
srcs: [
"BitSet_test.cpp",
"LruCache_test.cpp",
+ "Mutex_test.cpp",
"Singleton_test.cpp",
"String8_test.cpp",
"StrongPointer_test.cpp",
@@ -71,6 +72,7 @@
"-Wall",
"-Wextra",
"-Werror",
+ "-Wthread-safety",
],
}
diff --git a/libunwindstack/Log.h b/libutils/tests/Mutex_test.cpp
similarity index 68%
copy from libunwindstack/Log.h
copy to libutils/tests/Mutex_test.cpp
index 2d01aa8..8a1805f 100644
--- a/libunwindstack/Log.h
+++ b/libutils/tests/Mutex_test.cpp
@@ -14,12 +14,19 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
+#include <utils/Mutex.h>
-#include <stdint.h>
+#include <gtest/gtest.h>
-void log_to_stdout(bool enable);
-void log(uint8_t indent, const char* format, ...);
+static android::Mutex mLock;
+static int i GUARDED_BY(mLock);
-#endif // _LIBUNWINDSTACK_LOG_H
+void modifyLockedVariable() REQUIRES(mLock) {
+ i = 1;
+}
+
+TEST(Mutex, compile) {
+ android::Mutex::Autolock _l(mLock);
+ i = 0;
+ modifyLockedVariable();
+}
\ No newline at end of file
diff --git a/libziparchive/.clang-format b/libziparchive/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libziparchive/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 0a4f088..f395c74 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -50,19 +50,32 @@
"libbase",
"liblog",
],
-}
+ export_include_dirs: ["include"],
+}
cc_library {
name: "libziparchive",
host_supported: true,
- vendor_available:true,
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
- defaults: ["libziparchive_defaults", "libziparchive_flags"],
- shared_libs: ["liblog", "libbase"],
+ defaults: [
+ "libziparchive_defaults",
+ "libziparchive_flags",
+ ],
+ shared_libs: [
+ "liblog",
+ "libbase",
+ ],
target: {
android: {
- shared_libs: ["libz", "libutils"],
+ shared_libs: [
+ "libz",
+ "libutils",
+ ],
},
host: {
static_libs: ["libutils"],
@@ -88,7 +101,10 @@
name: "libziparchive-host",
host_supported: true,
device_supported: false,
- defaults: ["libziparchive_defaults", "libziparchive_flags"],
+ defaults: [
+ "libziparchive_defaults",
+ "libziparchive_flags",
+ ],
shared_libs: ["libz-host"],
static_libs: ["libutils"],
}
@@ -124,3 +140,39 @@
},
},
}
+
+// Performance benchmarks.
+cc_benchmark {
+ name: "ziparchive-benchmarks",
+ defaults: ["libziparchive_flags"],
+
+ srcs: [
+ "zip_archive_benchmark.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libziparchive",
+ "libz",
+ "libutils",
+ ],
+
+ target: {
+ host: {
+ cppflags: ["-Wno-unnamed-type-template-args"],
+ },
+ },
+}
+
+cc_binary {
+ name: "unzip",
+ defaults: ["libziparchive_flags"],
+ srcs: ["unzip.cpp"],
+ shared_libs: [
+ "libbase",
+ "libziparchive",
+ ],
+}
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
index ddbc286..5fc2fb4 100644
--- a/libziparchive/entry_name_utils-inl.h
+++ b/libziparchive/entry_name_utils-inl.h
@@ -55,5 +55,4 @@
return true;
}
-
#endif // LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
diff --git a/libziparchive/entry_name_utils_test.cc b/libziparchive/entry_name_utils_test.cc
index 20715bb..d83d854 100644
--- a/libziparchive/entry_name_utils_test.cc
+++ b/libziparchive/entry_name_utils_test.cc
@@ -20,44 +20,43 @@
TEST(entry_name_utils, NullChars) {
// 'A', 'R', '\0', 'S', 'E'
- const uint8_t zeroes[] = { 0x41, 0x52, 0x00, 0x53, 0x45 };
+ const uint8_t zeroes[] = {0x41, 0x52, 0x00, 0x53, 0x45};
ASSERT_FALSE(IsValidEntryName(zeroes, sizeof(zeroes)));
- const uint8_t zeroes_continuation_chars[] = { 0xc2, 0xa1, 0xc2, 0x00 };
- ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars,
- sizeof(zeroes_continuation_chars)));
+ const uint8_t zeroes_continuation_chars[] = {0xc2, 0xa1, 0xc2, 0x00};
+ ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars, sizeof(zeroes_continuation_chars)));
}
TEST(entry_name_utils, InvalidSequence) {
// 0xfe is an invalid start byte
- const uint8_t invalid[] = { 0x41, 0xfe };
+ const uint8_t invalid[] = {0x41, 0xfe};
ASSERT_FALSE(IsValidEntryName(invalid, sizeof(invalid)));
// 0x91 is an invalid start byte (it's a valid continuation byte).
- const uint8_t invalid2[] = { 0x41, 0x91 };
+ const uint8_t invalid2[] = {0x41, 0x91};
ASSERT_FALSE(IsValidEntryName(invalid2, sizeof(invalid2)));
}
TEST(entry_name_utils, TruncatedContinuation) {
// Malayalam script with truncated bytes. There should be 2 bytes
// after 0xe0
- const uint8_t truncated[] = { 0xe0, 0xb4, 0x85, 0xe0, 0xb4 };
+ const uint8_t truncated[] = {0xe0, 0xb4, 0x85, 0xe0, 0xb4};
ASSERT_FALSE(IsValidEntryName(truncated, sizeof(truncated)));
// 0xc2 is the start of a 2 byte sequence that we've subsequently
// dropped.
- const uint8_t truncated2[] = { 0xc2, 0xc2, 0xa1 };
+ const uint8_t truncated2[] = {0xc2, 0xc2, 0xa1};
ASSERT_FALSE(IsValidEntryName(truncated2, sizeof(truncated2)));
}
TEST(entry_name_utils, BadContinuation) {
// 0x41 is an invalid continuation char, since it's MSBs
// aren't "10..." (are 01).
- const uint8_t bad[] = { 0xc2, 0xa1, 0xc2, 0x41 };
+ const uint8_t bad[] = {0xc2, 0xa1, 0xc2, 0x41};
ASSERT_FALSE(IsValidEntryName(bad, sizeof(bad)));
// 0x41 is an invalid continuation char, since it's MSBs
// aren't "10..." (are 11).
- const uint8_t bad2[] = { 0xc2, 0xa1, 0xc2, 0xfe };
+ const uint8_t bad2[] = {0xc2, 0xa1, 0xc2, 0xfe};
ASSERT_FALSE(IsValidEntryName(bad2, sizeof(bad2)));
}
diff --git a/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
similarity index 86%
rename from include/ziparchive/zip_archive.h
rename to libziparchive/include/ziparchive/zip_archive.h
index 31fc2df..73ae68d 100644
--- a/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -28,8 +28,8 @@
/* Zip compression methods we support */
enum {
- kCompressStored = 0, // no compression
- kCompressDeflated = 8, // standard deflate
+ kCompressStored = 0, // no compression
+ kCompressDeflated = 8, // standard deflate
};
struct ZipString {
@@ -44,19 +44,17 @@
explicit ZipString(const char* entry_name);
bool operator==(const ZipString& rhs) const {
- return name && (name_length == rhs.name_length) &&
- (memcmp(name, rhs.name, name_length) == 0);
+ return name && (name_length == rhs.name_length) && (memcmp(name, rhs.name, name_length) == 0);
}
bool StartsWith(const ZipString& prefix) const {
return name && (name_length >= prefix.name_length) &&
- (memcmp(name, prefix.name, prefix.name_length) == 0);
+ (memcmp(name, prefix.name, prefix.name_length) == 0);
}
bool EndsWith(const ZipString& suffix) const {
return name && (name_length >= suffix.name_length) &&
- (memcmp(name + name_length - suffix.name_length, suffix.name,
- suffix.name_length) == 0);
+ (memcmp(name + name_length - suffix.name_length, suffix.name, suffix.name_length) == 0);
}
};
@@ -71,8 +69,17 @@
// Modification time. The zipfile format specifies
// that the first two little endian bytes contain the time
// and the last two little endian bytes contain the date.
+ // See `GetModificationTime`.
+ // TODO: should be overridden by extra time field, if present.
uint32_t mod_time;
+ // Returns `mod_time` as a broken-down struct tm.
+ struct tm GetModificationTime() const;
+
+ // Suggested Unix mode for this entry, from the zip archive if created on
+ // Unix, or a default otherwise.
+ mode_t unix_mode;
+
// 1 if this entry contains a data descriptor segment, 0
// otherwise.
uint8_t has_data_descriptor;
@@ -125,11 +132,11 @@
*
* Returns 0 on success, and negative values on failure.
*/
-int32_t OpenArchiveFd(const int fd, const char* debugFileName,
- ZipArchiveHandle *handle, bool assume_ownership = true);
+int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
+ bool assume_ownership = true);
int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debugFileName,
- ZipArchiveHandle *handle);
+ ZipArchiveHandle* handle);
/*
* Close archive, releasing resources associated with it. This will
* unmap the central directory of the zipfile and free all internal
@@ -155,8 +162,7 @@
* On non-Windows platforms this method does not modify internal state and
* can be called concurrently.
*/
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
- ZipEntry* data);
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data);
/*
* Start iterating over all entries of a zip file. The order of iteration
@@ -171,8 +177,7 @@
*
* Returns 0 on success and negative values on failure.
*/
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
- const ZipString* optional_prefix,
+int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
const ZipString* optional_suffix);
/*
@@ -208,8 +213,7 @@
*
* Returns 0 on success and negative values on failure.
*/
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
- uint8_t* begin, uint32_t size);
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size);
int GetFileDescriptor(const ZipArchiveHandle handle);
@@ -221,9 +225,9 @@
/*
* Stream the uncompressed data through the supplied function,
* passing cookie to it each time it gets called.
-*/
+ */
int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
- ProcessZipEntryFunction func, void* cookie);
+ ProcessZipEntryFunction func, void* cookie);
#endif
#endif // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
similarity index 96%
rename from include/ziparchive/zip_archive_stream_entry.h
rename to libziparchive/include/ziparchive/zip_archive_stream_entry.h
index a40b799..b4766f8 100644
--- a/include/ziparchive/zip_archive_stream_entry.h
+++ b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
@@ -40,7 +40,8 @@
ZipArchiveHandle handle_;
- uint32_t crc32_;
+ off64_t offset_ = 0;
+ uint32_t crc32_ = 0u;
};
#endif // LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
diff --git a/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
similarity index 96%
rename from include/ziparchive/zip_writer.h
rename to libziparchive/include/ziparchive/zip_writer.h
index 08ead48..c350a27 100644
--- a/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -19,7 +19,6 @@
#include <cstdio>
#include <ctime>
-#include <zlib.h>
#include <memory>
#include <string>
@@ -28,6 +27,9 @@
#include "android-base/macros.h"
#include "utils/Compat.h"
+struct z_stream_s;
+typedef struct z_stream_s z_stream;
+
/**
* Writes a Zip file via a stateful interface.
*
@@ -50,7 +52,7 @@
* fclose(file);
*/
class ZipWriter {
-public:
+ public:
enum {
/**
* Flag to compress the zip entry using deflate.
@@ -120,8 +122,7 @@
/**
* Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
*/
- int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
- uint32_t alignment);
+ int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time, uint32_t alignment);
/**
* Writes bytes to the zip file for the previously started zip entry.
@@ -156,7 +157,7 @@
*/
int32_t Finish();
-private:
+ private:
DISALLOW_COPY_AND_ASSIGN(ZipWriter);
int32_t HandleError(int32_t error_code);
@@ -179,7 +180,7 @@
std::vector<FileEntry> files_;
FileEntry current_file_entry_;
- std::unique_ptr<z_stream, void(*)(z_stream*)> z_stream_;
+ std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
std::vector<uint8_t> buffer_;
};
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
new file mode 100644
index 0000000..6756007
--- /dev/null
+++ b/libziparchive/unzip.cpp
@@ -0,0 +1,345 @@
+/*
+ * 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 <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <ziparchive/zip_archive.h>
+
+enum OverwriteMode {
+ kAlways,
+ kNever,
+ kPrompt,
+};
+
+static OverwriteMode overwrite_mode = kPrompt;
+static const char* flag_d = nullptr;
+static bool flag_l = false;
+static bool flag_p = false;
+static bool flag_q = false;
+static bool flag_v = false;
+static const char* archive_name = nullptr;
+static std::set<std::string> includes;
+static std::set<std::string> excludes;
+static uint64_t total_uncompressed_length = 0;
+static uint64_t total_compressed_length = 0;
+static size_t file_count = 0;
+
+static bool Filter(const std::string& name) {
+ if (!excludes.empty() && excludes.find(name) != excludes.end()) return true;
+ if (!includes.empty() && includes.find(name) == includes.end()) return true;
+ return false;
+}
+
+static bool MakeDirectoryHierarchy(const std::string& path) {
+ // stat rather than lstat because a symbolic link to a directory is fine too.
+ struct stat sb;
+ if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return true;
+
+ // Ensure the parent directories exist first.
+ if (!MakeDirectoryHierarchy(android::base::Dirname(path))) return false;
+
+ // Then try to create this directory.
+ return (mkdir(path.c_str(), 0777) != -1);
+}
+
+static int CompressionRatio(int64_t uncompressed, int64_t compressed) {
+ if (uncompressed == 0) return 0;
+ return (100LL * (uncompressed - compressed)) / uncompressed;
+}
+
+static void MaybeShowHeader() {
+ if (!flag_q) printf("Archive: %s\n", archive_name);
+ if (flag_v) {
+ printf(
+ " Length Method Size Cmpr Date Time CRC-32 Name\n"
+ "-------- ------ ------- ---- ---------- ----- -------- ----\n");
+ } else if (flag_l) {
+ printf(
+ " Length Date Time Name\n"
+ "--------- ---------- ----- ----\n");
+ }
+}
+
+static void MaybeShowFooter() {
+ if (flag_v) {
+ printf(
+ "-------- ------- --- -------\n"
+ "%8" PRId64 " %8" PRId64 " %3d%% %zu file%s\n",
+ total_uncompressed_length, total_compressed_length,
+ CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
+ (file_count == 1) ? "" : "s");
+ } else if (flag_l) {
+ printf(
+ "--------- -------\n"
+ "%9" PRId64 " %zu file%s\n",
+ total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
+ }
+}
+
+static bool PromptOverwrite(const std::string& dst) {
+ // TODO: [r]ename not implemented because it doesn't seem useful.
+ printf("replace %s? [y]es, [n]o, [A]ll, [N]one: ", dst.c_str());
+ fflush(stdout);
+ while (true) {
+ char* line = nullptr;
+ size_t n;
+ if (getline(&line, &n, stdin) == -1) {
+ error(1, 0, "(EOF/read error; assuming [N]one...)");
+ overwrite_mode = kNever;
+ return false;
+ }
+ if (n == 0) continue;
+ char cmd = line[0];
+ free(line);
+ switch (cmd) {
+ case 'y':
+ return true;
+ case 'n':
+ return false;
+ case 'A':
+ overwrite_mode = kAlways;
+ return true;
+ case 'N':
+ overwrite_mode = kNever;
+ return false;
+ }
+ }
+}
+
+static void ExtractToPipe(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+ // We need to extract to memory because ExtractEntryToFile insists on
+ // being able to seek and truncate, and you can't do that with stdout.
+ uint8_t* buffer = new uint8_t[entry.uncompressed_length];
+ int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length);
+ if (err < 0) {
+ error(1, 0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
+ }
+ if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) {
+ error(1, errno, "failed to write %s to stdout", name.c_str());
+ }
+ delete[] buffer;
+}
+
+static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+ // Bad filename?
+ if (android::base::StartsWith(name, "/") || android::base::StartsWith(name, "../") ||
+ name.find("/../") != std::string::npos) {
+ error(1, 0, "bad filename %s", name.c_str());
+ }
+
+ // Where are we actually extracting to (for human-readable output)?
+ std::string dst;
+ if (flag_d) {
+ dst = flag_d;
+ if (!android::base::EndsWith(dst, "/")) dst += '/';
+ }
+ dst += name;
+
+ // Ensure the directory hierarchy exists.
+ if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
+ error(1, errno, "couldn't create directory hierarchy for %s", dst.c_str());
+ }
+
+ // An entry in a zip file can just be a directory itself.
+ if (android::base::EndsWith(name, "/")) {
+ if (mkdir(name.c_str(), entry.unix_mode) == -1) {
+ // If the directory already exists, that's fine.
+ if (errno == EEXIST) {
+ struct stat sb;
+ if (stat(name.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return;
+ }
+ error(1, errno, "couldn't extract directory %s", dst.c_str());
+ }
+ return;
+ }
+
+ // Create the file.
+ int fd = open(name.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC | O_EXCL, entry.unix_mode);
+ if (fd == -1 && errno == EEXIST) {
+ if (overwrite_mode == kNever) return;
+ if (overwrite_mode == kPrompt && !PromptOverwrite(dst)) return;
+ // Either overwrite_mode is kAlways or the user consented to this specific case.
+ fd = open(name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, entry.unix_mode);
+ }
+ if (fd == -1) error(1, errno, "couldn't create file %s", dst.c_str());
+
+ // Actually extract into the file.
+ if (!flag_q) printf(" inflating: %s\n", dst.c_str());
+ int err = ExtractEntryToFile(zah, &entry, fd);
+ if (err < 0) error(1, 0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
+ close(fd);
+}
+
+static void ListOne(const ZipEntry& entry, const std::string& name) {
+ tm t = entry.GetModificationTime();
+ char time[32];
+ snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
+ t.tm_mday, t.tm_hour, t.tm_min);
+ if (flag_v) {
+ printf("%8d %s %7d %3d%% %s %08x %s\n", entry.uncompressed_length,
+ (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length,
+ CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32,
+ name.c_str());
+ } else {
+ printf("%9d %s %s\n", entry.uncompressed_length, time, name.c_str());
+ }
+}
+
+static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+ if (flag_l || flag_v) {
+ // -l or -lv or -lq or -v.
+ ListOne(entry, name);
+ } else {
+ // Actually extract.
+ if (flag_p) {
+ ExtractToPipe(zah, entry, name);
+ } else {
+ ExtractOne(zah, entry, name);
+ }
+ }
+ total_uncompressed_length += entry.uncompressed_length;
+ total_compressed_length += entry.compressed_length;
+ ++file_count;
+}
+
+static void ProcessAll(ZipArchiveHandle zah) {
+ MaybeShowHeader();
+
+ // libziparchive iteration order doesn't match the central directory.
+ // We could sort, but that would cost extra and wouldn't match either.
+ void* cookie;
+ int err = StartIteration(zah, &cookie, nullptr, nullptr);
+ if (err != 0) {
+ error(1, 0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
+ }
+
+ ZipEntry entry;
+ ZipString string;
+ while ((err = Next(cookie, &entry, &string)) >= 0) {
+ std::string name(string.name, string.name + string.name_length);
+ if (!Filter(name)) ProcessOne(zah, entry, name);
+ }
+
+ if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
+ EndIteration(cookie);
+
+ MaybeShowFooter();
+}
+
+static void ShowHelp(bool full) {
+ fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
+ if (!full) exit(EXIT_FAILURE);
+
+ printf(
+ "\n"
+ "Extract FILEs from ZIP archive. Default is all files.\n"
+ "\n"
+ "-d DIR Extract into DIR\n"
+ "-l List contents (-lq excludes archive name, -lv is verbose)\n"
+ "-n Never overwrite files (default: prompt)\n"
+ "-o Always overwrite files\n"
+ "-p Pipe to stdout\n"
+ "-q Quiet\n"
+ "-v List contents verbosely\n"
+ "-x FILE Exclude files\n");
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char* argv[]) {
+ static struct option opts[] = {
+ {"help", no_argument, 0, 'h'},
+ };
+ bool saw_x = false;
+ int opt;
+ while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
+ switch (opt) {
+ case 'd':
+ flag_d = optarg;
+ break;
+ case 'h':
+ ShowHelp(true);
+ break;
+ case 'l':
+ flag_l = true;
+ break;
+ case 'n':
+ overwrite_mode = kNever;
+ break;
+ case 'o':
+ overwrite_mode = kAlways;
+ break;
+ case 'p':
+ flag_p = flag_q = true;
+ break;
+ case 'q':
+ flag_q = true;
+ break;
+ case 'v':
+ flag_v = true;
+ break;
+ case 'x':
+ saw_x = true;
+ break;
+ case 1:
+ // -x swallows all following arguments, so we use '-' in the getopt
+ // string and collect files here.
+ if (!archive_name) {
+ archive_name = optarg;
+ } else if (saw_x) {
+ excludes.insert(optarg);
+ } else {
+ includes.insert(optarg);
+ }
+ break;
+ default:
+ ShowHelp(false);
+ }
+ }
+
+ if (!archive_name) error(1, 0, "missing archive filename");
+
+ // We can't support "-" to unzip from stdin because libziparchive relies on mmap.
+ ZipArchiveHandle zah;
+ int32_t err;
+ if ((err = OpenArchive(archive_name, &zah)) != 0) {
+ error(1, 0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
+ }
+
+ // Implement -d by changing into that directory.
+ // We'll create implicit directories based on paths in the zip file, but we
+ // require that the -d directory already exists.
+ if (flag_d && chdir(flag_d) == -1) error(1, errno, "couldn't chdir to %s", flag_d);
+
+ ProcessAll(zah);
+
+ CloseArchive(zah);
+ return 0;
+}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 78de40a..ad40d42 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -27,6 +27,7 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
#include <memory>
@@ -48,6 +49,10 @@
using android::base::get_unaligned;
+// Used to turn on crc checks - verify that the content CRC matches the values
+// specified in the local file header and the central directory.
+static const bool kCrcChecksEnabled = false;
+
// This is for windows. If we don't open a file in binary mode, weird
// things will happen.
#ifndef O_BINARY
@@ -57,68 +62,6 @@
// The maximum number of bytes to scan backwards for the EOCD start.
static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
-static const char* kErrorMessages[] = {
- "Unknown return code.",
- "Iteration ended",
- "Zlib error",
- "Invalid file",
- "Invalid handle",
- "Duplicate entries in archive",
- "Empty archive",
- "Entry not found",
- "Invalid offset",
- "Inconsistent information",
- "Invalid entry name",
- "I/O Error",
- "File mapping failed"
-};
-
-static const int32_t kErrorMessageUpperBound = 0;
-
-static const int32_t kIterationEnd = -1;
-
-// We encountered a Zlib error when inflating a stream from this file.
-// Usually indicates file corruption.
-static const int32_t kZlibError = -2;
-
-// The input file cannot be processed as a zip archive. Usually because
-// it's too small, too large or does not have a valid signature.
-static const int32_t kInvalidFile = -3;
-
-// An invalid iteration / ziparchive handle was passed in as an input
-// argument.
-static const int32_t kInvalidHandle = -4;
-
-// The zip archive contained two (or possibly more) entries with the same
-// name.
-static const int32_t kDuplicateEntry = -5;
-
-// The zip archive contains no entries.
-static const int32_t kEmptyArchive = -6;
-
-// The specified entry was not found in the archive.
-static const int32_t kEntryNotFound = -7;
-
-// The zip archive contained an invalid local file header pointer.
-static const int32_t kInvalidOffset = -8;
-
-// The zip archive contained inconsistent entry information. This could
-// be because the central directory & local file header did not agree, or
-// if the actual uncompressed length or crc32 do not match their declared
-// values.
-static const int32_t kInconsistentInformation = -9;
-
-// An invalid entry name was encountered.
-static const int32_t kInvalidEntryName = -10;
-
-// An I/O related system call (read, lseek, ftruncate, map) failed.
-static const int32_t kIoError = -11;
-
-// We were not able to mmap the central directory or entry contents.
-static const int32_t kMmapFailed = -12;
-
-static const int32_t kErrorMessageLowerBound = -13;
-
/*
* A Read-only Zip archive.
*
@@ -172,8 +115,7 @@
* Convert a ZipEntry to a hash table index, verifying that it's in a
* valid range.
*/
-static int64_t EntryToIndex(const ZipString* hash_table,
- const uint32_t hash_table_size,
+static int64_t EntryToIndex(const ZipString* hash_table, const uint32_t hash_table_size,
const ZipString& name) {
const uint32_t hash = ComputeHash(name);
@@ -194,7 +136,7 @@
/*
* Add a new entry to the hash table.
*/
-static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
+static int32_t AddToHash(ZipString* hash_table, const uint64_t hash_table_size,
const ZipString& name) {
const uint64_t hash = ComputeHash(name);
uint32_t ent = hash & (hash_table_size - 1);
@@ -218,13 +160,12 @@
}
static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
- off64_t file_length, off64_t read_amount,
- uint8_t* scan_buffer) {
+ off64_t file_length, off64_t read_amount, uint8_t* scan_buffer) {
const off64_t search_start = file_length - read_amount;
- if(!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
- ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed",
- static_cast<int64_t>(read_amount), static_cast<int64_t>(search_start));
+ if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
+ ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed", static_cast<int64_t>(read_amount),
+ static_cast<int64_t>(search_start));
return kIoError;
}
@@ -255,8 +196,7 @@
* Verify that there's no trailing space at the end of the central directory
* and its comment.
*/
- const off64_t calculated_length = eocd_offset + sizeof(EocdRecord)
- + eocd->comment_length;
+ const off64_t calculated_length = eocd_offset + sizeof(EocdRecord) + eocd->comment_length;
if (calculated_length != file_length) {
ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
static_cast<int64_t>(file_length - calculated_length));
@@ -269,7 +209,7 @@
*/
if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
- eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
+ eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
#if defined(__ANDROID__)
if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) {
android_errorWriteLog(0x534e4554, "31251826");
@@ -282,8 +222,8 @@
return kEmptyArchive;
}
- ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
- eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
+ ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32, eocd->num_records,
+ eocd->cd_size, eocd->cd_start_offset);
/*
* It all looks good. Create a mapping for the CD, and set the fields
@@ -312,7 +252,6 @@
* num_entries
*/
static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
-
// Test file length. We use lseek64 to make sure the file
// is small enough to be a zip file (Its size must be less than
// 0xffffffff bytes).
@@ -349,8 +288,8 @@
}
std::vector<uint8_t> scan_buffer(read_amount);
- int32_t result = MapCentralDirectory0(debug_file_name, archive, file_length, read_amount,
- scan_buffer.data());
+ int32_t result =
+ MapCentralDirectory0(debug_file_name, archive, file_length, read_amount, scan_buffer.data());
return result;
}
@@ -371,8 +310,8 @@
* least one unused entry to avoid an infinite loop during creation.
*/
archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
- archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
- sizeof(ZipString)));
+ archive->hash_table =
+ reinterpret_cast<ZipString*>(calloc(archive->hash_table_size, sizeof(ZipString)));
if (archive->hash_table == nullptr) {
ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
archive->hash_table_size, sizeof(ZipString));
@@ -394,8 +333,7 @@
return -1;
}
- const CentralDirectoryRecord* cdr =
- reinterpret_cast<const CentralDirectoryRecord*>(ptr);
+ const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
return -1;
@@ -404,7 +342,7 @@
const off64_t local_header_offset = cdr->local_file_header_offset;
if (local_header_offset >= archive->directory_offset) {
ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
- static_cast<int64_t>(local_header_offset), i);
+ static_cast<int64_t>(local_header_offset), i);
return -1;
}
@@ -414,8 +352,10 @@
const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
if (file_name + file_name_length > cd_end) {
- ALOGW("Zip: file name boundary exceeds the central directory range, file_name_length: "
- "%" PRIx16 ", cd_length: %zu", file_name_length, cd_length);
+ ALOGW(
+ "Zip: file name boundary exceeds the central directory range, file_name_length: "
+ "%" PRIx16 ", cd_length: %zu",
+ file_name_length, cd_length);
return -1;
}
/* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
@@ -427,8 +367,7 @@
ZipString entry_name;
entry_name.name = file_name;
entry_name.name_length = file_name_length;
- const int add_result = AddToHash(archive->hash_table,
- archive->hash_table_size, entry_name);
+ const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name);
if (add_result != 0) {
ALOGW("Zip: Error adding entry to hash table %d", add_result);
return add_result;
@@ -436,8 +375,7 @@
ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
- ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16,
- ptr - cd_ptr, cd_length, i);
+ ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, ptr - cd_ptr, cd_length, i);
return -1;
}
}
@@ -446,8 +384,7 @@
return 0;
}
-static int32_t OpenArchiveInternal(ZipArchive* archive,
- const char* debug_file_name) {
+static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) {
int32_t result = -1;
if ((result = MapCentralDirectory(debug_file_name, archive)) != 0) {
return result;
@@ -460,8 +397,8 @@
return 0;
}
-int32_t OpenArchiveFd(int fd, const char* debug_file_name,
- ZipArchiveHandle* handle, bool assume_ownership) {
+int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
+ bool assume_ownership) {
ZipArchive* archive = new ZipArchive(fd, assume_ownership);
*handle = archive;
return OpenArchiveInternal(archive, debug_file_name);
@@ -481,7 +418,7 @@
}
int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name,
- ZipArchiveHandle *handle) {
+ ZipArchiveHandle* handle) {
ZipArchive* archive = new ZipArchive(address, length);
*handle = archive;
return OpenArchiveInternal(archive, debug_file_name);
@@ -496,26 +433,39 @@
delete archive;
}
-static int32_t UpdateEntryFromDataDescriptor(MappedZipFile& mapped_zip,
- ZipEntry *entry) {
+static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry) {
uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
- if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) {
+ off64_t offset = entry->offset;
+ if (entry->method != kCompressStored) {
+ offset += entry->compressed_length;
+ } else {
+ offset += entry->uncompressed_length;
+ }
+
+ if (!mapped_zip.ReadAtOffset(ddBuf, sizeof(ddBuf), offset)) {
return kIoError;
}
const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
- const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
- const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
+ const uint16_t ddOffset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
+ const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + ddOffset);
- entry->crc32 = descriptor->crc32;
- entry->compressed_length = descriptor->compressed_size;
- entry->uncompressed_length = descriptor->uncompressed_size;
+ // Validate that the values in the data descriptor match those in the central
+ // directory.
+ if (entry->compressed_length != descriptor->compressed_size ||
+ entry->uncompressed_length != descriptor->uncompressed_size ||
+ entry->crc32 != descriptor->crc32) {
+ ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+ "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
+ entry->compressed_length, entry->uncompressed_length, entry->crc32,
+ descriptor->compressed_size, descriptor->uncompressed_size, descriptor->crc32);
+ return kInconsistentInformation;
+ }
return 0;
}
-static int32_t FindEntry(const ZipArchive* archive, const int ent,
- ZipEntry* data) {
+static int32_t FindEntry(const ZipArchive* archive, const int ent, ZipEntry* data) {
const uint16_t nameLen = archive->hash_table[ent].name_length;
// Recover the start of the central directory entry from the filename
@@ -533,8 +483,7 @@
return kInvalidOffset;
}
- const CentralDirectoryRecord *cdr =
- reinterpret_cast<const CentralDirectoryRecord*>(ptr);
+ const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
// The offset of the start of the central directory in the zipfile.
// We keep this lying around so that we can sanity check all our lengths
@@ -562,46 +511,62 @@
uint8_t lfh_buf[sizeof(LocalFileHeader)];
if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
ALOGW("Zip: failed reading lfh name from offset %" PRId64,
- static_cast<int64_t>(local_header_offset));
+ static_cast<int64_t>(local_header_offset));
return kIoError;
}
- const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
+ const LocalFileHeader* lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
if (lfh->lfh_signature != LocalFileHeader::kSignature) {
ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
- static_cast<int64_t>(local_header_offset));
+ static_cast<int64_t>(local_header_offset));
return kInvalidOffset;
}
// Paranoia: Match the values specified in the local file header
// to those specified in the central directory.
- // Verify that the central directory and local file header have the same general purpose bit
- // flags set.
- if (lfh->gpb_flags != cdr->gpb_flags) {
- ALOGW("Zip: gpb flag mismatch. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
+ // Warn if central directory and local file header don't agree on the use
+ // of a trailing Data Descriptor. The reference implementation is inconsistent
+ // and appears to use the LFH value during extraction (unzip) but the CD value
+ // while displayng information about archives (zipinfo). The spec remains
+ // silent on this inconsistency as well.
+ //
+ // For now, always use the version from the LFH but make sure that the values
+ // specified in the central directory match those in the data descriptor.
+ //
+ // NOTE: It's also worth noting that unzip *does* warn about inconsistencies in
+ // bit 11 (EFS: The language encoding flag, marking that filename and comment are
+ // encoded using UTF-8). This implementation does not check for the presence of
+ // that flag and always enforces that entry names are valid UTF-8.
+ if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) {
+ ALOGW("Zip: gpb flag mismatch at bit 3. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
cdr->gpb_flags, lfh->gpb_flags);
- return kInconsistentInformation;
}
// If there is no trailing data descriptor, verify that the central directory and local file
// header agree on the crc, compressed, and uncompressed sizes of the entry.
if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
data->has_data_descriptor = 0;
- if (data->compressed_length != lfh->compressed_size
- || data->uncompressed_length != lfh->uncompressed_size
- || data->crc32 != lfh->crc32) {
- ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
- ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
- data->compressed_length, data->uncompressed_length, data->crc32,
- lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
+ if (data->compressed_length != lfh->compressed_size ||
+ data->uncompressed_length != lfh->uncompressed_size || data->crc32 != lfh->crc32) {
+ ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+ "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
+ data->compressed_length, data->uncompressed_length, data->crc32, lfh->compressed_size,
+ lfh->uncompressed_size, lfh->crc32);
return kInconsistentInformation;
}
} else {
data->has_data_descriptor = 1;
}
+ // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
+ if ((cdr->version_made_by >> 8) == 3) {
+ data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
+ } else {
+ data->unix_mode = 0777;
+ }
+
// Check that the local file header name matches the declared
// name in the central directory.
if (lfh->file_name_length == nameLen) {
@@ -626,8 +591,8 @@
return kInconsistentInformation;
}
- const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
- + lfh->file_name_length + lfh->extra_field_length;
+ const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader) +
+ lfh->file_name_length + lfh->extra_field_length;
if (data_offset > cd_offset) {
ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
return kInvalidOffset;
@@ -635,16 +600,17 @@
if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
- static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
+ static_cast<int64_t>(data_offset), data->compressed_length,
+ static_cast<int64_t>(cd_offset));
return kInvalidOffset;
}
if (data->method == kCompressStored &&
- static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
- ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
- static_cast<int64_t>(data_offset), data->uncompressed_length,
- static_cast<int64_t>(cd_offset));
- return kInvalidOffset;
+ static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
+ ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
+ static_cast<int64_t>(data_offset), data->uncompressed_length,
+ static_cast<int64_t>(cd_offset));
+ return kInvalidOffset;
}
data->offset = data_offset;
@@ -659,8 +625,7 @@
ZipString suffix;
ZipArchive* archive;
- IterationHandle(const ZipString* in_prefix,
- const ZipString* in_suffix) {
+ IterationHandle(const ZipString* in_prefix, const ZipString* in_suffix) {
if (in_prefix) {
uint8_t* name_copy = new uint8_t[in_prefix->name_length];
memcpy(name_copy, in_prefix->name, in_prefix->name_length);
@@ -687,8 +652,7 @@
}
};
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
- const ZipString* optional_prefix,
+int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
const ZipString* optional_suffix) {
ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
@@ -701,7 +665,7 @@
cookie->position = 0;
cookie->archive = archive;
- *cookie_ptr = cookie ;
+ *cookie_ptr = cookie;
return 0;
}
@@ -709,16 +673,14 @@
delete reinterpret_cast<IterationHandle*>(cookie);
}
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
- ZipEntry* data) {
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data) {
const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
if (entryName.name_length == 0) {
ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
return kInvalidEntryName;
}
- const int64_t ent = EntryToIndex(archive->hash_table,
- archive->hash_table_size, entryName);
+ const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName);
if (ent < 0) {
ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
@@ -746,10 +708,8 @@
for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
if (hash_table[i].name != NULL &&
- (handle->prefix.name_length == 0 ||
- hash_table[i].StartsWith(handle->prefix)) &&
- (handle->suffix.name_length == 0 ||
- hash_table[i].EndsWith(handle->suffix))) {
+ (handle->prefix.name_length == 0 || hash_table[i].StartsWith(handle->prefix)) &&
+ (handle->suffix.name_length == 0 || hash_table[i].EndsWith(handle->suffix))) {
handle->position = (i + 1);
const int error = FindEntry(archive, i, data);
if (!error) {
@@ -769,8 +729,10 @@
public:
virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
virtual ~Writer() {}
+
protected:
Writer() = default;
+
private:
DISALLOW_COPY_AND_ASSIGN(Writer);
};
@@ -780,14 +742,12 @@
// the data appended to it.
class MemoryWriter : public Writer {
public:
- MemoryWriter(uint8_t* buf, size_t size) : Writer(),
- buf_(buf), size_(size), bytes_written_(0) {
- }
+ MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
virtual bool Append(uint8_t* buf, size_t buf_size) override {
if (bytes_written_ + buf_size > size_) {
- ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
- size_, bytes_written_ + buf_size);
+ ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", size_,
+ bytes_written_ + buf_size);
return false;
}
@@ -806,7 +766,6 @@
// The file will be truncated to the end of the written data.
class FileWriter : public Writer {
public:
-
// Creates a FileWriter for |fd| and prepare to write |entry| to it,
// guaranteeing that the file descriptor is valid and that there's enough
// space on the volume to write out the entry completely and that the file
@@ -865,8 +824,8 @@
virtual bool Append(uint8_t* buf, size_t buf_size) override {
if (total_bytes_written_ + buf_size > declared_length_) {
- ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
- declared_length_, total_bytes_written_ + buf_size);
+ ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", declared_length_,
+ total_bytes_written_ + buf_size);
return false;
}
@@ -879,13 +838,10 @@
return result;
}
+
private:
- FileWriter(const int fd, const size_t declared_length) :
- Writer(),
- fd_(fd),
- declared_length_(declared_length),
- total_bytes_written_(0) {
- }
+ FileWriter(const int fd, const size_t declared_length)
+ : Writer(), fd_(fd), declared_length_(declared_length), total_bytes_written_(0) {}
const int fd_;
const size_t declared_length_;
@@ -928,8 +884,7 @@
zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
if (zerr != Z_OK) {
if (zerr == Z_VERSION_ERROR) {
- ALOGE("Installed zlib is not compatible with linked version (%s)",
- ZLIB_VERSION);
+ ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
} else {
ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
}
@@ -938,19 +893,22 @@
}
auto zstream_deleter = [](z_stream* stream) {
- inflateEnd(stream); /* free up any allocated structures */
+ inflateEnd(stream); /* free up any allocated structures */
};
std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
const uint32_t uncompressed_length = entry->uncompressed_length;
+ uint64_t crc = 0;
uint32_t compressed_length = entry->compressed_length;
do {
/* read as much as we can */
if (zstream.avail_in == 0) {
const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
- if (!mapped_zip.ReadData(read_buf.data(), getSize)) {
+ off64_t offset = entry->offset + (entry->compressed_length - compressed_length);
+ // Make sure to read at offset to ensure concurrent access to the fd.
+ if (!mapped_zip.ReadAtOffset(read_buf.data(), getSize, offset)) {
ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
return kIoError;
}
@@ -964,19 +922,19 @@
/* uncompress the data */
zerr = inflate(&zstream, Z_NO_FLUSH);
if (zerr != Z_OK && zerr != Z_STREAM_END) {
- ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
- zerr, zstream.next_in, zstream.avail_in,
- zstream.next_out, zstream.avail_out);
+ ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, zstream.next_in,
+ zstream.avail_in, zstream.next_out, zstream.avail_out);
return kZlibError;
}
/* write when we're full or when we're done */
- if (zstream.avail_out == 0 ||
- (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
+ if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
const size_t write_size = zstream.next_out - &write_buf[0];
if (!writer->Append(&write_buf[0], write_size)) {
// The file might have declared a bogus length.
return kInconsistentInformation;
+ } else {
+ crc = crc32(crc, &write_buf[0], write_size);
}
zstream.next_out = &write_buf[0];
@@ -984,14 +942,19 @@
}
} while (zerr == Z_OK);
- assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+ assert(zerr == Z_STREAM_END); /* other errors should've been caught */
- // stream.adler holds the crc32 value for such streams.
- *crc_out = zstream.adler;
+ // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
+ // "feature" of zlib to tell it there won't be a zlib file header. zlib
+ // doesn't bother calculating the checksum in that scenario. We just do
+ // it ourselves above because there are no additional gains to be made by
+ // having zlib calculate it for us, since they do it by calling crc32 in
+ // the same manner that we have above.
+ *crc_out = crc;
if (zstream.total_out != uncompressed_length || compressed_length != 0) {
- ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
- zstream.total_out, uncompressed_length);
+ ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out,
+ uncompressed_length);
return kInconsistentInformation;
}
@@ -999,7 +962,7 @@
}
static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, Writer* writer,
- uint64_t *crc_out) {
+ uint64_t* crc_out) {
static const uint32_t kBufSize = 32768;
std::vector<uint8_t> buf(kBufSize);
@@ -1008,12 +971,15 @@
uint64_t crc = 0;
while (count < length) {
uint32_t remaining = length - count;
+ off64_t offset = entry->offset + count;
- // Safe conversion because kBufSize is narrow enough for a 32 bit signed
- // value.
+ // Safe conversion because kBufSize is narrow enough for a 32 bit signed value.
const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
- if (!mapped_zip.ReadData(buf.data(), block_size)) {
- ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
+
+ // Make sure to read at offset to ensure concurrent access to the fd.
+ if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) {
+ ALOGW("CopyFileToFile: copy read failed, block_size = %zu, offset = %" PRId64 ": %s",
+ block_size, static_cast<int64_t>(offset), strerror(errno));
return kIoError;
}
@@ -1029,16 +995,9 @@
return 0;
}
-int32_t ExtractToWriter(ZipArchiveHandle handle,
- ZipEntry* entry, Writer* writer) {
+int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, Writer* writer) {
ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
const uint16_t method = entry->method;
- off64_t data_offset = entry->offset;
-
- if (!archive->mapped_zip.SeekToOffset(data_offset)) {
- ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
- return kIoError;
- }
// this should default to kUnknownCompressionMethod.
int32_t return_value = -1;
@@ -1050,15 +1009,14 @@
}
if (!return_value && entry->has_data_descriptor) {
- return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry);
+ return_value = ValidateDataDescriptor(archive->mapped_zip, entry);
if (return_value) {
return return_value;
}
}
- // TODO: Fix this check by passing the right flags to inflate2 so that
- // it calculates the CRC for us.
- if (entry->crc32 != crc && false) {
+ // Validate that the CRC matches the calculated value.
+ if (kCrcChecksEnabled && (entry->crc32 != static_cast<uint32_t>(crc))) {
ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
return kInconsistentInformation;
}
@@ -1066,14 +1024,12 @@
return return_value;
}
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
- uint8_t* begin, uint32_t size) {
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size) {
std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
return ExtractToWriter(handle, entry, writer.get());
}
-int32_t ExtractEntryToFile(ZipArchiveHandle handle,
- ZipEntry* entry, int fd) {
+int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd) {
std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
if (writer.get() == nullptr) {
return kIoError;
@@ -1083,19 +1039,24 @@
}
const char* ErrorCodeString(int32_t error_code) {
- if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
- return kErrorMessages[error_code * -1];
+ // Make sure that the number of entries in kErrorMessages and ErrorCodes
+ // match.
+ static_assert((-kLastErrorCode + 1) == arraysize(kErrorMessages),
+ "(-kLastErrorCode + 1) != arraysize(kErrorMessages)");
+
+ const uint32_t idx = -error_code;
+ if (idx < arraysize(kErrorMessages)) {
+ return kErrorMessages[idx];
}
- return kErrorMessages[0];
+ return "Unknown return code";
}
int GetFileDescriptor(const ZipArchiveHandle handle) {
return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
}
-ZipString::ZipString(const char* entry_name)
- : name(reinterpret_cast<const uint8_t*>(entry_name)) {
+ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) {
size_t len = strlen(entry_name);
CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
name_length = static_cast<uint16_t>(len);
@@ -1104,10 +1065,8 @@
#if !defined(_WIN32)
class ProcessWriter : public Writer {
public:
- ProcessWriter(ProcessZipEntryFunction func, void* cookie) : Writer(),
- proc_function_(func),
- cookie_(cookie) {
- }
+ ProcessWriter(ProcessZipEntryFunction func, void* cookie)
+ : Writer(), proc_function_(func), cookie_(cookie) {}
virtual bool Append(uint8_t* buf, size_t buf_size) override {
return proc_function_(buf, buf_size, cookie_);
@@ -1124,7 +1083,7 @@
return ExtractToWriter(handle, entry, &writer);
}
-#endif //!defined(_WIN32)
+#endif //! defined(_WIN32)
int MappedZipFile::GetFileDescriptor() const {
if (!has_fd_) {
@@ -1158,54 +1117,21 @@
}
}
-bool MappedZipFile::SeekToOffset(off64_t offset) {
- if (has_fd_) {
- if (lseek64(fd_, offset, SEEK_SET) != offset) {
- ALOGE("Zip: lseek to %" PRId64 " failed: %s\n", offset, strerror(errno));
- return false;
- }
- return true;
- } else {
- if (offset < 0 || offset > static_cast<off64_t>(data_length_)) {
- ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n" , offset,
- data_length_);
- return false;
- }
-
- read_pos_ = offset;
- return true;
- }
-}
-
-bool MappedZipFile::ReadData(uint8_t* buffer, size_t read_amount) {
- if (has_fd_) {
- if(!android::base::ReadFully(fd_, buffer, read_amount)) {
- ALOGE("Zip: read from %d failed\n", fd_);
- return false;
- }
- } else {
- memcpy(buffer, static_cast<uint8_t*>(base_ptr_) + read_pos_, read_amount);
- read_pos_ += read_amount;
- }
- return true;
-}
-
// Attempts to read |len| bytes into |buf| at offset |off|.
bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) {
-#if !defined(_WIN32)
if (has_fd_) {
- if (static_cast<size_t>(TEMP_FAILURE_RETRY(pread64(fd_, buf, len, off))) != len) {
+ if (!android::base::ReadFullyAtOffset(fd_, buf, len, off)) {
ALOGE("Zip: failed to read at offset %" PRId64 "\n", off);
return false;
}
- return true;
+ } else {
+ if (off < 0 || off > static_cast<off64_t>(data_length_)) {
+ ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n", off, data_length_);
+ return false;
+ }
+ memcpy(buf, static_cast<uint8_t*>(base_ptr_) + off, len);
}
-#endif
- if (!SeekToOffset(off)) {
- return false;
- }
- return ReadData(buf, len);
-
+ return true;
}
void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
@@ -1216,13 +1142,13 @@
bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
size_t cd_size) {
if (mapped_zip.HasFd()) {
- if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(),
- cd_start_offset, cd_size, true /* read only */)) {
+ if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(), cd_start_offset,
+ cd_size, true /* read only */)) {
return false;
}
CHECK_EQ(directory_map->getDataLength(), cd_size);
- central_directory.Initialize(directory_map->getDataPtr(), 0/*offset*/, cd_size);
+ central_directory.Initialize(directory_map->getDataPtr(), 0 /*offset*/, cd_size);
} else {
if (mapped_zip.GetBasePtr() == nullptr) {
ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
@@ -1230,9 +1156,10 @@
}
if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
mapped_zip.GetFileLength()) {
- ALOGE("Zip: Failed to map central directory, offset exceeds mapped memory region ("
- "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
- static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
+ ALOGE(
+ "Zip: Failed to map central directory, offset exceeds mapped memory region ("
+ "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
+ static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
return false;
}
@@ -1240,3 +1167,17 @@
}
return true;
}
+
+tm ZipEntry::GetModificationTime() const {
+ tm t = {};
+
+ t.tm_hour = (mod_time >> 11) & 0x1f;
+ t.tm_min = (mod_time >> 5) & 0x3f;
+ t.tm_sec = (mod_time & 0x1f) << 1;
+
+ t.tm_year = ((mod_time >> 25) & 0x7f) + 80;
+ t.tm_mon = ((mod_time >> 21) & 0xf) - 1;
+ t.tm_mday = (mod_time >> 16) & 0x1f;
+
+ return t;
+}
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
new file mode 100644
index 0000000..cd3e164
--- /dev/null
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <android-base/test_utils.h>
+#include <benchmark/benchmark.h>
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_archive_stream_entry.h>
+#include <ziparchive/zip_writer.h>
+
+static TemporaryFile* CreateZip() {
+ TemporaryFile* result = new TemporaryFile;
+ FILE* fp = fdopen(result->fd, "w");
+
+ ZipWriter writer(fp);
+ std::string lastName = "file";
+ for (size_t i = 0; i < 1000; i++) {
+ // Make file names longer and longer.
+ lastName = lastName + std::to_string(i);
+ writer.StartEntry(lastName.c_str(), ZipWriter::kCompress);
+ writer.WriteBytes("helo", 4);
+ writer.FinishEntry();
+ }
+ writer.Finish();
+ fclose(fp);
+
+ return result;
+}
+
+static void FindEntry_no_match(benchmark::State& state) {
+ // Create a temporary zip archive.
+ std::unique_ptr<TemporaryFile> temp_file(CreateZip());
+ ZipArchiveHandle handle;
+ ZipEntry data;
+
+ // In order to walk through all file names in the archive, look for a name
+ // that does not exist in the archive.
+ ZipString name("thisFileNameDoesNotExist");
+
+ // Start the benchmark.
+ while (state.KeepRunning()) {
+ OpenArchive(temp_file->path, &handle);
+ FindEntry(handle, name, &data);
+ CloseArchive(handle);
+ }
+}
+BENCHMARK(FindEntry_no_match);
+
+static void Iterate_all_files(benchmark::State& state) {
+ std::unique_ptr<TemporaryFile> temp_file(CreateZip());
+ ZipArchiveHandle handle;
+ void* iteration_cookie;
+ ZipEntry data;
+ ZipString name;
+
+ while (state.KeepRunning()) {
+ OpenArchive(temp_file->path, &handle);
+ StartIteration(handle, &iteration_cookie, nullptr, nullptr);
+ while (Next(iteration_cookie, &data, &name) == 0) {
+ }
+ EndIteration(iteration_cookie);
+ CloseArchive(handle);
+ }
+}
+BENCHMARK(Iterate_all_files);
+
+BENCHMARK_MAIN()
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
index ca42509..8b99bde 100644
--- a/libziparchive/zip_archive_common.h
+++ b/libziparchive/zip_archive_common.h
@@ -57,6 +57,7 @@
uint32_t cd_start_offset;
// Length of the central directory comment.
uint16_t comment_length;
+
private:
EocdRecord() = default;
DISALLOW_COPY_AND_ASSIGN(EocdRecord);
@@ -73,7 +74,7 @@
// The start of record signature. Must be |kSignature|.
uint32_t record_signature;
- // Tool version. Ignored by this implementation.
+ // Source tool version. Top byte gives source OS.
uint16_t version_made_by;
// Tool version. Ignored by this implementation.
uint16_t version_needed;
@@ -106,11 +107,12 @@
uint16_t file_start_disk;
// File attributes. Ignored by this implementation.
uint16_t internal_file_attributes;
- // File attributes. Ignored by this implementation.
+ // File attributes. For archives created on Unix, the top bits are the mode.
uint32_t external_file_attributes;
// The offset to the local file header for this entry, from the
// beginning of this archive.
uint32_t local_file_header_offset;
+
private:
CentralDirectoryRecord() = default;
DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
@@ -149,6 +151,7 @@
// The length of the extra field info (in bytes). This data
// will appear immediately after the entry file name.
uint16_t extra_field_length;
+
private:
LocalFileHeader() = default;
DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
@@ -164,6 +167,7 @@
uint32_t compressed_size;
// Uncompressed size of the entry.
uint32_t uncompressed_size;
+
private:
DataDescriptor() = default;
DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 971db4f..174aa3f 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -26,24 +26,79 @@
#include <utils/FileMap.h>
#include <ziparchive/zip_archive.h>
+#include "android-base/macros.h"
+
+static const char* kErrorMessages[] = {
+ "Success",
+ "Iteration ended",
+ "Zlib error",
+ "Invalid file",
+ "Invalid handle",
+ "Duplicate entries in archive",
+ "Empty archive",
+ "Entry not found",
+ "Invalid offset",
+ "Inconsistent information",
+ "Invalid entry name",
+ "I/O error",
+ "File mapping failed",
+};
+
+enum ErrorCodes : int32_t {
+ kIterationEnd = -1,
+
+ // We encountered a Zlib error when inflating a stream from this file.
+ // Usually indicates file corruption.
+ kZlibError = -2,
+
+ // The input file cannot be processed as a zip archive. Usually because
+ // it's too small, too large or does not have a valid signature.
+ kInvalidFile = -3,
+
+ // An invalid iteration / ziparchive handle was passed in as an input
+ // argument.
+ kInvalidHandle = -4,
+
+ // The zip archive contained two (or possibly more) entries with the same
+ // name.
+ kDuplicateEntry = -5,
+
+ // The zip archive contains no entries.
+ kEmptyArchive = -6,
+
+ // The specified entry was not found in the archive.
+ kEntryNotFound = -7,
+
+ // The zip archive contained an invalid local file header pointer.
+ kInvalidOffset = -8,
+
+ // The zip archive contained inconsistent entry information. This could
+ // be because the central directory & local file header did not agree, or
+ // if the actual uncompressed length or crc32 do not match their declared
+ // values.
+ kInconsistentInformation = -9,
+
+ // An invalid entry name was encountered.
+ kInvalidEntryName = -10,
+
+ // An I/O related system call (read, lseek, ftruncate, map) failed.
+ kIoError = -11,
+
+ // We were not able to mmap the central directory or entry contents.
+ kMmapFailed = -12,
+
+ kLastErrorCode = kMmapFailed,
+};
class MappedZipFile {
public:
- explicit MappedZipFile(const int fd) :
- has_fd_(true),
- fd_(fd),
- base_ptr_(nullptr),
- data_length_(0),
- read_pos_(0) {}
+ explicit MappedZipFile(const int fd)
+ : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0) {}
- explicit MappedZipFile(void* address, size_t length) :
- has_fd_(false),
- fd_(-1),
- base_ptr_(address),
- data_length_(static_cast<off64_t>(length)),
- read_pos_(0) {}
+ explicit MappedZipFile(void* address, size_t length)
+ : has_fd_(false), fd_(-1), base_ptr_(address), data_length_(static_cast<off64_t>(length)) {}
- bool HasFd() const {return has_fd_;}
+ bool HasFd() const { return has_fd_; }
int GetFileDescriptor() const;
@@ -51,10 +106,6 @@
off64_t GetFileLength() const;
- bool SeekToOffset(off64_t offset);
-
- bool ReadData(uint8_t* buffer, size_t read_amount);
-
bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off);
private:
@@ -68,19 +119,15 @@
void* const base_ptr_;
const off64_t data_length_;
- // read_pos_ is the offset to the base_ptr_ where we read data from.
- size_t read_pos_;
};
class CentralDirectory {
public:
- CentralDirectory(void) :
- base_ptr_(nullptr),
- length_(0) {}
+ CentralDirectory(void) : base_ptr_(nullptr), length_(0) {}
- const uint8_t* GetBasePtr() const {return base_ptr_;}
+ const uint8_t* GetBasePtr() const { return base_ptr_; }
- size_t GetMapLength() const {return length_;}
+ size_t GetMapLength() const { return length_; }
void Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
@@ -109,25 +156,25 @@
uint32_t hash_table_size;
ZipString* hash_table;
- ZipArchive(const int fd, bool assume_ownership) :
- mapped_zip(fd),
- close_file(assume_ownership),
- directory_offset(0),
- central_directory(),
- directory_map(new android::FileMap()),
- num_entries(0),
- hash_table_size(0),
- hash_table(nullptr) {}
+ ZipArchive(const int fd, bool assume_ownership)
+ : mapped_zip(fd),
+ close_file(assume_ownership),
+ directory_offset(0),
+ central_directory(),
+ directory_map(new android::FileMap()),
+ num_entries(0),
+ hash_table_size(0),
+ hash_table(nullptr) {}
- ZipArchive(void* address, size_t length) :
- mapped_zip(address, length),
- close_file(false),
- directory_offset(0),
- central_directory(),
- directory_map(new android::FileMap()),
- num_entries(0),
- hash_table_size(0),
- hash_table(nullptr) {}
+ ZipArchive(void* address, size_t length)
+ : mapped_zip(address, length),
+ close_file(false),
+ directory_offset(0),
+ central_directory(),
+ directory_map(new android::FileMap()),
+ num_entries(0),
+ hash_table_size(0),
+ hash_table(nullptr) {}
~ZipArchive() {
if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
@@ -139,7 +186,6 @@
bool InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
size_t cd_size);
-
};
#endif // LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
index 3f336a6..9ec89b1 100644
--- a/libziparchive/zip_archive_stream_entry.cc
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -38,13 +38,8 @@
static constexpr size_t kBufSize = 65535;
bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
- ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
- off64_t data_offset = entry.offset;
- if (!archive->mapped_zip.SeekToOffset(data_offset)) {
- ALOGW("lseek to data at %" PRId64 " failed: %s", data_offset, strerror(errno));
- return false;
- }
crc32_ = entry.crc32;
+ offset_ = entry.offset;
return true;
}
@@ -61,11 +56,11 @@
protected:
bool Init(const ZipEntry& entry) override;
- uint32_t length_;
+ uint32_t length_ = 0u;
private:
std::vector<uint8_t> data_;
- uint32_t computed_crc32_;
+ uint32_t computed_crc32_ = 0u;
};
bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
@@ -89,7 +84,7 @@
size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
errno = 0;
- if (!archive->mapped_zip.ReadData(data_.data(), bytes)) {
+ if (!archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_)) {
if (errno != 0) {
ALOGE("Error reading from archive fd: %s", strerror(errno));
} else {
@@ -104,6 +99,7 @@
}
computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
length_ -= bytes;
+ offset_ += bytes;
return &data_;
}
@@ -129,9 +125,9 @@
z_stream z_stream_;
std::vector<uint8_t> in_;
std::vector<uint8_t> out_;
- uint32_t uncompressed_length_;
- uint32_t compressed_length_;
- uint32_t computed_crc32_;
+ uint32_t uncompressed_length_ = 0u;
+ uint32_t compressed_length_ = 0u;
+ uint32_t computed_crc32_ = 0u;
};
// This method is using libz macros with old-style-casts
@@ -162,8 +158,7 @@
int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
if (zerr != Z_OK) {
if (zerr == Z_VERSION_ERROR) {
- ALOGE("Installed zlib is not compatible with linked version (%s)",
- ZLIB_VERSION);
+ ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
} else {
ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
}
@@ -193,13 +188,14 @@
bool ZipArchiveStreamEntryCompressed::Verify() {
return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
- crc32_ == computed_crc32_;
+ crc32_ == computed_crc32_;
}
const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
if (z_stream_.avail_out == 0) {
z_stream_.next_out = out_.data();
- z_stream_.avail_out = out_.size();;
+ z_stream_.avail_out = out_.size();
+ ;
}
while (true) {
@@ -210,7 +206,7 @@
size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
errno = 0;
- if (!archive->mapped_zip.ReadData(in_.data(), bytes)) {
+ if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
if (errno != 0) {
ALOGE("Error reading from archive fd: %s", strerror(errno));
} else {
@@ -220,15 +216,15 @@
}
compressed_length_ -= bytes;
+ offset_ += bytes;
z_stream_.next_in = in_.data();
z_stream_.avail_in = bytes;
}
int zerr = inflate(&z_stream_, Z_NO_FLUSH);
if (zerr != Z_OK && zerr != Z_STREAM_END) {
- ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
- zerr, z_stream_.next_in, z_stream_.avail_in,
- z_stream_.next_out, z_stream_.avail_out);
+ ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, z_stream_.next_in,
+ z_stream_.avail_in, z_stream_.next_out, z_stream_.avail_out);
return nullptr;
}
@@ -276,8 +272,8 @@
return length_ == 0;
}
-ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(
- ZipArchiveHandle handle, const ZipEntry& entry) {
+ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle,
+ const ZipEntry& entry) {
ZipArchiveStreamEntry* stream = nullptr;
if (entry.method != kCompressStored) {
stream = new ZipArchiveStreamEntryCompressed(handle);
@@ -292,8 +288,8 @@
return stream;
}
-ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(
- ZipArchiveHandle handle, const ZipEntry& entry) {
+ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle,
+ const ZipEntry& entry) {
ZipArchiveStreamEntry* stream = nullptr;
if (entry.method == kCompressStored) {
// Not compressed, don't need to do anything special.
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 493a0ce..dbc14f0 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "zip_archive_private.h"
+
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
@@ -42,21 +44,13 @@
static const std::string kBadFilenameZip = "bad_filename.zip";
static const std::string kUpdateZip = "dummy-update.zip";
-static const std::vector<uint8_t> kATxtContents {
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
- '\n'
-};
+static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a',
+ 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
-static const std::vector<uint8_t> kATxtContentsCompressed {
- 'K', 'L', 'J', 'N', 'I', 'M', 'K', 207, 'H',
- 132, 210, '\\', '\0'
-};
+static const std::vector<uint8_t> kATxtContentsCompressed{'K', 'L', 'J', 'N', 'I', 'M', 'K',
+ 207, 'H', 132, 210, '\\', '\0'};
-static const std::vector<uint8_t> kBTxtContents {
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
- '\n'
-};
+static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
static const std::string kATxtName("a.txt");
static const std::string kBTxtName("b.txt");
@@ -65,14 +59,12 @@
static const std::string kLargeCompressTxtName("compress.txt");
static const std::string kLargeUncompressTxtName("uncompress.txt");
-static int32_t OpenArchiveWrapper(const std::string& name,
- ZipArchiveHandle* handle) {
+static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) {
const std::string abs_path = test_data_dir + "/" + name;
return OpenArchive(abs_path.c_str(), handle);
}
-static void AssertNameEquals(const std::string& name_str,
- const ZipString& name) {
+static void AssertNameEquals(const std::string& name_str, const ZipString& name) {
ASSERT_EQ(name_str.size(), name.name_length);
ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
}
@@ -343,47 +335,36 @@
}
static const uint32_t kEmptyEntriesZip[] = {
- 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000,
- 0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13,
- 0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b,
- 0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000,
- 0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974,
- 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
- 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
+ 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000,
+ 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875,
+ 0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863,
+ 0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081,
+ 0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904,
+ 0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000};
// This is a zip file containing a single entry (ab.txt) that contains
// 90072 repetitions of the string "ab\n" and has an uncompressed length
// of 270216 bytes.
static const uint16_t kAbZip[] = {
- 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0,
- 0x2cda, 0x011b, 0x0000, 0x1f88, 0x0004, 0x0006, 0x001c, 0x6261,
- 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
- 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000,
- 0xc2ed, 0x0d31, 0x0000, 0x030c, 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa,
- 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02,
- 0x1403, 0x0000, 0x0800, 0xd200, 0x9851, 0xb046, 0xdac4, 0x1b2c,
- 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
- 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574,
- 0x0554, 0x0300, 0x097c, 0x553a, 0x7875, 0x000b, 0x0401, 0x4289,
- 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
- 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000
-};
+ 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88,
+ 0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
+ 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c,
+ 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200,
+ 0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
+ 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a,
+ 0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
+ 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000};
static const std::string kAbTxtName("ab.txt");
static const size_t kAbUncompressedSize = 270216;
@@ -404,7 +385,6 @@
uint8_t buffer[1];
ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
-
TemporaryFile tmp_output_file;
ASSERT_NE(-1, tmp_output_file.fd);
ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
@@ -418,7 +398,7 @@
TemporaryFile tmp_file;
ASSERT_NE(-1, tmp_file.fd);
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
- sizeof(kAbZip) - 1));
+ sizeof(kAbZip) - 1));
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle));
@@ -446,8 +426,7 @@
// the same as the memory buffer we extracted directly to.
std::vector<uint8_t> file_contents(kAbUncompressedSize);
ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
- ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0],
- file_contents.size()));
+ ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
ASSERT_EQ(file_contents, buffer);
for (int i = 0; i < 90072; ++i) {
@@ -463,7 +442,7 @@
ASSERT_NE(-1, tmp_file.fd);
// Create a file with 8 bytes of random garbage.
- static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
+ static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'};
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
@@ -474,7 +453,7 @@
TEST(ziparchive, ExtractToFile) {
TemporaryFile tmp_file;
ASSERT_NE(-1, tmp_file.fd);
- const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
+ const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
const size_t data_size = sizeof(data);
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
@@ -488,7 +467,6 @@
ASSERT_EQ(0, FindEntry(handle, name, &entry));
ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
-
// Assert that the first 8 bytes of the file haven't been clobbered.
uint8_t read_buffer[data_size];
ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
@@ -497,10 +475,9 @@
// Assert that the remainder of the file contains the incompressed data.
std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
- ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
- entry.uncompressed_length));
- ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(),
- kATxtContents.size()));
+ ASSERT_TRUE(
+ android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), entry.uncompressed_length));
+ ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
// Assert that the total length of the file is sane
ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
@@ -517,7 +494,7 @@
// Memory map the file first and open the archive from the memory region.
android::FileMap file_map;
- file_map.create(zip_path.c_str(), fd, 0/*offset*/, sb.st_size, true);
+ file_map.create(zip_path.c_str(), fd, 0 /*offset*/, sb.st_size, true);
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFromMemory(file_map.getDataPtr(), file_map.getDataLength(),
zip_path.c_str(), &handle));
@@ -533,9 +510,8 @@
}
#endif
-static void ZipArchiveStreamTest(
- ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
- bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
+static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
+ bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
ZipString name;
SetZipString(&name, entry_name);
ASSERT_EQ(0, FindEntry(handle, name, entry));
@@ -564,9 +540,9 @@
ASSERT_EQ(total_size, read_data->size());
}
-static void ZipArchiveStreamTestUsingContents(
- const std::string& zip_file, const std::string& entry_name,
- const std::vector<uint8_t>& contents, bool raw) {
+static void ZipArchiveStreamTestUsingContents(const std::string& zip_file,
+ const std::string& entry_name,
+ const std::vector<uint8_t>& contents, bool raw) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
@@ -580,7 +556,8 @@
CloseArchive(handle);
}
-static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file, const std::string& entry_name) {
+static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file,
+ const std::string& entry_name) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
@@ -642,13 +619,109 @@
CloseArchive(handle);
}
+// Generated using the following Java program:
+// public static void main(String[] foo) throws Exception {
+// FileOutputStream fos = new
+// FileOutputStream("/tmp/data_descriptor.zip");
+// ZipOutputStream zos = new ZipOutputStream(fos);
+// ZipEntry ze = new ZipEntry("name");
+// ze.setMethod(ZipEntry.DEFLATED);
+// zos.putNextEntry(ze);
+// zos.write("abdcdefghijk".getBytes());
+// zos.closeEntry();
+// zos.close();
+// }
+//
+// cat /tmp/data_descriptor.zip | xxd -i
+//
+static const std::vector<uint8_t> kDataDescriptorZipFile{
+ 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
+ 0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
+ //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
+ 0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
+ 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
+ 0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
+ 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+// The offsets of the data descriptor in this file, so we can mess with
+// them later in the test.
+static constexpr uint32_t kDataDescriptorOffset = 48;
+static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
+static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
+
+static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
+ std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle));
+
+ // This function expects a variant of kDataDescriptorZipFile, for look for
+ // an entry whose name is "name" and whose size is 12 (contents =
+ // "abdcdefghijk").
+ ZipEntry entry;
+ ZipString empty_name;
+ SetZipString(&empty_name, "name");
+
+ ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+ ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
+
+ entry_out->resize(12);
+ (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, ValidDataDescriptors) {
+ std::vector<uint8_t> entry;
+ int32_t error_code = 0;
+ ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
+
+ ASSERT_EQ(0, error_code);
+ ASSERT_EQ(12u, entry.size());
+ ASSERT_EQ('a', entry[0]);
+ ASSERT_EQ('k', entry[11]);
+}
+
+TEST(ziparchive, InvalidDataDescriptors) {
+ std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
+ invalid_csize[kCSizeOffset] = 0xfe;
+
+ std::vector<uint8_t> entry;
+ int32_t error_code = 0;
+ ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+ ASSERT_EQ(kInconsistentInformation, error_code);
+
+ std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
+ invalid_csize[kSizeOffset] = 0xfe;
+
+ error_code = 0;
+ entry.clear();
+ ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+ ASSERT_EQ(kInconsistentInformation, error_code);
+}
+
+TEST(ziparchive, ErrorCodeString) {
+ ASSERT_STREQ("Success", ErrorCodeString(0));
+
+ // Out of bounds.
+ ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
+ ASSERT_STREQ("Unknown return code", ErrorCodeString(-13));
+
+ ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
+}
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
- static struct option options[] = {
- { "test_data_dir", required_argument, nullptr, 't' },
- { nullptr, 0, nullptr, 0 }
- };
+ static struct option options[] = {{"test_data_dir", required_argument, nullptr, 't'},
+ {nullptr, 0, nullptr, 0}};
while (true) {
int option_index;
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 6d28bdb..6ad3366 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -16,11 +16,11 @@
#include "ziparchive/zip_writer.h"
-#include <cstdio>
#include <sys/param.h>
#include <sys/stat.h>
#include <zlib.h>
-#define DEF_MEM_LEVEL 8 // normally in zutil.h?
+#include <cstdio>
+#define DEF_MEM_LEVEL 8 // normally in zutil.h?
#include <memory>
#include <vector>
@@ -33,13 +33,13 @@
#include "zip_archive_common.h"
#if !defined(powerof2)
-#define powerof2(x) ((((x)-1)&(x))==0)
+#define powerof2(x) ((((x)-1) & (x)) == 0)
#endif
/* Zip compression methods we support */
enum {
- kCompressStored = 0, // no compression
- kCompressDeflated = 8, // standard deflate
+ kCompressStored = 0, // no compression
+ kCompressDeflated = 8, // standard deflate
};
// Size of the output buffer used for compression.
@@ -67,10 +67,7 @@
static const int32_t kInvalidAlignment = -6;
static const char* sErrorCodes[] = {
- "Invalid state",
- "IO error",
- "Invalid entry name",
- "Zlib error",
+ "Invalid state", "IO error", "Invalid entry name", "Zlib error",
};
const char* ZipWriter::ErrorCodeString(int32_t error_code) {
@@ -85,9 +82,13 @@
delete stream;
}
-ZipWriter::ZipWriter(FILE* f) : file_(f), seekable_(false), current_offset_(0),
- state_(State::kWritingZip), z_stream_(nullptr, DeleteZStream),
- buffer_(kBufSize) {
+ZipWriter::ZipWriter(FILE* f)
+ : file_(f),
+ seekable_(false),
+ current_offset_(0),
+ state_(State::kWritingZip),
+ z_stream_(nullptr, DeleteZStream),
+ buffer_(kBufSize) {
// Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls
// will fail as well.
struct stat file_stats;
@@ -96,13 +97,14 @@
}
}
-ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
- seekable_(writer.seekable_),
- current_offset_(writer.current_offset_),
- state_(writer.state_),
- files_(std::move(writer.files_)),
- z_stream_(std::move(writer.z_stream_)),
- buffer_(std::move(writer.buffer_)){
+ZipWriter::ZipWriter(ZipWriter&& writer)
+ : file_(writer.file_),
+ seekable_(writer.seekable_),
+ current_offset_(writer.current_offset_),
+ state_(writer.state_),
+ files_(std::move(writer.files_)),
+ z_stream_(std::move(writer.z_stream_)),
+ buffer_(std::move(writer.buffer_)) {
writer.file_ = nullptr;
writer.state_ = State::kError;
}
@@ -154,10 +156,10 @@
struct tm* ptm;
#if !defined(_WIN32)
- struct tm tm_result;
- ptm = localtime_r(&when, &tm_result);
+ struct tm tm_result;
+ ptm = localtime_r(&when, &tm_result);
#else
- ptm = localtime(&when);
+ ptm = localtime(&when);
#endif
int year = ptm->tm_year;
@@ -193,8 +195,8 @@
dst->extra_field_length = src.padding_length;
}
-int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
- time_t time, uint32_t alignment) {
+int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
+ uint32_t alignment) {
if (state_ != State::kWritingZip) {
return kInvalidState;
}
@@ -252,9 +254,8 @@
return HandleError(kIoError);
}
- if (file_entry.padding_length != 0 &&
- fwrite(zero_padding.data(), 1, file_entry.padding_length, file_)
- != file_entry.padding_length) {
+ if (file_entry.padding_length != 0 && fwrite(zero_padding.data(), 1, file_entry.padding_length,
+ file_) != file_entry.padding_length) {
return HandleError(kIoError);
}
@@ -292,7 +293,7 @@
CHECK(state_ == State::kWritingZip);
// Initialize the z_stream for compression.
- z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream);
+ z_stream_ = std::unique_ptr<z_stream, void (*)(z_stream*)>(new z_stream(), DeleteZStream);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
@@ -331,8 +332,8 @@
return result;
}
- current_file_entry_.crc32 = crc32(current_file_entry_.crc32,
- reinterpret_cast<const Bytef*>(data), len);
+ current_file_entry_.crc32 =
+ crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len);
current_file_entry_.uncompressed_size += len;
return kNoError;
}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 5b526a4..c284273 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "ziparchive/zip_archive.h"
#include "ziparchive/zip_writer.h"
+#include "ziparchive/zip_archive.h"
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
@@ -135,17 +135,6 @@
CloseArchive(handle);
}
-static void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
- memset(tm, 0, sizeof(struct tm));
- tm->tm_hour = (zip_time >> 11) & 0x1f;
- tm->tm_min = (zip_time >> 5) & 0x3f;
- tm->tm_sec = (zip_time & 0x1f) << 1;
-
- tm->tm_year = ((zip_time >> 25) & 0x7f) + 80;
- tm->tm_mon = ((zip_time >> 21) & 0xf) - 1;
- tm->tm_mday = (zip_time >> 16) & 0x1f;
-}
-
static struct tm MakeTm() {
struct tm tm;
memset(&tm, 0, sizeof(struct tm));
@@ -177,8 +166,7 @@
ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
EXPECT_EQ(0, data.offset & 0x03);
- struct tm mod;
- ConvertZipTimeToTm(data.mod_time, &mod);
+ struct tm mod = data.GetModificationTime();
EXPECT_EQ(tm.tm_sec, mod.tm_sec);
EXPECT_EQ(tm.tm_min, mod.tm_min);
EXPECT_EQ(tm.tm_hour, mod.tm_hour);
@@ -228,8 +216,7 @@
ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
EXPECT_EQ(0, data.offset & 0xfff);
- struct tm mod;
- ConvertZipTimeToTm(data.mod_time, &mod);
+ struct tm mod = data.GetModificationTime();
EXPECT_EQ(tm.tm_sec, mod.tm_sec);
EXPECT_EQ(tm.tm_min, mod.tm_min);
EXPECT_EQ(tm.tm_hour, mod.tm_hour);
@@ -374,7 +361,7 @@
ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
std::vector<uint8_t> data;
- data.resize(1024*1024, 0xef);
+ data.resize(1024 * 1024, 0xef);
ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
ASSERT_EQ(0, writer.FinishEntry());
@@ -395,8 +382,9 @@
ZipArchiveHandle handle,
ZipEntry* zip_entry) {
if (expected.size() != zip_entry->uncompressed_length) {
- return ::testing::AssertionFailure() << "uncompressed entry size "
- << zip_entry->uncompressed_length << " does not match expected size " << expected.size();
+ return ::testing::AssertionFailure()
+ << "uncompressed entry size " << zip_entry->uncompressed_length
+ << " does not match expected size " << expected.size();
}
std::string actual;
@@ -409,7 +397,7 @@
if (expected != actual) {
return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
- << "' does not match expected '" << expected << "'";
+ << "' does not match expected '" << expected << "'";
}
return ::testing::AssertionSuccess();
}
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
new file mode 100644
index 0000000..3f8a503
--- /dev/null
+++ b/lmkd/Android.bp
@@ -0,0 +1,13 @@
+cc_binary {
+ name: "lmkd",
+
+ srcs: ["lmkd.c"],
+ shared_libs: [
+ "liblog",
+ "libprocessgroup",
+ "libcutils",
+ ],
+ cflags: ["-Werror"],
+
+ init_rc: ["lmkd.rc"],
+}
diff --git a/lmkd/Android.mk b/lmkd/Android.mk
deleted file mode 100644
index 8980d1c..0000000
--- a/lmkd/Android.mk
+++ /dev/null
@@ -1,12 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := lmkd.c
-LOCAL_SHARED_LIBRARIES := liblog libm libc libprocessgroup libcutils
-LOCAL_CFLAGS := -Werror
-
-LOCAL_MODULE := lmkd
-
-LOCAL_INIT_RC := lmkd.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/logcat/Android.bp b/logcat/Android.bp
new file mode 100644
index 0000000..729c8ff
--- /dev/null
+++ b/logcat/Android.bp
@@ -0,0 +1,74 @@
+//
+// Copyright (C) 2006-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.
+//
+
+cc_defaults {
+ name: "logcat_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libpcrecpp",
+ ],
+ logtags: ["event.logtags"],
+}
+
+cc_library {
+ name: "liblogcat",
+
+ defaults: ["logcat_defaults"],
+ srcs: [
+ "logcat.cpp",
+ "getopt_long.cpp",
+ "logcat_system.cpp",
+ ],
+ export_include_dirs: ["include"],
+}
+
+cc_binary {
+ name: "logcat",
+
+ defaults: ["logcat_defaults"],
+ shared_libs: ["liblogcat"],
+ srcs: [
+ "logcat_main.cpp",
+ ],
+}
+
+cc_binary {
+ name: "logcatd",
+
+ defaults: ["logcat_defaults"],
+ shared_libs: ["liblogcat"],
+ srcs: [
+ "logcatd_main.cpp",
+ ],
+}
+
+cc_prebuilt_binary {
+ name: "logpersist.start",
+ srcs: ["logpersist"],
+ init_rc: ["logcatd.rc"],
+ symlinks: ["logpersist.stop", "logpersist.cat"],
+ strip: {
+ none: true,
+ }
+}
diff --git a/logcat/Android.mk b/logcat/Android.mk
index 4e11ca9..a716993 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -2,48 +2,4 @@
LOCAL_PATH := $(call my-dir)
-logcatLibs := liblog libbase libcutils libpcrecpp
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logcat
-LOCAL_SRC_FILES := logcat_main.cpp event.logtags
-LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logcatd
-LOCAL_MODULE_TAGS := debug
-LOCAL_SRC_FILES := logcatd_main.cpp event.logtags
-LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := liblogcat
-LOCAL_SRC_FILES := logcat.cpp getopt_long.cpp logcat_system.cpp
-LOCAL_SHARED_LIBRARIES := $(logcatLibs)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logpersist.start
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_INIT_RC := logcatd.rc
-LOCAL_MODULE_PATH := $(bin_dir)
-LOCAL_SRC_FILES := logpersist
-ALL_TOOLS := logpersist.start logpersist.stop logpersist.cat
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(filter-out $(LOCAL_MODULE),$(ALL_TOOLS)),ln -sf $(LOCAL_MODULE) $(TARGET_OUT)/bin/$(t);)
-include $(BUILD_PREBUILT)
-
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 909f8e2..efcc817 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -30,12 +30,15 @@
# 4: Number of allocations
# 5: Id
# 6: Percent
+# s: Number of seconds (monotonic time)
# Default value for data of type int/long is 2 (bytes).
#
# TODO: generate ".java" and ".h" files with integer constants from this file.
# These are used for testing, do not modify without updating
# tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+# system/core/liblog/tests/liblog_benchmark.cpp
+# system/core/liblog/tests/liblog_test.cpp
42 answer (to life the universe etc|3)
314 pi
2718 e
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 64d1d2f..3d56472 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -530,6 +530,7 @@
" process — Display PID only.\n"
" raw — Display the raw log message, with no other metadata fields.\n"
" tag — Display the priority/tag only.\n"
+ " thread — Display priority, PID and TID of process issuing the message.\n"
" threadtime — Display the date, invocation time, priority, tag, and the PID\n"
" and TID of the thread issuing the message. (the default format).\n"
" time — Display the date, invocation time, priority/tag, and PID of the\n"
@@ -1018,7 +1019,6 @@
break;
case 'm': {
- char* end = nullptr;
if (!getSizeTArg(optctx.optarg, &context->maxCount)) {
logcat_panic(context, HELP_FALSE,
"-%c \"%s\" isn't an "
@@ -1181,7 +1181,6 @@
std::unique_ptr<char, void (*)(void*)> formats(
strdup(optctx.optarg), free);
char* arg = formats.get();
- unsigned idMask = 0;
char* sv = nullptr; // protect against -ENOMEM above
while (!!(arg = strtok_r(arg, delimiters, &sv))) {
err = setLogFormat(context, arg);
@@ -1255,7 +1254,7 @@
// example: "qemu_pipe,pipe:logcat"
// upon opening of /dev/qemu_pipe, the "pipe:logcat"
// string with trailing '\0' should be written to the fd
- size_t pos = devname.find(",");
+ size_t pos = devname.find(',');
if (pos != std::string::npos) {
pipePurpose = devname.substr(pos + 1);
devname = devname.substr(0, pos);
@@ -1637,7 +1636,7 @@
logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
break;
}
- logcat_panic(context, HELP_FALSE, "logcat read failure");
+ logcat_panic(context, HELP_FALSE, "logcat read failure\n");
break;
}
@@ -1732,7 +1731,7 @@
pthread_attr_setschedparam(&attr, ¶m);
pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
- int save_errno = errno;
+ save_errno = errno;
goto pthread_attr_exit;
}
@@ -1772,7 +1771,7 @@
context->retval = EXIT_SUCCESS;
if (pthread_create(&context->thr, &attr,
(void*(*)(void*))__logcat, context)) {
- int save_errno = errno;
+ save_errno = errno;
goto argv_exit;
}
pthread_attr_destroy(&attr);
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 06cc90d..07040b0 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -61,3 +61,4 @@
user logd
group log
writepid /dev/cpuset/system-background/tasks
+ oom_score_adjust -600
diff --git a/logcat/tests/liblogcat_test.cpp b/logcat/tests/liblogcat_test.cpp
index 9e9a2c2..c8a00da 100644
--- a/logcat/tests/liblogcat_test.cpp
+++ b/logcat/tests/liblogcat_test.cpp
@@ -17,8 +17,8 @@
#include <log/logcat.h>
#define logcat_define(context) android_logcat_context context
-#define logcat_popen(context, command) android_logcat_popen(&context, command)
-#define logcat_pclose(context, fp) android_logcat_pclose(&context, fp)
+#define logcat_popen(context, command) android_logcat_popen(&(context), command)
+#define logcat_pclose(context, fp) android_logcat_pclose(&(context), fp)
#define logcat_system(command) android_logcat_system(command)
#define logcat liblogcat
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 21868f2..786fb14 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -17,6 +17,7 @@
#include <ctype.h>
#include <dirent.h>
#include <signal.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -30,7 +31,9 @@
#include <string>
#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
+#include <log/event_tag_map.h>
#include <log/log.h>
#include <log/log_event_list.h>
@@ -47,6 +50,16 @@
#define BIG_BUFFER (5 * 1024)
+// rest(), let the logs settle.
+//
+// logd is in a background cgroup and under extreme load can take up to
+// 3 seconds to land a log entry. Under moderate load we can do with 200ms.
+static void rest() {
+ static const useconds_t restPeriod = 200000;
+
+ usleep(restPeriod);
+}
+
// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
// non-syscall libs. Since we are only using this in the emergency of
// a signal to stuff a terminating code into the logs, we will spin rather
@@ -68,9 +81,14 @@
logcat_define(ctx);
#undef LOG_TAG
-#define LOG_TAG "inject"
- RLOGE(logcat_executable ".buckets");
- sleep(1);
+#define LOG_TAG "inject.buckets"
+ // inject messages into radio, system, main and events buffers to
+ // ensure that we see all the begin[] bucket messages.
+ RLOGE(logcat_executable);
+ SLOGE(logcat_executable);
+ ALOGE(logcat_executable);
+ __android_log_bswrite(0, logcat_executable ".inject.buckets");
+ rest();
ASSERT_TRUE(NULL !=
(fp = logcat_popen(
@@ -95,32 +113,45 @@
logcat_pclose(ctx, fp);
- EXPECT_EQ(15, ids);
+ EXPECT_EQ(ids, 15);
- EXPECT_EQ(4, count);
+ EXPECT_EQ(count, 4);
}
TEST(logcat, event_tag_filter) {
FILE* fp;
logcat_define(ctx);
- ASSERT_TRUE(NULL !=
- (fp = logcat_popen(ctx, logcat_executable
- " -b events -d -s auditd "
- "am_proc_start am_pss am_proc_bound "
- "dvm_lock_sample am_wtf 2>/dev/null")));
+#undef LOG_TAG
+#define LOG_TAG "inject.filter"
+ // inject messages into radio, system and main buffers
+ // with our unique log tag to test logcat filter.
+ RLOGE(logcat_executable);
+ SLOGE(logcat_executable);
+ ALOGE(logcat_executable);
+ rest();
+
+ std::string command = android::base::StringPrintf(
+ logcat_executable
+ " -b radio -b system -b main --pid=%d -d -s inject.filter 2>/dev/null",
+ getpid());
+ ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, command.c_str())));
char buffer[BIG_BUFFER];
int count = 0;
while (fgets(buffer, sizeof(buffer), fp)) {
- ++count;
+ if (strncmp(begin, buffer, sizeof(begin) - 1)) ++count;
}
logcat_pclose(ctx, fp);
- EXPECT_LT(4, count);
+ // logcat, liblogcat and logcatd test instances result in the progression
+ // of 3, 6 and 9 for our counts as each round is performed.
+ EXPECT_GE(count, 3);
+ EXPECT_LE(count, 9);
+ EXPECT_EQ(count % 3, 0);
}
// If there is not enough background noise in the logs, then spam the logs to
@@ -364,7 +395,7 @@
} while ((count < 10) && --tries && inject(10 - count));
- EXPECT_EQ(10, count); // We want _some_ history, too small, falses below
+ EXPECT_EQ(count, 10); // We want _some_ history, too small, falses below
EXPECT_TRUE(last_timestamp != NULL);
EXPECT_TRUE(first_timestamp != NULL);
EXPECT_TRUE(second_timestamp != NULL);
@@ -689,9 +720,9 @@
pclose(fp);
- EXPECT_LE(2, count);
+ EXPECT_GE(count, 2);
- EXPECT_EQ(1, signals);
+ EXPECT_EQ(signals, 1);
}
static void caught_blocking_tail(int signum) {
@@ -759,9 +790,9 @@
pclose(fp);
- EXPECT_LE(2, count);
+ EXPECT_GE(count, 2);
- EXPECT_EQ(1, signals);
+ EXPECT_EQ(signals, 1);
}
#endif
@@ -869,7 +900,7 @@
ADD_FAILURE();
}
pclose(fp);
- EXPECT_EQ(11, log_file_count);
+ EXPECT_EQ(log_file_count, 11);
}
snprintf(command, sizeof(command), "rm -rf %s", tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
@@ -1122,17 +1153,17 @@
char tmp_out_dir[strlen(tmp_out_dir_form) + 1];
ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
- EXPECT_EQ(34, logrotate_count_id(logcat_cmd, tmp_out_dir));
- EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+ EXPECT_EQ(logrotate_count_id(logcat_cmd, tmp_out_dir), 34);
+ EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
char id_file[strlen(tmp_out_dir_form) + strlen(log_filename) + 5];
snprintf(id_file, sizeof(id_file), "%s/%s.id", tmp_out_dir, log_filename);
if (getuid() != 0) {
chmod(id_file, 0);
- EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+ EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
}
unlink(id_file);
- EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+ EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
FILE* fp = fopen(id_file, "w");
if (fp) {
@@ -1147,9 +1178,9 @@
}
int new_signature;
- EXPECT_LE(
- 2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)));
- EXPECT_GT(34, new_signature);
+ EXPECT_GE(
+ (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)), 2);
+ EXPECT_LT(new_signature, 34);
static const char cleanup_cmd[] = "rm -rf %s";
char command[strlen(cleanup_cmd) + strlen(tmp_out_dir_form)];
@@ -1204,7 +1235,12 @@
signal(SIGALRM, caught_blocking_clear);
alarm(2);
while (fgets(buffer, sizeof(buffer), fp)) {
- if (!strncmp(buffer, "clearLog: ", 10)) {
+ if (!strncmp(buffer, "clearLog: ", strlen("clearLog: "))) {
+ fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
+ count = signals = 1;
+ break;
+ }
+ if (!strncmp(buffer, "failed to clear", strlen("failed to clear"))) {
fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
count = signals = 1;
break;
@@ -1285,10 +1321,10 @@
pclose(fp);
- EXPECT_LE(1, count);
- EXPECT_EQ(1, minus_g);
+ EXPECT_GE(count, 1);
+ EXPECT_EQ(minus_g, 1);
- EXPECT_EQ(1, signals);
+ EXPECT_EQ(signals, 1);
}
#endif
@@ -1412,7 +1448,7 @@
LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
logcat_regex_prefix "_aaaa"));
// Let the logs settle
- sleep(1);
+ rest();
ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
@@ -1450,8 +1486,7 @@
LOG_FAILURE_RETRY(
__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
- // Let the logs settle
- sleep(1);
+ rest();
ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
@@ -1476,8 +1511,7 @@
static bool End_to_End(const char* tag, const char* fmt, ...) {
logcat_define(ctx);
- FILE* fp = logcat_popen(ctx,
- "logcat"
+ FILE* fp = logcat_popen(ctx, logcat_executable
" -v brief"
" -b events"
" -v descriptive"
@@ -1523,13 +1557,12 @@
// Help us pinpoint where things went wrong ...
fprintf(stderr, "Closest match for\n %s\n is\n %s",
expect.c_str(), lastMatch.c_str());
- } else if (count > 2) {
+ } else if (count > 3) {
fprintf(stderr, "Too many matches (%d) for %s\n", count, expect.c_str());
}
- // Expect one the first time around as either liblogcat.descriptive or
- // logcat.descriptive. Expect two the second time as the other.
- return count == 1 || count == 2;
+ // Three different known tests, we can see pollution from the others
+ return count && (count <= 3);
}
TEST(logcat, descriptive) {
@@ -1537,24 +1570,28 @@
uint32_t tagNo;
const char* tagStr;
};
+ int ret;
{
static const struct tag hhgtg = { 42, "answer" };
android_log_event_list ctx(hhgtg.tagNo);
static const char theAnswer[] = "what is five by seven";
ctx << theAnswer;
- ctx.write();
+ // crafted to rest at least once after, and rest between retries.
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(
End_to_End(hhgtg.tagStr, "to life the universe etc=%s", theAnswer));
}
{
static const struct tag sync = { 2720, "sync" };
- static const char id[] = "logcat.decriptive";
+ static const char id[] = ___STRING(logcat) ".descriptive-sync";
{
android_log_event_list ctx(sync.tagNo);
ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr,
"[id=%s,event=42,source=-1,account=0]", id));
}
@@ -1563,7 +1600,8 @@
{
android_log_event_list ctx(sync.tagNo);
ctx << id << (int32_t)43 << (int64_t)-1 << (int32_t)0;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "[id=%s,event=43,-1,0]", id));
}
@@ -1571,7 +1609,8 @@
{
android_log_event_list ctx(sync.tagNo);
ctx << id << (int32_t)44 << (int32_t)-1 << (int64_t)0;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
fprintf(stderr, "Expect a \"Closest match\" message\n");
EXPECT_FALSE(End_to_End(
sync.tagStr, "[id=%s,event=44,source=-1,account=0]", id));
@@ -1583,7 +1622,8 @@
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint64_t)30 << (int32_t)2;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(
End_to_End(sync.tagStr, "[aggregation time=30ms,count=2]"));
}
@@ -1591,7 +1631,8 @@
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint64_t)31570 << (int32_t)911;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(
End_to_End(sync.tagStr, "[aggregation time=31.57s,count=911]"));
}
@@ -1602,42 +1643,48 @@
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint32_t)512;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "current=512B"));
}
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint32_t)3072;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "current=3KB"));
}
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint32_t)2097152;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "current=2MB"));
}
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint32_t)2097153;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "current=2097153B"));
}
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint32_t)1073741824;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "current=1GB"));
}
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint32_t)3221225472; // 3MB, but on purpose overflowed
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, "current=-1GB"));
}
}
@@ -1645,9 +1692,52 @@
{
static const struct tag sync = { 27501, "notification_panel_hidden" };
android_log_event_list ctx(sync.tagNo);
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, ""));
}
+
+ {
+ // Invent new entries because existing can not serve
+ EventTagMap* map = android_openEventTagMap(nullptr);
+ ASSERT_TRUE(nullptr != map);
+ static const char name[] = ___STRING(logcat) ".descriptive-monotonic";
+ int myTag = android_lookupEventTagNum(map, name, "(new|1|s)",
+ ANDROID_LOG_UNKNOWN);
+ android_closeEventTagMap(map);
+ ASSERT_NE(-1, myTag);
+
+ const struct tag sync = { (uint32_t)myTag, name };
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)7;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "new=7s"));
+ }
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)62;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:02"));
+ }
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)3673;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:01:13"));
+ }
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)(86400 + 7200 + 180 + 58);
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "new=1d 2:03:58"));
+ }
+ }
}
static bool reportedSecurity(const char* command) {
@@ -1685,7 +1775,7 @@
TEST(logcat, help) {
size_t logcatHelpTextSize = commandOutputSize(logcat_executable " -h 2>&1");
- EXPECT_LT(4096UL, logcatHelpTextSize);
+ EXPECT_GT(logcatHelpTextSize, 4096UL);
size_t logcatLastHelpTextSize =
commandOutputSize(logcat_executable " -L -h 2>&1");
#ifdef USING_LOGCAT_EXECUTABLE_DEFAULT // logcat and liblogcat
diff --git a/logd/Android.bp b/logd/Android.bp
new file mode 100644
index 0000000..68b79d3
--- /dev/null
+++ b/logd/Android.bp
@@ -0,0 +1,78 @@
+// 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.
+
+// This is what we want to do:
+// event_logtags = $(shell
+// sed -n
+// "s/^\([0-9]*\)[ \t]*$1[ \t].*/-D`echo $1 | tr a-z A-Z`_LOG_TAG=\1/p"
+// $(LOCAL_PATH)/$2/event.logtags)
+// event_flag := $(call event_logtags,auditd)
+// event_flag += $(call event_logtags,logd)
+// event_flag += $(call event_logtags,tag_def)
+// so make sure we do not regret hard-coding it as follows:
+event_flag = [
+ "-DAUDITD_LOG_TAG=1003",
+ "-DCHATTY_LOG_TAG=1004",
+ "-DTAG_DEF_LOG_TAG=1005",
+ "-DLIBLOG_LOG_TAG=1006"
+]
+
+cc_library_static {
+ name: "liblogd",
+
+ srcs: [
+ "LogCommand.cpp",
+ "CommandListener.cpp",
+ "LogListener.cpp",
+ "LogReader.cpp",
+ "FlushCommand.cpp",
+ "LogBuffer.cpp",
+ "LogBufferElement.cpp",
+ "LogBufferInterface.cpp",
+ "LogTimes.cpp",
+ "LogStatistics.cpp",
+ "LogWhiteBlackList.cpp",
+ "libaudit.c",
+ "LogAudit.cpp",
+ "LogKlog.cpp",
+ "LogTags.cpp",
+ ],
+ logtags: ["event.logtags"],
+
+ shared_libs: ["libbase"],
+
+ export_include_dirs: ["."],
+
+ cflags: ["-Werror"] + event_flag,
+}
+
+cc_binary {
+ name: "logd",
+ init_rc: ["logd.rc"],
+
+ srcs: ["main.cpp"],
+
+ static_libs: ["liblogd"],
+
+ shared_libs: [
+ "libsysutils",
+ "liblog",
+ "libcutils",
+ "libbase",
+ "libpackagelistparser",
+ "libcap",
+ ],
+
+ cflags: ["-Werror"],
+}
diff --git a/logd/Android.mk b/logd/Android.mk
index 9211037..1bca891 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -2,54 +2,6 @@
include $(CLEAR_VARS)
-LOCAL_MODULE:= logd
-
-LOCAL_INIT_RC := logd.rc
-
-LOCAL_SRC_FILES := \
- main.cpp \
- LogCommand.cpp \
- CommandListener.cpp \
- LogListener.cpp \
- LogReader.cpp \
- FlushCommand.cpp \
- LogBuffer.cpp \
- LogBufferElement.cpp \
- LogTimes.cpp \
- LogStatistics.cpp \
- LogWhiteBlackList.cpp \
- libaudit.c \
- LogAudit.cpp \
- LogKlog.cpp \
- LogTags.cpp \
- event.logtags
-
-LOCAL_SHARED_LIBRARIES := \
- libsysutils \
- liblog \
- libcutils \
- libbase \
- libpackagelistparser \
- libcap
-
-# This is what we want to do:
-# event_logtags = $(shell \
-# sed -n \
-# "s/^\([0-9]*\)[ \t]*$1[ \t].*/-D`echo $1 | tr a-z A-Z`_LOG_TAG=\1/p" \
-# $(LOCAL_PATH)/$2/event.logtags)
-# event_flag := $(call event_logtags,auditd)
-# event_flag += $(call event_logtags,logd)
-# event_flag += $(call event_logtags,tag_def)
-# so make sure we do not regret hard-coding it as follows:
-event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004 -DTAG_DEF_LOG_TAG=1005
-event_flag += -DLIBLOG_LOG_TAG=1006
-
-LOCAL_CFLAGS := -Werror $(event_flag)
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
LOCAL_MODULE := logtagd.rc
LOCAL_SRC_FILES := $(LOCAL_MODULE)
LOCAL_MODULE_CLASS := ETC
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index c67d2bf..a9edc3e 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -44,14 +44,14 @@
// LogTimeEntrys, and spawn a transitory per-client thread to
// work at filing data to the socket.
//
-// global LogTimeEntry::lock() is used to protect access,
+// global LogTimeEntry::wrlock() is used to protect access,
// reference counts are used to ensure that individual
// LogTimeEntry lifetime is managed when not protected.
void FlushCommand::runSocketCommand(SocketClient* client) {
LogTimeEntry* entry = NULL;
LastLogTimes& times = mReader.logbuf().mTimes;
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LastLogTimes::iterator it = times.begin();
while (it != times.end()) {
entry = (*it);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 2d9024a..cfcbaa5 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -25,7 +25,11 @@
#include <sys/uio.h>
#include <syslog.h>
+#include <fstream>
+#include <sstream>
+
#include <android-base/macros.h>
+#include <log/log_properties.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
@@ -138,6 +142,71 @@
return true;
}
+static inline bool hasMetadata(char* str, int str_len) {
+ // need to check and see if str already contains bug metadata from
+ // possibility of stuttering if log audit crashes and then reloads kernel
+ // messages. Kernel denials that contain metadata will either end in
+ // "b/[0-9]+$" or "b/[0-9]+ duplicate messages suppressed$" which will put
+ // a '/' character at either 9 or 39 indices away from the end of the str.
+ return str_len >= 39 &&
+ (str[str_len - 9] == '/' || str[str_len - 39] == '/');
+}
+
+std::map<std::string, std::string> LogAudit::populateDenialMap() {
+ std::ifstream bug_file("/system/etc/selinux/selinux_denial_metadata");
+ std::string line;
+ // allocate a map for the static map pointer in logParse to keep track of,
+ // this function only runs once
+ std::map<std::string, std::string> denial_to_bug;
+ if (bug_file.good()) {
+ std::string scontext;
+ std::string tcontext;
+ std::string tclass;
+ std::string bug_num;
+ while (std::getline(bug_file, line)) {
+ std::stringstream split_line(line);
+ split_line >> scontext >> tcontext >> tclass >> bug_num;
+ denial_to_bug.emplace(scontext + tcontext + tclass, bug_num);
+ }
+ }
+ return denial_to_bug;
+}
+
+std::string LogAudit::denialParse(const std::string& denial, char terminator,
+ const std::string& search_term) {
+ size_t start_index = denial.find(search_term);
+ if (start_index != std::string::npos) {
+ start_index += search_term.length();
+ return denial.substr(
+ start_index, denial.find(terminator, start_index) - start_index);
+ }
+ return "";
+}
+
+void LogAudit::logParse(const std::string& string, std::string* bug_num) {
+ if (!__android_log_is_debuggable()) {
+ bug_num->assign("");
+ return;
+ }
+ static std::map<std::string, std::string> denial_to_bug =
+ populateDenialMap();
+ std::string scontext = denialParse(string, ':', "scontext=u:object_r:");
+ std::string tcontext = denialParse(string, ':', "tcontext=u:object_r:");
+ std::string tclass = denialParse(string, ' ', "tclass=");
+ if (scontext.empty()) {
+ scontext = denialParse(string, ':', "scontext=u:r:");
+ }
+ if (tcontext.empty()) {
+ tcontext = denialParse(string, ':', "tcontext=u:r:");
+ }
+ auto search = denial_to_bug.find(scontext + tcontext + tclass);
+ if (search != denial_to_bug.end()) {
+ bug_num->assign(" b/" + search->second);
+ } else {
+ bug_num->assign("");
+ }
+}
+
int LogAudit::logPrint(const char* fmt, ...) {
if (fmt == NULL) {
return -EINVAL;
@@ -153,7 +222,6 @@
if (rc < 0) {
return rc;
}
-
char* cp;
// Work around kernels missing
// https://github.com/torvalds/linux/commit/b8f89caafeb55fba75b74bea25adc4e4cd91be67
@@ -165,10 +233,10 @@
while ((cp = strstr(str, " "))) {
memmove(cp, cp + 1, strlen(cp + 1) + 1);
}
-
bool info = strstr(str, " permissive=1") || strstr(str, " policy loaded ");
+ static std::string bug_metadata;
if ((fdDmesg >= 0) && initialized) {
- struct iovec iov[3];
+ struct iovec iov[4];
static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
static const char newline[] = "\n";
@@ -197,19 +265,20 @@
}
if (!skip) {
static const char resume[] = " duplicate messages suppressed\n";
-
iov[0].iov_base = last_info ? const_cast<char*>(log_info)
: const_cast<char*>(log_warning);
iov[0].iov_len =
last_info ? sizeof(log_info) : sizeof(log_warning);
iov[1].iov_base = last_str;
iov[1].iov_len = strlen(last_str);
+ iov[2].iov_base = const_cast<char*>(bug_metadata.c_str());
+ iov[2].iov_len = bug_metadata.length();
if (count > 1) {
- iov[2].iov_base = const_cast<char*>(resume);
- iov[2].iov_len = strlen(resume);
+ iov[3].iov_base = const_cast<char*>(resume);
+ iov[3].iov_len = strlen(resume);
} else {
- iov[2].iov_base = const_cast<char*>(newline);
- iov[2].iov_len = strlen(newline);
+ iov[3].iov_base = const_cast<char*>(newline);
+ iov[3].iov_len = strlen(newline);
}
writev(fdDmesg, iov, arraysize(iov));
@@ -223,13 +292,16 @@
last_info = info;
}
if (count == 0) {
+ logParse(str, &bug_metadata);
iov[0].iov_base = info ? const_cast<char*>(log_info)
: const_cast<char*>(log_warning);
iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
iov[1].iov_base = str;
iov[1].iov_len = strlen(str);
- iov[2].iov_base = const_cast<char*>(newline);
- iov[2].iov_len = strlen(newline);
+ iov[2].iov_base = const_cast<char*>(bug_metadata.c_str());
+ iov[2].iov_len = bug_metadata.length();
+ iov[3].iov_base = const_cast<char*>(newline);
+ iov[3].iov_len = strlen(newline);
writev(fdDmesg, iov, arraysize(iov));
}
@@ -277,7 +349,7 @@
++cp;
}
tid = pid;
- logbuf->lock();
+ logbuf->wrlock();
uid = logbuf->pidToUid(pid);
logbuf->unlock();
memmove(pidptr, cp, strlen(cp) + 1);
@@ -285,24 +357,32 @@
// log to events
- size_t l = strnlen(str, LOGGER_ENTRY_MAX_PAYLOAD);
- size_t n = l + sizeof(android_log_event_string_t);
+ size_t str_len = strnlen(str, LOGGER_ENTRY_MAX_PAYLOAD);
+ if (((fdDmesg < 0) || !initialized) && !hasMetadata(str, str_len))
+ logParse(str, &bug_metadata);
+ str_len = (str_len + bug_metadata.length() <= LOGGER_ENTRY_MAX_PAYLOAD)
+ ? str_len + bug_metadata.length()
+ : LOGGER_ENTRY_MAX_PAYLOAD;
+ size_t message_len = str_len + sizeof(android_log_event_string_t);
bool notify = false;
if (events) { // begin scope for event buffer
- uint32_t buffer[(n + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
+ uint32_t buffer[(message_len + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
android_log_event_string_t* event =
reinterpret_cast<android_log_event_string_t*>(buffer);
event->header.tag = htole32(AUDITD_LOG_TAG);
event->type = EVENT_TYPE_STRING;
- event->length = htole32(l);
- memcpy(event->data, str, l);
+ event->length = htole32(message_len);
+ memcpy(event->data, str, str_len - bug_metadata.length());
+ memcpy(event->data + str_len - bug_metadata.length(),
+ bug_metadata.c_str(), bug_metadata.length());
- rc = logbuf->log(LOG_ID_EVENTS, now, uid, pid, tid,
- reinterpret_cast<char*>(event),
- (n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX);
+ rc = logbuf->log(
+ LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
+ (message_len <= USHRT_MAX) ? (unsigned short)message_len
+ : USHRT_MAX);
if (rc >= 0) {
notify = true;
}
@@ -322,7 +402,7 @@
pid = tid;
comm = "auditd";
} else {
- logbuf->lock();
+ logbuf->wrlock();
comm = commfree = logbuf->pidToName(pid);
logbuf->unlock();
if (!comm) {
@@ -333,28 +413,31 @@
const char* ecomm = strchr(comm, '"');
if (ecomm) {
++ecomm;
- l = ecomm - comm;
+ str_len = ecomm - comm;
} else {
- l = strlen(comm) + 1;
+ str_len = strlen(comm) + 1;
ecomm = "";
}
- size_t b = estr - str;
- if (b > LOGGER_ENTRY_MAX_PAYLOAD) {
- b = LOGGER_ENTRY_MAX_PAYLOAD;
+ size_t prefix_len = estr - str;
+ if (prefix_len > LOGGER_ENTRY_MAX_PAYLOAD) {
+ prefix_len = LOGGER_ENTRY_MAX_PAYLOAD;
}
- size_t e = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - b);
- n = b + e + l + 2;
+ size_t suffix_len = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - prefix_len);
+ message_len = str_len + prefix_len + suffix_len + bug_metadata.length() + 2;
if (main) { // begin scope for main buffer
- char newstr[n];
+ char newstr[message_len];
*newstr = info ? ANDROID_LOG_INFO : ANDROID_LOG_WARN;
- strlcpy(newstr + 1, comm, l);
- strncpy(newstr + 1 + l, str, b);
- strncpy(newstr + 1 + l + b, ecomm, e);
+ strlcpy(newstr + 1, comm, str_len);
+ strncpy(newstr + 1 + str_len, str, prefix_len);
+ strncpy(newstr + 1 + str_len + prefix_len, ecomm, suffix_len);
+ strncpy(newstr + 1 + str_len + prefix_len + suffix_len,
+ bug_metadata.c_str(), bug_metadata.length());
rc = logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
- (n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX);
+ (message_len <= USHRT_MAX) ? (unsigned short)message_len
+ : USHRT_MAX);
if (rc >= 0) {
notify = true;
@@ -368,7 +451,7 @@
if (notify) {
reader->notifyNewLog();
if (rc < 0) {
- rc = n;
+ rc = message_len;
}
}
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 5947819..2bd02d4 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -17,6 +17,7 @@
#ifndef _LOGD_LOG_AUDIT_H__
#define _LOGD_LOG_AUDIT_H__
+#include <map>
#include <queue>
#include <sysutils/SocketListener.h>
@@ -50,6 +51,10 @@
private:
static int getLogSocket();
+ std::map<std::string, std::string> populateDenialMap();
+ std::string denialParse(const std::string& denial, char terminator,
+ const std::string& search_term);
+ void logParse(const std::string& string, std::string* bug_num);
int logPrint(const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)));
};
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 4aa2c9f..7498325 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -72,22 +72,28 @@
// as the act of mounting /data would trigger persist.logd.timestamp to
// be corrected. 1/30 corner case YMMV.
//
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
LogBufferElementCollection::iterator it = mLogElements.begin();
while ((it != mLogElements.end())) {
LogBufferElement* e = *it;
if (monotonic) {
if (!android::isMonotonic(e->mRealTime)) {
LogKlog::convertRealToMonotonic(e->mRealTime);
+ if ((e->mRealTime.tv_nsec % 1000) == 0) {
+ e->mRealTime.tv_nsec++;
+ }
}
} else {
if (android::isMonotonic(e->mRealTime)) {
LogKlog::convertMonotonicToReal(e->mRealTime);
+ if ((e->mRealTime.tv_nsec % 1000) == 0) {
+ e->mRealTime.tv_nsec++;
+ }
}
}
++it;
}
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
}
// We may have been triggered by a SIGHUP. Release any sleeping reader
@@ -95,7 +101,7 @@
//
// NB: this is _not_ performed in the context of a SIGHUP, it is
// performed during startup, and in context of reinit administrative thread
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
@@ -111,7 +117,7 @@
LogBuffer::LogBuffer(LastLogTimes* times)
: monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
- pthread_mutex_init(&mLogElementsLock, nullptr);
+ pthread_rwlock_init(&mLogElementsLock, nullptr);
log_id_for_each(i) {
lastLoggedElements[i] = nullptr;
@@ -196,6 +202,11 @@
return -EINVAL;
}
+ // Slip the time by 1 nsec if the incoming lands on xxxxxx000 ns.
+ // This prevents any chance that an outside source can request an
+ // exact entry with time specified in ms or us precision.
+ if ((realtime.tv_nsec % 1000) == 0) ++realtime.tv_nsec;
+
LogBufferElement* elem =
new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
if (log_id != LOG_ID_SECURITY) {
@@ -209,16 +220,15 @@
}
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
// Log traffic received to total
- pthread_mutex_lock(&mLogElementsLock);
- stats.add(elem);
- stats.subtract(elem);
- pthread_mutex_unlock(&mLogElementsLock);
+ wrlock();
+ stats.addTotal(elem);
+ unlock();
delete elem;
return -EACCES;
}
}
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
LogBufferElement* currentLast = lastLoggedElements[log_id];
if (currentLast) {
LogBufferElement* dropped = droppedElements[log_id];
@@ -319,15 +329,14 @@
// check for overflow
if (total >= UINT32_MAX) {
log(currentLast);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return len;
}
- stats.add(currentLast);
- stats.subtract(currentLast);
+ stats.addTotal(currentLast);
delete currentLast;
swab = total;
event->payload.data = htole32(swab);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return len;
}
if (count == USHRT_MAX) {
@@ -339,13 +348,12 @@
}
}
if (count) {
- stats.add(currentLast);
- stats.subtract(currentLast);
+ stats.addTotal(currentLast);
currentLast->setDropped(count);
}
droppedElements[log_id] = currentLast;
lastLoggedElements[log_id] = elem;
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return len;
}
if (dropped) { // State 1 or 2
@@ -363,12 +371,12 @@
lastLoggedElements[log_id] = new LogBufferElement(*elem);
log(elem);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return len;
}
-// assumes mLogElementsLock held, owns elem, will look after garbage collection
+// assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
void LogBuffer::log(LogBufferElement* elem) {
// cap on how far back we will sort in-place, otherwise append
static uint32_t too_far_back = 5; // five seconds
@@ -389,7 +397,7 @@
bool end_set = false;
bool end_always = false;
- LogTimeEntry::lock();
+ LogTimeEntry::rdlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
@@ -431,7 +439,7 @@
// Prune at most 10% of the log entries or maxPrune, whichever is less.
//
-// mLogElementsLock must be held when this function is called.
+// LogBuffer::wrlock() must be held when this function is called.
void LogBuffer::maybePrune(log_id_t id) {
size_t sizes = stats.sizes(id);
unsigned long maxSize = log_buffer_size(id);
@@ -610,6 +618,33 @@
}
};
+// Determine if watermark is within pruneMargin + 1s from the end of the list,
+// the caller will use this result to set an internal busy flag indicating
+// the prune operation could not be completed because a reader is blocking
+// the request.
+bool LogBuffer::isBusy(log_time watermark) {
+ LogBufferElementCollection::iterator ei = mLogElements.end();
+ --ei;
+ return watermark < ((*ei)->getRealTime() - pruneMargin - log_time(1, 0));
+}
+
+// If the selected reader is blocking our pruning progress, decide on
+// what kind of mitigation is necessary to unblock the situation.
+void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
+ if (stats.sizes(id) > (2 * log_buffer_size(id))) { // +100%
+ // A misbehaving or slow reader has its connection
+ // dropped if we hit too much memory pressure.
+ me->release_Locked();
+ } else if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
+ // Allow a blocked WRAP timeout reader to
+ // trigger and start reporting the log data.
+ me->triggerReader_Locked();
+ } else {
+ // tell slow reader to skip entries to catch up
+ me->triggerSkip_Locked(id, pruneRows);
+ }
+}
+
// prune "pruneRows" of type "id" from the buffer.
//
// This garbage collection task is used to expire log entries. It is called to
@@ -655,14 +690,14 @@
// The third thread is optional, and only gets hit if there was a whitelist
// and more needs to be pruned against the backstop of the region lock.
//
-// mLogElementsLock must be held when this function is called.
+// LogBuffer::wrlock() must be held when this function is called.
//
bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
LogTimeEntry* oldest = nullptr;
bool busy = false;
bool clearAll = pruneRows == ULONG_MAX;
- LogTimeEntry::lock();
+ LogTimeEntry::rdlock();
// Region locked?
LastLogTimes::iterator times = mTimes.begin();
@@ -700,12 +735,8 @@
}
if (oldest && (watermark <= element->getRealTime())) {
- busy = true;
- if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
- oldest->triggerReader_Locked();
- } else {
- oldest->triggerSkip_Locked(id, pruneRows);
- }
+ busy = isBusy(watermark);
+ if (busy) kickMe(oldest, id, pruneRows);
break;
}
@@ -792,10 +823,8 @@
LogBufferElement* element = *it;
if (oldest && (watermark <= element->getRealTime())) {
- busy = true;
- if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
- oldest->triggerReader_Locked();
- }
+ busy = isBusy(watermark);
+ // Do not let chatty eliding trigger any reader mitigation
break;
}
@@ -946,19 +975,8 @@
}
if (oldest && (watermark <= element->getRealTime())) {
- busy = true;
- if (whitelist) {
- break;
- }
-
- if (stats.sizes(id) > (2 * log_buffer_size(id))) {
- // kick a misbehaving log reader client off the island
- oldest->release_Locked();
- } else if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
- oldest->triggerReader_Locked();
- } else {
- oldest->triggerSkip_Locked(id, pruneRows);
- }
+ busy = isBusy(watermark);
+ if (!whitelist && busy) kickMe(oldest, id, pruneRows);
break;
}
@@ -990,15 +1008,8 @@
}
if (oldest && (watermark <= element->getRealTime())) {
- busy = true;
- if (stats.sizes(id) > (2 * log_buffer_size(id))) {
- // kick a misbehaving log reader client off the island
- oldest->release_Locked();
- } else if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
- oldest->triggerReader_Locked();
- } else {
- oldest->triggerSkip_Locked(id, pruneRows);
- }
+ busy = isBusy(watermark);
+ if (busy) kickMe(oldest, id, pruneRows);
break;
}
@@ -1022,15 +1033,15 @@
// one entry, not another clear run, so we are looking for
// the quick side effect of the return value to tell us if
// we have a _blocked_ reader.
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
busy = prune(id, 1, uid);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
// It is still busy, blocked reader(s), lets kill them all!
// otherwise, lets be a good citizen and preserve the slow
// readers and let the clear run (below) deal with determining
// if we are still blocked and return an error code to caller.
if (busy) {
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
LogTimeEntry* entry = (*times);
@@ -1043,9 +1054,9 @@
LogTimeEntry::unlock();
}
}
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
busy = prune(id, ULONG_MAX, uid);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
if (!busy || !--retry) {
break;
}
@@ -1056,9 +1067,9 @@
// get the used space associated with "id".
unsigned long LogBuffer::getSizeUsed(log_id_t id) {
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
size_t retval = stats.sizes(id);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return retval;
}
@@ -1068,17 +1079,17 @@
if (!__android_logger_valid_buffer_size(size)) {
return -1;
}
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
log_buffer_size(id) = size;
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return 0;
}
// get the total space allocated to "id"
unsigned long LogBuffer::getSize(log_id_t id) {
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
size_t retval = log_buffer_size(id);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return retval;
}
@@ -1090,7 +1101,7 @@
LogBufferElementCollection::iterator it;
uid_t uid = reader->getUid();
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
if (start == log_time::EPOCH) {
// client wants to start from the beginning
@@ -1111,6 +1122,9 @@
LogBufferElement* element = *it;
if (element->getRealTime() > start) {
last = it;
+ } else if (element->getRealTime() == start) {
+ last = ++it;
+ break;
} else if (!--count || (element->getRealTime() < min)) {
break;
}
@@ -1118,7 +1132,7 @@
it = last;
}
- log_time max = start;
+ log_time curr = start;
LogBufferElement* lastElement = nullptr; // iterator corruption paranoia
static const size_t maxSkip = 4194304; // maximum entries to skip
@@ -1144,11 +1158,7 @@
continue;
}
- if (element->getRealTime() <= start) {
- continue;
- }
-
- // NB: calling out to another object with mLogElementsLock held (safe)
+ // NB: calling out to another object with wrlock() held (safe)
if (filter) {
int ret = (*filter)(element, arg);
if (ret == false) {
@@ -1171,30 +1181,30 @@
(element->getDropped() && !sameTid) ? 0 : element->getTid();
}
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
// range locking in LastLogTimes looks after us
- max = element->flushTo(reader, this, privileged, sameTid);
+ curr = element->flushTo(reader, this, privileged, sameTid);
- if (max == element->FLUSH_ERROR) {
- return max;
+ if (curr == element->FLUSH_ERROR) {
+ return curr;
}
skip = maxSkip;
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
}
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
- return max;
+ return curr;
}
std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
unsigned int logMask) {
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
std::string ret = stats.format(uid, pid, logMask);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return ret;
}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 1272c20..0942987 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -27,6 +27,7 @@
#include <sysutils/SocketClient.h>
#include "LogBufferElement.h"
+#include "LogBufferInterface.h"
#include "LogStatistics.h"
#include "LogTags.h"
#include "LogTimes.h"
@@ -74,9 +75,9 @@
typedef std::list<LogBufferElement*> LogBufferElementCollection;
-class LogBuffer {
+class LogBuffer : public LogBufferInterface {
LogBufferElementCollection mLogElements;
- pthread_mutex_t mLogElementsLock;
+ pthread_rwlock_t mLogElementsLock;
LogStatistics stats;
@@ -107,14 +108,14 @@
LastLogTimes& mTimes;
explicit LogBuffer(LastLogTimes* times);
- ~LogBuffer();
+ ~LogBuffer() override;
void init();
bool isMonotonic() {
return monotonic;
}
int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
- const char* msg, unsigned short len);
+ const char* msg, unsigned short len) override;
// lastTid is an optional context to help detect if the last previous
// valid message was from the same source so we can differentiate chatty
// filter types (identical or expired)
@@ -154,21 +155,27 @@
return tags.tagToName(tag);
}
- // helper must be protected directly or implicitly by lock()/unlock()
+ // helper must be protected directly or implicitly by wrlock()/unlock()
const char* pidToName(pid_t pid) {
return stats.pidToName(pid);
}
- uid_t pidToUid(pid_t pid) {
+ virtual uid_t pidToUid(pid_t pid) override {
return stats.pidToUid(pid);
}
+ virtual pid_t tidToPid(pid_t tid) override {
+ return stats.tidToPid(tid);
+ }
const char* uidToName(uid_t uid) {
return stats.uidToName(uid);
}
- void lock() {
- pthread_mutex_lock(&mLogElementsLock);
+ void wrlock() {
+ pthread_rwlock_wrlock(&mLogElementsLock);
+ }
+ void rdlock() {
+ pthread_rwlock_rdlock(&mLogElementsLock);
}
void unlock() {
- pthread_mutex_unlock(&mLogElementsLock);
+ pthread_rwlock_unlock(&mLogElementsLock);
}
private:
@@ -177,6 +184,9 @@
static const log_time pruneMargin;
void maybePrune(log_id_t id);
+ bool isBusy(log_time watermark);
+ void kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows);
+
bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
LogBufferElementCollection::iterator erase(
LogBufferElementCollection::iterator it, bool coalesce = false);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 04a620c..f20ac45 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -41,22 +41,20 @@
mTid(tid),
mRealTime(realtime),
mMsgLen(len),
- mLogId(log_id) {
+ mLogId(log_id),
+ mDropped(false) {
mMsg = new char[len];
memcpy(mMsg, msg, len);
- mTag = (isBinary() && (mMsgLen >= sizeof(uint32_t)))
- ? le32toh(reinterpret_cast<android_event_header_t*>(mMsg)->tag)
- : 0;
}
LogBufferElement::LogBufferElement(const LogBufferElement& elem)
- : mTag(elem.mTag),
- mUid(elem.mUid),
+ : mUid(elem.mUid),
mPid(elem.mPid),
mTid(elem.mTid),
mRealTime(elem.mRealTime),
mMsgLen(elem.mMsgLen),
- mLogId(elem.mLogId) {
+ mLogId(elem.mLogId),
+ mDropped(elem.mDropped) {
mMsg = new char[mMsgLen];
memcpy(mMsg, elem.mMsg, mMsgLen);
}
@@ -65,6 +63,32 @@
delete[] mMsg;
}
+uint32_t LogBufferElement::getTag() const {
+ return (isBinary() &&
+ ((mDropped && mMsg != nullptr) ||
+ (!mDropped && mMsgLen >= sizeof(android_event_header_t))))
+ ? reinterpret_cast<const android_event_header_t*>(mMsg)->tag
+ : 0;
+}
+
+unsigned short LogBufferElement::setDropped(unsigned short value) {
+ // The tag information is saved in mMsg data, if the tag is non-zero
+ // save only the information needed to get the tag.
+ if (getTag() != 0) {
+ if (mMsgLen > sizeof(android_event_header_t)) {
+ char* truncated_msg = new char[sizeof(android_event_header_t)];
+ memcpy(truncated_msg, mMsg, sizeof(android_event_header_t));
+ delete[] mMsg;
+ mMsg = truncated_msg;
+ } // mMsgLen == sizeof(android_event_header_t), already at minimum.
+ } else {
+ delete[] mMsg;
+ mMsg = nullptr;
+ }
+ mDropped = true;
+ return mDroppedCount = value;
+}
+
// caller must own and free character string
char* android::tidToName(pid_t tid) {
char* retval = NULL;
@@ -121,7 +145,7 @@
}
static const char format_uid[] = "uid=%u%s%s %s %u line%s";
- parent->lock();
+ parent->wrlock();
const char* name = parent->uidToName(mUid);
parent->unlock();
const char* commName = android::tidToName(mTid);
@@ -129,7 +153,7 @@
commName = android::tidToName(mPid);
}
if (!commName) {
- parent->lock();
+ parent->wrlock();
commName = parent->pidToName(mPid);
parent->unlock();
}
@@ -164,8 +188,8 @@
// identical to below to calculate the buffer size required
const char* type = lastSame ? "identical" : "expire";
size_t len = snprintf(NULL, 0, format_uid, mUid, name ? name : "",
- commName ? commName : "", type, mDropped,
- (mDropped > 1) ? "s" : "");
+ commName ? commName : "", type, getDropped(),
+ (getDropped() > 1) ? "s" : "");
size_t hdrLen;
if (isBinary()) {
@@ -196,8 +220,8 @@
}
snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
- commName ? commName : "", type, mDropped,
- (mDropped > 1) ? "s" : "");
+ commName ? commName : "", type, getDropped(),
+ (getDropped() > 1) ? "s" : "");
free(const_cast<char*>(name));
free(const_cast<char*>(commName));
@@ -225,7 +249,7 @@
char* buffer = NULL;
- if (!mMsg) {
+ if (mDropped) {
entry.len = populateDroppedMessage(buffer, parent, lastSame);
if (!entry.len) return mRealTime;
iovec[1].iov_base = buffer;
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 814ec87..b168645 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -32,25 +32,25 @@
// chatty for the temporal expire messages
#define EXPIRE_RATELIMIT 10 // maximum rate in seconds to report expiration
-class LogBufferElement {
+class __attribute__((packed)) LogBufferElement {
friend LogBuffer;
// sized to match reality of incoming log packets
- uint32_t mTag; // only valid for isBinary()
const uint32_t mUid;
const uint32_t mPid;
const uint32_t mTid;
log_time mRealTime;
char* mMsg;
union {
- const uint16_t mMsgLen; // mMSg != NULL
- uint16_t mDropped; // mMsg == NULL
+ const uint16_t mMsgLen; // mDropped == false
+ uint16_t mDroppedCount; // mDropped == true
};
const uint8_t mLogId;
+ bool mDropped;
static atomic_int_fast64_t sequence;
- // assumption: mMsg == NULL
+ // assumption: mDropped == true
size_t populateDroppedMessage(char*& buffer, LogBuffer* parent,
bool lastSame);
@@ -58,7 +58,7 @@
LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
pid_t tid, const char* msg, unsigned short len);
LogBufferElement(const LogBufferElement& elem);
- virtual ~LogBufferElement();
+ ~LogBufferElement();
bool isBinary(void) const {
return (mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY);
@@ -76,24 +76,16 @@
pid_t getTid(void) const {
return mTid;
}
- uint32_t getTag() const {
- return mTag;
- }
+ uint32_t getTag() const;
unsigned short getDropped(void) const {
- return mMsg ? 0 : mDropped;
+ return mDropped ? mDroppedCount : 0;
}
- unsigned short setDropped(unsigned short value) {
- if (mMsg) {
- delete[] mMsg;
- mMsg = NULL;
- }
- return mDropped = value;
- }
+ unsigned short setDropped(unsigned short value);
unsigned short getMsgLen() const {
- return mMsg ? mMsgLen : 0;
+ return mDropped ? 0 : mMsgLen;
}
const char* getMsg() const {
- return mMsg;
+ return mDropped ? nullptr : mMsg;
}
log_time getRealTime(void) const {
return mRealTime;
diff --git a/libunwindstack/Log.h b/logd/LogBufferInterface.cpp
similarity index 60%
copy from libunwindstack/Log.h
copy to logd/LogBufferInterface.cpp
index 2d01aa8..4b6d363 100644
--- a/libunwindstack/Log.h
+++ b/logd/LogBufferInterface.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,12 +14,16 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
+#include "LogBufferInterface.h"
+#include "LogUtils.h"
-#include <stdint.h>
-
-void log_to_stdout(bool enable);
-void log(uint8_t indent, const char* format, ...);
-
-#endif // _LIBUNWINDSTACK_LOG_H
+LogBufferInterface::LogBufferInterface() {
+}
+LogBufferInterface::~LogBufferInterface() {
+}
+uid_t LogBufferInterface::pidToUid(pid_t pid) {
+ return android::pidToUid(pid);
+}
+pid_t LogBufferInterface::tidToPid(pid_t tid) {
+ return android::tidToPid(tid);
+}
diff --git a/logd/LogBufferInterface.h b/logd/LogBufferInterface.h
new file mode 100644
index 0000000..ff73a22
--- /dev/null
+++ b/logd/LogBufferInterface.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012-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.
+ */
+
+#ifndef _LOGD_LOG_BUFFER_INTERFACE_H__
+#define _LOGD_LOG_BUFFER_INTERFACE_H__
+
+#include <sys/types.h>
+
+#include <android-base/macros.h>
+#include <log/log_id.h>
+#include <log/log_time.h>
+
+// Abstract interface that handles log when log available.
+class LogBufferInterface {
+ public:
+ LogBufferInterface();
+ virtual ~LogBufferInterface();
+ // Handles a log entry when available in LogListener.
+ // Returns the size of the handled log message.
+ virtual int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
+ pid_t tid, const char* msg, unsigned short len) = 0;
+
+ virtual uid_t pidToUid(pid_t pid);
+ virtual pid_t tidToPid(pid_t tid);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LogBufferInterface);
+};
+
+#endif // _LOGD_LOG_BUFFER_INTERFACE_H__
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index d23254d..a7e7208 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -579,7 +579,7 @@
const pid_t tid = pid;
uid_t uid = AID_ROOT;
if (pid) {
- logbuf->lock();
+ logbuf->wrlock();
uid = logbuf->pidToUid(pid);
logbuf->unlock();
}
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index dadc75f..d2df68e 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+#include <ctype.h>
#include <limits.h>
+#include <stdio.h>
#include <sys/cdefs.h>
#include <sys/prctl.h>
#include <sys/socket.h>
@@ -30,7 +32,7 @@
#include "LogListener.h"
#include "LogUtils.h"
-LogListener::LogListener(LogBuffer* buf, LogReader* reader)
+LogListener::LogListener(LogBufferInterface* buf, LogReader* reader)
: SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {
}
@@ -72,8 +74,11 @@
cmsg = CMSG_NXTHDR(&hdr, cmsg);
}
+ struct ucred fake_cred;
if (cred == NULL) {
- return false;
+ cred = &fake_cred;
+ cred->pid = 0;
+ cred->uid = DEFAULT_OVERFLOWUID;
}
if (cred->uid == AID_LOGD) {
@@ -96,17 +101,41 @@
return false;
}
+ // Check credential validity, acquire corrected details if not supplied.
+ if (cred->pid == 0) {
+ cred->pid = logbuf ? logbuf->tidToPid(header->tid)
+ : android::tidToPid(header->tid);
+ if (cred->pid == getpid()) {
+ // We expect that /proc/<tid>/ is accessible to self even without
+ // readproc group, so that we will always drop messages that come
+ // from any of our logd threads and their library calls.
+ return false; // ignore self
+ }
+ }
+ if (cred->uid == DEFAULT_OVERFLOWUID) {
+ uid_t uid =
+ logbuf ? logbuf->pidToUid(cred->pid) : android::pidToUid(cred->pid);
+ if (uid == AID_LOGD) {
+ uid = logbuf ? logbuf->pidToUid(header->tid)
+ : android::pidToUid(cred->pid);
+ }
+ if (uid != AID_LOGD) cred->uid = uid;
+ }
+
char* msg = ((char*)buffer) + sizeof(android_log_header_t);
n -= sizeof(android_log_header_t);
// NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
// truncated message to the logs.
- if (logbuf->log((log_id_t)header->id, header->realtime, cred->uid,
- cred->pid, header->tid, msg,
- ((size_t)n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX) >=
- 0) {
- reader->notifyNewLog();
+ if (logbuf != nullptr) {
+ int res = logbuf->log(
+ (log_id_t)header->id, header->realtime, cred->uid, cred->pid,
+ header->tid, msg,
+ ((size_t)n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX);
+ if (res > 0 && reader != nullptr) {
+ reader->notifyNewLog();
+ }
}
return true;
@@ -116,14 +145,14 @@
static const char socketName[] = "logdw";
int sock = android_get_control_socket(socketName);
- if (sock < 0) {
+ if (sock < 0) { // logd started up in init.sh
sock = socket_local_server(
socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM);
- }
- int on = 1;
- if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
- return -1;
+ int on = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
+ return -1;
+ }
}
return sock;
}
diff --git a/logd/LogListener.h b/logd/LogListener.h
index 2973b8b..a562a54 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -20,12 +20,22 @@
#include <sysutils/SocketListener.h>
#include "LogReader.h"
+// DEFAULT_OVERFLOWUID is defined in linux/highuid.h, which is not part of
+// the uapi headers for userspace to use. This value is filled in on the
+// out-of-band socket credentials if the OS fails to find one available.
+// One of the causes of this is if SO_PASSCRED is set, all the packets before
+// that point will have this value. We also use it in a fake credential if
+// no socket credentials are supplied.
+#ifndef DEFAULT_OVERFLOWUID
+#define DEFAULT_OVERFLOWUID 65534
+#endif
+
class LogListener : public SocketListener {
- LogBuffer* logbuf;
+ LogBufferInterface* logbuf;
LogReader* reader;
public:
- LogListener(LogBuffer* buf, LogReader* reader);
+ LogListener(LogBufferInterface* buf, LogReader* reader /* nullable */);
protected:
virtual bool onDataAvailable(SocketClient* cli);
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index af19279..6d69316 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -15,6 +15,7 @@
*/
#include <ctype.h>
+#include <inttypes.h>
#include <poll.h>
#include <sys/prctl.h>
#include <sys/socket.h>
@@ -110,7 +111,7 @@
if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
// Allow writer to get some cycles, and wait for pending notifications
sched_yield();
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LogTimeEntry::unlock();
sched_yield();
nonBlock = true;
@@ -192,6 +193,12 @@
}
}
+ android::prdebug(
+ "logdr: UID=%d GID=%d PID=%d %c tail=%lu logMask=%x pid=%d "
+ "start=%" PRIu64 "ns timeout=%" PRIu64 "ns\n",
+ cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail,
+ logMask, (int)pid, sequence.nsec(), timeout);
+
FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
// Set acceptable upper limit to wait for slow reader processing b/27242723
@@ -205,7 +212,7 @@
void LogReader::doSocketDelete(SocketClient* cli) {
LastLogTimes& times = mLogbuf.mTimes;
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LastLogTimes::iterator it = times.begin();
while (it != times.end()) {
LogTimeEntry* entry = (*it);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index cc30f77..af59ddc 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+#include <ctype.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
@@ -23,19 +25,26 @@
#include <list>
-#include <android/log.h>
+#include <private/android_logger.h>
#include "LogStatistics.h"
+static const uint64_t hourSec = 60 * 60;
+static const uint64_t monthSec = 31 * 24 * hourSec;
+
size_t LogStatistics::SizesTotal;
LogStatistics::LogStatistics() : enable(false) {
+ log_time now(CLOCK_REALTIME);
log_id_for_each(id) {
mSizes[id] = 0;
mElements[id] = 0;
mDroppedElements[id] = 0;
mSizesTotal[id] = 0;
mElementsTotal[id] = 0;
+ mOldest[id] = now;
+ mNewest[id] = now;
+ mNewestDropped[id] = now;
}
}
@@ -70,25 +79,56 @@
}
}
+void LogStatistics::addTotal(LogBufferElement* element) {
+ if (element->getDropped()) return;
+
+ log_id_t log_id = element->getLogId();
+ unsigned short size = element->getMsgLen();
+ mSizesTotal[log_id] += size;
+ SizesTotal += size;
+ ++mElementsTotal[log_id];
+}
+
void LogStatistics::add(LogBufferElement* element) {
log_id_t log_id = element->getLogId();
unsigned short size = element->getMsgLen();
mSizes[log_id] += size;
++mElements[log_id];
+ // When caller adding a chatty entry, they will have already
+ // called add() and subtract() for each entry as they are
+ // evaluated and trimmed, thus recording size and number of
+ // elements, but we must recognize the manufactured dropped
+ // entry as not contributing to the lifetime totals.
if (element->getDropped()) {
++mDroppedElements[log_id];
} else {
- // When caller adding a chatty entry, they will have already
- // called add() and subtract() for each entry as they are
- // evaluated and trimmed, thus recording size and number of
- // elements, but we must recognize the manufactured dropped
- // entry as not contributing to the lifetime totals.
mSizesTotal[log_id] += size;
SizesTotal += size;
++mElementsTotal[log_id];
}
+ log_time stamp(element->getRealTime());
+ if (mNewest[log_id] < stamp) {
+ // A major time update invalidates the statistics :-(
+ log_time diff = stamp - mNewest[log_id];
+ mNewest[log_id] = stamp;
+
+ if (diff.tv_sec > hourSec) {
+ // approximate Do-Your-Best fixup
+ diff += mOldest[log_id];
+ if ((diff > stamp) && ((diff - stamp).tv_sec < hourSec)) {
+ diff = stamp;
+ }
+ if (diff <= stamp) {
+ mOldest[log_id] = diff;
+ if (mNewestDropped[log_id] < diff) {
+ mNewestDropped[log_id] = diff;
+ }
+ }
+ }
+ }
+
if (log_id == LOG_ID_KERNEL) {
return;
}
@@ -113,6 +153,10 @@
tagTable.add(tag, element);
}
}
+
+ if (!element->getDropped()) {
+ tagNameTable.add(TagNameKey(element), element);
+ }
}
void LogStatistics::subtract(LogBufferElement* element) {
@@ -124,6 +168,10 @@
--mDroppedElements[log_id];
}
+ if (mOldest[log_id] < element->getRealTime()) {
+ mOldest[log_id] = element->getRealTime();
+ }
+
if (log_id == LOG_ID_KERNEL) {
return;
}
@@ -148,6 +196,10 @@
tagTable.subtract(tag, element);
}
}
+
+ if (!element->getDropped()) {
+ tagNameTable.subtract(TagNameKey(element), element);
+ }
}
// Atomically set an entry to drop
@@ -158,6 +210,10 @@
mSizes[log_id] -= size;
++mDroppedElements[log_id];
+ if (mNewestDropped[log_id] < element->getRealTime()) {
+ mNewestDropped[log_id] = element->getRealTime();
+ }
+
uidTable[log_id].drop(element->getUid(), element);
if (element->getUid() == AID_SYSTEM) {
pidSystemTable[log_id].drop(element->getPid(), element);
@@ -178,9 +234,12 @@
tagTable.drop(tag, element);
}
}
+
+ tagNameTable.subtract(TagNameKey(element), element);
}
// caller must own and free character string
+// Requires parent LogBuffer::wrlock() to be held
const char* LogStatistics::uidToName(uid_t uid) const {
// Local hard coded favourites
if (uid == AID_LOGD) {
@@ -248,18 +307,38 @@
std::string(isprune ? "NUM" : ""));
}
+// Helper to truncate name, if too long, and add name dressings
+static void formatTmp(const LogStatistics& stat, const char* nameTmp, uid_t uid,
+ std::string& name, std::string& size, size_t nameLen) {
+ const char* allocNameTmp = nullptr;
+ if (!nameTmp) nameTmp = allocNameTmp = stat.uidToName(uid);
+ if (nameTmp) {
+ size_t lenSpace = std::max(nameLen - name.length(), (size_t)1);
+ size_t len = EntryBaseConstants::total_len -
+ EntryBaseConstants::pruned_len - size.length() -
+ name.length() - lenSpace - 2;
+ size_t lenNameTmp = strlen(nameTmp);
+ while ((len < lenNameTmp) && (lenSpace > 1)) {
+ ++len;
+ --lenSpace;
+ }
+ name += android::base::StringPrintf("%*s", (int)lenSpace, "");
+ if (len < lenNameTmp) {
+ name += "...";
+ nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
+ }
+ name += nameTmp;
+ free(const_cast<char*>(allocNameTmp));
+ }
+}
+
std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
uid_t uid = getUid();
std::string name = android::base::StringPrintf("%u", uid);
- const char* nameTmp = stat.uidToName(uid);
- if (nameTmp) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", nameTmp);
- free(const_cast<char*>(nameTmp));
- }
-
std::string size = android::base::StringPrintf("%zu", getSizes());
+ formatTmp(stat, nullptr, uid, name, size, 6);
+
std::string pruned = "";
if (worstUidEnabledForLogid(id)) {
size_t totalDropped = 0;
@@ -366,18 +445,10 @@
uid_t uid = getUid();
pid_t pid = getPid();
std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
- const char* nameTmp = getName();
- if (nameTmp) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
- } else if ((nameTmp = stat.uidToName(uid))) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
- free(const_cast<char*>(nameTmp));
- }
-
std::string size = android::base::StringPrintf("%zu", getSizes());
+ formatTmp(stat, getName(), uid, name, size, 12);
+
std::string pruned = "";
size_t dropped = getDropped();
if (dropped) {
@@ -398,21 +469,10 @@
log_id_t /* id */) const {
uid_t uid = getUid();
std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid);
- const char* nameTmp = getName();
- if (nameTmp) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
- } else if ((nameTmp = stat.uidToName(uid))) {
- // if we do not have a PID name, lets punt to try UID name?
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
- free(const_cast<char*>(nameTmp));
- // We tried, better to not have a name at all, we still
- // have TID/UID by number to report in any case.
- }
-
std::string size = android::base::StringPrintf("%zu", getSizes());
+ formatTmp(stat, getName(), uid, name, size, 12);
+
std::string pruned = "";
size_t dropped = getDropped();
if (dropped) {
@@ -456,6 +516,101 @@
return formatLine(name, size, pruned);
}
+std::string TagNameEntry::formatHeader(const std::string& name,
+ log_id_t /* id */) const {
+ return formatLine(name, std::string("Size"), std::string("")) +
+ formatLine(std::string(" TID/PID/UID LOG_TAG NAME"),
+ std::string("BYTES"), std::string(""));
+}
+
+std::string TagNameEntry::format(const LogStatistics& /* stat */,
+ log_id_t /* id */) const {
+ std::string name;
+ pid_t tid = getTid();
+ pid_t pid = getPid();
+ std::string pidstr;
+ if (pid != (pid_t)-1) {
+ pidstr = android::base::StringPrintf("%u", pid);
+ if ((tid != (pid_t)-1) && (tid != pid)) pidstr = "/" + pidstr;
+ }
+ int len = 9 - pidstr.length();
+ if (len < 0) len = 0;
+ if ((tid == (pid_t)-1) || (tid == pid)) {
+ name = android::base::StringPrintf("%*s", len, "");
+ } else {
+ name = android::base::StringPrintf("%*u", len, tid);
+ }
+ name += pidstr;
+ uid_t uid = getUid();
+ if (uid != (uid_t)-1) {
+ name += android::base::StringPrintf("/%u", uid);
+ }
+
+ std::string size = android::base::StringPrintf("%zu", getSizes());
+
+ const char* nameTmp = getName();
+ if (nameTmp) {
+ size_t lenSpace = std::max(16 - name.length(), (size_t)1);
+ size_t len = EntryBaseConstants::total_len -
+ EntryBaseConstants::pruned_len - size.length() -
+ name.length() - lenSpace - 2;
+ size_t lenNameTmp = strlen(nameTmp);
+ while ((len < lenNameTmp) && (lenSpace > 1)) {
+ ++len;
+ --lenSpace;
+ }
+ name += android::base::StringPrintf("%*s", (int)lenSpace, "");
+ if (len < lenNameTmp) {
+ name += "...";
+ nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
+ }
+ name += nameTmp;
+ }
+
+ std::string pruned = "";
+
+ return formatLine(name, size, pruned);
+}
+
+static std::string formatMsec(uint64_t val) {
+ static const unsigned subsecDigits = 3;
+ static const uint64_t sec = MS_PER_SEC;
+
+ static const uint64_t minute = 60 * sec;
+ static const uint64_t hour = 60 * minute;
+ static const uint64_t day = 24 * hour;
+
+ std::string output;
+ if (val < sec) return output;
+
+ if (val >= day) {
+ output = android::base::StringPrintf("%" PRIu64 "d ", val / day);
+ val = (val % day) + day;
+ }
+ if (val >= minute) {
+ if (val >= hour) {
+ output += android::base::StringPrintf("%" PRIu64 ":",
+ (val / hour) % (day / hour));
+ }
+ output += android::base::StringPrintf(
+ (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+ (val / minute) % (hour / minute));
+ }
+ output +=
+ android::base::StringPrintf((val >= minute) ? "%02" PRIu64 : "%" PRIu64,
+ (val / sec) % (minute / sec));
+ val %= sec;
+ unsigned digits = subsecDigits;
+ while (digits && ((val % 10) == 0)) {
+ val /= 10;
+ --digits;
+ }
+ if (digits) {
+ output += android::base::StringPrintf(".%0*" PRIu64, digits, val);
+ }
+ return output;
+}
+
std::string LogStatistics::format(uid_t uid, pid_t pid,
unsigned int logMask) const {
static const unsigned short spaces_total = 19;
@@ -525,6 +680,67 @@
output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
totalEls);
+ static const char SpanStr[] = "\nLogspan";
+ spaces = 10 - strlen(SpanStr);
+ output += SpanStr;
+
+ // Total reports the greater of the individual maximum time span, or the
+ // validated minimum start and maximum end time span if it makes sense.
+ uint64_t minTime = UINT64_MAX;
+ uint64_t maxTime = 0;
+ uint64_t maxSpan = 0;
+ totalSize = 0;
+
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) continue;
+
+ // validity checking
+ uint64_t oldest = mOldest[id].msec();
+ uint64_t newest = mNewest[id].msec();
+ if (newest <= oldest) {
+ spaces += spaces_total;
+ continue;
+ }
+
+ uint64_t span = newest - oldest;
+ if (span > (monthSec * MS_PER_SEC)) {
+ spaces += spaces_total;
+ continue;
+ }
+
+ // total span
+ if (minTime > oldest) minTime = oldest;
+ if (maxTime < newest) maxTime = newest;
+ if (span > maxSpan) maxSpan = span;
+ totalSize += span;
+
+ uint64_t dropped = mNewestDropped[id].msec();
+ if (dropped < oldest) dropped = oldest;
+ if (dropped > newest) dropped = newest;
+
+ oldLength = output.length();
+ output += android::base::StringPrintf("%*s%s", spaces, "",
+ formatMsec(span).c_str());
+ unsigned permille = ((newest - dropped) * 1000 + (span / 2)) / span;
+ if ((permille > 1) && (permille < 999)) {
+ output += android::base::StringPrintf("(%u", permille / 10);
+ permille %= 10;
+ if (permille) {
+ output += android::base::StringPrintf(".%u", permille);
+ }
+ output += android::base::StringPrintf("%%)");
+ }
+ spaces -= output.length() - oldLength;
+ spaces += spaces_total;
+ }
+ if ((maxTime > minTime) && ((maxTime -= minTime) < totalSize) &&
+ (maxTime > maxSpan)) {
+ maxSpan = maxTime;
+ }
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*s%s", spaces, "",
+ formatMsec(maxSpan).c_str());
+
static const char OverheadStr[] = "\nOverhead";
spaces = 10 - strlen(OverheadStr);
output += OverheadStr;
@@ -591,6 +807,13 @@
securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
}
+ if (enable) {
+ name = "Chattiest TAGs";
+ if (pid) name += android::base::StringPrintf(" for PID %d", pid);
+ name += ":";
+ output += tagNameTable.format(*this, uid, pid, name);
+ }
+
return output;
}
@@ -602,8 +825,10 @@
FILE* fp = fopen(buffer, "r");
if (fp) {
while (fgets(buffer, sizeof(buffer), fp)) {
- int uid;
- if (sscanf(buffer, "Uid: %d", &uid) == 1) {
+ int uid = AID_LOGD;
+ char space = 0;
+ if ((sscanf(buffer, "Uid: %d%c", &uid, &space) == 2) &&
+ isspace(space)) {
fclose(fp);
return uid;
}
@@ -612,12 +837,35 @@
}
return AID_LOGD; // associate this with the logger
}
+
+pid_t tidToPid(pid_t tid) {
+ char buffer[512];
+ snprintf(buffer, sizeof(buffer), "/proc/%u/status", tid);
+ FILE* fp = fopen(buffer, "r");
+ if (fp) {
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ int pid = tid;
+ char space = 0;
+ if ((sscanf(buffer, "Tgid: %d%c", &pid, &space) == 2) &&
+ isspace(space)) {
+ fclose(fp);
+ return pid;
+ }
+ }
+ fclose(fp);
+ }
+ return tid;
+}
}
uid_t LogStatistics::pidToUid(pid_t pid) {
return pidTable.add(pid)->second.getUid();
}
+pid_t LogStatistics::tidToPid(pid_t tid) {
+ return tidTable.add(tid)->second.getPid();
+}
+
// caller must free character string
const char* LogStatistics::pidToName(pid_t pid) const {
// An inconvenient truth ... getName() can alter the object
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 066b7de..8808aac 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -18,17 +18,23 @@
#define _LOGD_LOG_STATISTICS_H__
#include <ctype.h>
+#include <inttypes.h>
+#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/types.h>
#include <algorithm> // std::max
+#include <experimental/string_view>
#include <memory>
#include <string> // std::string
#include <unordered_map>
#include <android-base/stringprintf.h>
#include <android/log.h>
+#include <log/log_time.h>
#include <private/android_filesystem_config.h>
+#include <utils/FastStrcmp.h>
#include "LogBufferElement.h"
#include "LogUtils.h"
@@ -76,7 +82,7 @@
std::unique_ptr<const TEntry* []> sort(uid_t uid, pid_t pid,
size_t len) const {
if (!len) {
- std::unique_ptr<const TEntry* []> sorted(NULL);
+ std::unique_ptr<const TEntry* []> sorted(nullptr);
return sorted;
}
@@ -111,7 +117,7 @@
return sorted;
}
- inline iterator add(TKey key, LogBufferElement* element) {
+ inline iterator add(const TKey& key, const LogBufferElement* element) {
iterator it = map.find(key);
if (it == map.end()) {
it = map.insert(std::make_pair(key, TEntry(element))).first;
@@ -131,14 +137,21 @@
return it;
}
- void subtract(TKey key, LogBufferElement* element) {
+ void subtract(TKey&& key, const LogBufferElement* element) {
+ iterator it = map.find(std::move(key));
+ if ((it != map.end()) && it->second.subtract(element)) {
+ map.erase(it);
+ }
+ }
+
+ void subtract(const TKey& key, const LogBufferElement* element) {
iterator it = map.find(key);
if ((it != map.end()) && it->second.subtract(element)) {
map.erase(it);
}
}
- inline void drop(TKey key, LogBufferElement* element) {
+ inline void drop(TKey key, const LogBufferElement* element) {
iterator it = map.find(key);
if (it != map.end()) {
it->second.drop(element);
@@ -198,17 +211,18 @@
EntryBase() : size(0) {
}
- explicit EntryBase(LogBufferElement* element) : size(element->getMsgLen()) {
+ explicit EntryBase(const LogBufferElement* element)
+ : size(element->getMsgLen()) {
}
size_t getSizes() const {
return size;
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
size += element->getMsgLen();
}
- inline bool subtract(LogBufferElement* element) {
+ inline bool subtract(const LogBufferElement* element) {
size -= element->getMsgLen();
return !size;
}
@@ -239,7 +253,7 @@
EntryBaseDropped() : dropped(0) {
}
- explicit EntryBaseDropped(LogBufferElement* element)
+ explicit EntryBaseDropped(const LogBufferElement* element)
: EntryBase(element), dropped(element->getDropped()) {
}
@@ -247,15 +261,15 @@
return dropped;
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
dropped += element->getDropped();
EntryBase::add(element);
}
- inline bool subtract(LogBufferElement* element) {
+ inline bool subtract(const LogBufferElement* element) {
dropped -= element->getDropped();
return EntryBase::subtract(element) && !dropped;
}
- inline void drop(LogBufferElement* element) {
+ inline void drop(const LogBufferElement* element) {
dropped += 1;
EntryBase::subtract(element);
}
@@ -265,7 +279,7 @@
const uid_t uid;
pid_t pid;
- explicit UidEntry(LogBufferElement* element)
+ explicit UidEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
uid(element->getUid()),
pid(element->getPid()) {
@@ -281,7 +295,7 @@
return pid;
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
if (pid != element->getPid()) {
pid = -1;
}
@@ -292,10 +306,6 @@
std::string format(const LogStatistics& stat, log_id_t id) const;
};
-namespace android {
-uid_t pidToUid(pid_t pid);
-}
-
struct PidEntry : public EntryBaseDropped {
const pid_t pid;
uid_t uid;
@@ -307,7 +317,7 @@
uid(android::pidToUid(pid)),
name(android::pidToName(pid)) {
}
- explicit PidEntry(LogBufferElement* element)
+ explicit PidEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
pid(element->getPid()),
uid(element->getUid()),
@@ -317,7 +327,7 @@
: EntryBaseDropped(element),
pid(element.pid),
uid(element.uid),
- name(element.name ? strdup(element.name) : NULL) {
+ name(element.name ? strdup(element.name) : nullptr) {
}
~PidEntry() {
free(name);
@@ -339,14 +349,14 @@
inline void add(pid_t newPid) {
if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
free(name);
- name = NULL;
+ name = nullptr;
}
if (!name) {
name = android::pidToName(newPid);
}
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
uid_t incomingUid = element->getUid();
if (getUid() != incomingUid) {
uid = incomingUid;
@@ -375,7 +385,14 @@
uid(android::pidToUid(tid)),
name(android::tidToName(tid)) {
}
- explicit TidEntry(LogBufferElement* element)
+ TidEntry(pid_t tid)
+ : EntryBaseDropped(),
+ tid(tid),
+ pid(android::tidToPid(tid)),
+ uid(android::pidToUid(tid)),
+ name(android::tidToName(tid)) {
+ }
+ explicit TidEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
tid(element->getTid()),
pid(element->getPid()),
@@ -387,7 +404,7 @@
tid(element.tid),
pid(element.pid),
uid(element.uid),
- name(element.name ? strdup(element.name) : NULL) {
+ name(element.name ? strdup(element.name) : nullptr) {
}
~TidEntry() {
free(name);
@@ -412,14 +429,14 @@
inline void add(pid_t incomingTid) {
if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
free(name);
- name = NULL;
+ name = nullptr;
}
if (!name) {
name = android::tidToName(incomingTid);
}
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
uid_t incomingUid = element->getUid();
pid_t incomingPid = element->getPid();
if ((getUid() != incomingUid) || (getPid() != incomingPid)) {
@@ -442,7 +459,7 @@
pid_t pid;
uid_t uid;
- explicit TagEntry(LogBufferElement* element)
+ explicit TagEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
tag(element->getTag()),
pid(element->getPid()),
@@ -462,7 +479,7 @@
return android::tagToName(tag);
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
if (uid != element->getUid()) {
uid = -1;
}
@@ -476,6 +493,144 @@
std::string format(const LogStatistics& stat, log_id_t id) const;
};
+struct TagNameKey {
+ std::string* alloc;
+ std::experimental::string_view name; // Saves space if const char*
+
+ explicit TagNameKey(const LogBufferElement* element)
+ : alloc(nullptr), name("", strlen("")) {
+ if (element->isBinary()) {
+ uint32_t tag = element->getTag();
+ if (tag) {
+ const char* cp = android::tagToName(tag);
+ if (cp) {
+ name = std::experimental::string_view(cp, strlen(cp));
+ return;
+ }
+ }
+ alloc = new std::string(
+ android::base::StringPrintf("[%" PRIu32 "]", tag));
+ if (!alloc) return;
+ name = std::experimental::string_view(alloc->c_str(), alloc->size());
+ return;
+ }
+ const char* msg = element->getMsg();
+ if (!msg) {
+ name = std::experimental::string_view("chatty", strlen("chatty"));
+ return;
+ }
+ ++msg;
+ unsigned short len = element->getMsgLen();
+ len = (len <= 1) ? 0 : strnlen(msg, len - 1);
+ if (!len) {
+ name = std::experimental::string_view("<NULL>", strlen("<NULL>"));
+ return;
+ }
+ alloc = new std::string(msg, len);
+ if (!alloc) return;
+ name = std::experimental::string_view(alloc->c_str(), alloc->size());
+ }
+
+ explicit TagNameKey(TagNameKey&& rval)
+ : alloc(rval.alloc), name(rval.name.data(), rval.name.length()) {
+ rval.alloc = nullptr;
+ }
+
+ explicit TagNameKey(const TagNameKey& rval)
+ : alloc(rval.alloc ? new std::string(*rval.alloc) : nullptr),
+ name(alloc ? alloc->data() : rval.name.data(), rval.name.length()) {
+ }
+
+ ~TagNameKey() {
+ if (alloc) delete alloc;
+ }
+
+ operator const std::experimental::string_view() const {
+ return name;
+ }
+
+ const char* data() const {
+ return name.data();
+ }
+ size_t length() const {
+ return name.length();
+ }
+
+ bool operator==(const TagNameKey& rval) const {
+ if (length() != rval.length()) return false;
+ if (length() == 0) return true;
+ return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
+ }
+ bool operator!=(const TagNameKey& rval) const {
+ return !(*this == rval);
+ }
+
+ size_t getAllocLength() const {
+ return alloc ? alloc->length() + 1 + sizeof(std::string) : 0;
+ }
+};
+
+// Hash for TagNameKey
+template <>
+struct std::hash<TagNameKey>
+ : public std::unary_function<const TagNameKey&, size_t> {
+ size_t operator()(const TagNameKey& __t) const noexcept {
+ if (!__t.length()) return 0;
+ return std::hash<std::experimental::string_view>()(
+ std::experimental::string_view(__t));
+ }
+};
+
+struct TagNameEntry : public EntryBase {
+ pid_t tid;
+ pid_t pid;
+ uid_t uid;
+ TagNameKey name;
+
+ explicit TagNameEntry(const LogBufferElement* element)
+ : EntryBase(element),
+ tid(element->getTid()),
+ pid(element->getPid()),
+ uid(element->getUid()),
+ name(element) {
+ }
+
+ const TagNameKey& getKey() const {
+ return name;
+ }
+ const pid_t& getTid() const {
+ return tid;
+ }
+ const pid_t& getPid() const {
+ return pid;
+ }
+ const uid_t& getUid() const {
+ return uid;
+ }
+ const char* getName() const {
+ return name.data();
+ }
+ size_t getNameAllocLength() const {
+ return name.getAllocLength();
+ }
+
+ inline void add(const LogBufferElement* element) {
+ if (uid != element->getUid()) {
+ uid = -1;
+ }
+ if (pid != element->getPid()) {
+ pid = -1;
+ }
+ if (tid != element->getTid()) {
+ tid = -1;
+ }
+ EntryBase::add(element);
+ }
+
+ std::string formatHeader(const std::string& name, log_id_t id) const;
+ std::string format(const LogStatistics& stat, log_id_t id) const;
+};
+
template <typename TEntry>
class LogFindWorst {
std::unique_ptr<const TEntry* []> sorted;
@@ -520,6 +675,9 @@
size_t mDroppedElements[LOG_ID_MAX];
size_t mSizesTotal[LOG_ID_MAX];
size_t mElementsTotal[LOG_ID_MAX];
+ log_time mOldest[LOG_ID_MAX];
+ log_time mNewest[LOG_ID_MAX];
+ log_time mNewestDropped[LOG_ID_MAX];
static size_t SizesTotal;
bool enable;
@@ -546,9 +704,14 @@
// security tag list
tagTable_t securityTagTable;
+ // global tag list
+ typedef LogHashtable<TagNameKey, TagNameEntry> tagNameTable_t;
+ tagNameTable_t tagNameTable;
+
size_t sizeOf() const {
size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
tagTable.sizeOf() + securityTagTable.sizeOf() +
+ tagNameTable.sizeOf() +
(pidTable.size() * sizeof(pidTable_t::iterator)) +
(tagTable.size() * sizeof(tagTable_t::iterator));
for (auto it : pidTable) {
@@ -559,6 +722,7 @@
const char* name = it.second.getName();
if (name) size += strlen(name) + 1;
}
+ for (auto it : tagNameTable) size += it.second.getNameAllocLength();
log_id_for_each(id) {
size += uidTable[id].sizeOf();
size += uidTable[id].size() * sizeof(uidTable_t::iterator);
@@ -576,6 +740,7 @@
enable = true;
}
+ void addTotal(LogBufferElement* entry);
void add(LogBufferElement* entry);
void subtract(LogBufferElement* entry);
// entry->setDropped(1) must follow this call
@@ -623,6 +788,7 @@
// helper (must be locked directly or implicitly by mLogElementsLock)
const char* pidToName(pid_t pid) const;
uid_t pidToUid(pid_t pid);
+ pid_t tidToPid(pid_t tid);
const char* uidToName(uid_t uid) const;
};
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index ccc550a..25c2ad2 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -78,7 +78,7 @@
void LogTimeEntry::threadStop(void* obj) {
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
- lock();
+ wrlock();
if (me->mNonBlock) {
me->error_Locked();
@@ -134,7 +134,7 @@
me->leadingDropped = true;
- lock();
+ wrlock();
log_time start = me->mStart;
@@ -160,7 +160,7 @@
start = logbuf.flushTo(client, start, me->mLastTid, privileged,
security, FilterSecondPass, me);
- lock();
+ wrlock();
if (start == LogBufferElement::FLUSH_ERROR) {
me->error_Locked();
@@ -191,7 +191,7 @@
int LogTimeEntry::FilterFirstPass(const LogBufferElement* element, void* obj) {
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
if (me->leadingDropped) {
if (element->getDropped()) {
@@ -219,7 +219,7 @@
int LogTimeEntry::FilterSecondPass(const LogBufferElement* element, void* obj) {
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
me->mStart = element->getRealTime();
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index ec8252e..9ca2aea 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -61,7 +61,10 @@
const log_time mEnd; // only relevant if mNonBlock
// Protect List manipulations
- static void lock(void) {
+ static void wrlock(void) {
+ pthread_mutex_lock(×Lock);
+ }
+ static void rdlock(void) {
pthread_mutex_lock(×Lock);
}
static void unlock(void) {
@@ -104,7 +107,7 @@
mError = true;
}
void error(void) {
- lock();
+ wrlock();
error_Locked();
unlock();
}
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index fa9f398..4dcd3e7 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -38,6 +38,8 @@
// Caller must own and free returned value
char* pidToName(pid_t pid);
char* tidToName(pid_t tid);
+uid_t pidToUid(pid_t pid);
+pid_t tidToPid(pid_t tid);
// Furnished in LogTags.cpp. Thread safe.
const char* tagToName(uint32_t tag);
diff --git a/logd/event.logtags b/logd/event.logtags
index 39063a9..fa13a62 100644
--- a/logd/event.logtags
+++ b/logd/event.logtags
@@ -29,6 +29,7 @@
# 4: Number of allocations
# 5: Id
# 6: Percent
+# s: Number of seconds (monotonic time)
# Default value for data of type int/long is 2 (bytes).
#
# TODO: generate ".java" and ".h" files with integer constants from this file.
diff --git a/logd/logd.rc b/logd/logd.rc
index ee89b83..8804246 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -1,11 +1,11 @@
service logd /system/bin/logd
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
- socket logdw dgram 0222 logd logd
+ socket logdw dgram+passcred 0222 logd logd
file /proc/kmsg r
file /dev/kmsg w
user logd
- group logd system readproc
+ group logd system package_info readproc
writepid /dev/cpuset/system-background/tasks
service logd-reinit /system/bin/logd --reinit
diff --git a/logd/main.cpp b/logd/main.cpp
index 946485b..c8183f0 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -240,23 +240,36 @@
set_sched_policy(0, SP_BACKGROUND);
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
+ // We should drop to AID_LOGD, if we are anything else, we have
+ // even lesser privileges and accept our fate.
+ gid_t groups[] = {
+ AID_SYSTEM, // search access to /data/system path
+ AID_PACKAGE_INFO, // readonly access to /data/system/packages.list
+ };
+ if (setgroups(arraysize(groups), groups) == -1) {
+ android::prdebug(
+ "logd.daemon: failed to set AID_SYSTEM AID_PACKAGE_INFO groups");
+ }
+ if (setgid(AID_LOGD) != 0) {
+ android::prdebug("logd.daemon: failed to set AID_LOGD gid");
+ }
+ if (setuid(AID_LOGD) != 0) {
+ android::prdebug("logd.daemon: failed to set AID_LOGD uid");
+ }
+
cap_t caps = cap_init();
(void)cap_clear(caps);
(void)cap_set_proc(caps);
(void)cap_free(caps);
- // If we are AID_ROOT, we should drop to AID_LOGD+AID_SYSTEM, if we are
- // anything else, we have even lesser privileges and accept our fate. Not
- // worth checking for error returns setting this thread's privileges.
- (void)setgid(AID_SYSTEM); // readonly access to /data/system/packages.list
- (void)setuid(AID_LOGD); // access to everything logd, eg /data/misc/logd
-
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
// uidToName Privileged Worker
if (uid) {
name = nullptr;
- packagelist_parse(package_list_parser_cb, nullptr);
+ // if we got the perms wrong above, this would spam if we reported
+ // problems with acquisition of an uid name from the packages.
+ (void)packagelist_parse(package_list_parser_cb, nullptr);
uid = 0;
sem_post(&uidName);
@@ -407,6 +420,11 @@
// logging plugins like auditd and restart control. Additional
// transitory per-client threads are created for each reader.
int main(int argc, char* argv[]) {
+ // logd is written under the assumption that the timezone is UTC.
+ // If TZ is not set, persist.sys.timezone is looked up in some time utility
+ // libc functions, including mktime. It confuses the logd time handling,
+ // so here explicitly set TZ to UTC, which overrides the property.
+ setenv("TZ", "UTC", 1);
// issue reinit command. KISS argument parsing.
if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
return issueReinit();
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
index c053993..1915677 100644
--- a/logd/tests/Android.mk
+++ b/logd/tests/Android.mk
@@ -49,3 +49,38 @@
LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
LOCAL_SRC_FILES := $(test_src_files)
include $(BUILD_NATIVE_TEST)
+
+cts_executable := CtsLogdTestCases
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(cts_executable)
+LOCAL_MODULE_TAGS := tests
+LOCAL_CFLAGS += $(test_c_flags)
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
+LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
+LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_CTS_TEST_PACKAGE := android.core.logd
+include $(BUILD_CTS_EXECUTABLE)
+
+ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(cts_executable)_list
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(test_c_flags) -DHOST
+LOCAL_C_INCLUDES := external/gtest/include
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_CXX_STL := libc++
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_HOST_NATIVE_TEST)
+
+endif # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/logd/tests/AndroidTest.xml b/logd/tests/AndroidTest.xml
new file mode 100644
index 0000000..b16bdfd
--- /dev/null
+++ b/logd/tests/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+<configuration description="Config for CTS Logging Daemon test cases">
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsLogdTestCases->/data/local/tmp/CtsLogdTestCases" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="CtsLogdTestCases" />
+ <option name="runtime-hint" value="65s" />
+ </test>
+</configuration>
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index c81aa32..8ee5ea1 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <ctype.h>
#include <fcntl.h>
#include <inttypes.h>
#include <poll.h>
@@ -26,12 +27,13 @@
#include <string>
+#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
#include <gtest/gtest.h>
-#include <log/log.h>
#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
#ifdef __ANDROID__
#include <selinux/selinux.h>
#endif
@@ -39,6 +41,7 @@
#include "../LogReader.h" // pickup LOGD_SNDTIMEO
#include "../libaudit.h" // pickup AUDIT_RATE_LIMIT_*
+#ifdef __ANDROID__
static void send_to_control(char* buf, size_t len) {
int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
@@ -84,7 +87,7 @@
size_t ret = atol(buf) + 1;
if (ret < 4) {
delete[] buf;
- buf = NULL;
+ buf = nullptr;
break;
}
bool check = ret <= len;
@@ -106,7 +109,7 @@
// UID PACKAGE BYTES LINES
// 0 root 54164 147569
//
- char* benchmark = NULL;
+ char* benchmark = nullptr;
do {
static const char signature[] = "\n0 root ";
@@ -121,7 +124,7 @@
benchmark = cp;
#ifdef DEBUG
char* end = strstr(benchmark, "\n");
- if (end == NULL) {
+ if (end == nullptr) {
end = benchmark + strlen(benchmark);
}
fprintf(stderr, "parse for spam counter in \"%.*s\"\n",
@@ -153,18 +156,25 @@
if (value > 10UL) {
break;
}
- benchmark = NULL;
+ benchmark = nullptr;
} while (*cp);
return benchmark;
}
+#endif
TEST(logd, statistics) {
+#ifdef __ANDROID__
size_t len;
char* buf;
+ // Drop cache so that any access problems can be discovered.
+ if (!android::base::WriteStringToFile("3\n", "/proc/sys/vm/drop_caches")) {
+ GTEST_LOG_(INFO) << "Could not open trigger dropping inode cache";
+ }
+
alloc_statistics(&buf, &len);
- ASSERT_TRUE(NULL != buf);
+ ASSERT_TRUE(nullptr != buf);
// remove trailing FF
char* cp = buf + len - 1;
@@ -189,23 +199,46 @@
EXPECT_EQ(0, truncated);
char* main_logs = strstr(cp, "\nChattiest UIDs in main ");
- EXPECT_TRUE(NULL != main_logs);
+ EXPECT_TRUE(nullptr != main_logs);
char* radio_logs = strstr(cp, "\nChattiest UIDs in radio ");
if (!radio_logs)
- GTEST_LOG_(INFO) << "Value of: NULL != radio_logs\n"
+ GTEST_LOG_(INFO) << "Value of: nullptr != radio_logs\n"
"Actual: false\n"
"Expected: false\n";
char* system_logs = strstr(cp, "\nChattiest UIDs in system ");
- EXPECT_TRUE(NULL != system_logs);
+ EXPECT_TRUE(nullptr != system_logs);
char* events_logs = strstr(cp, "\nChattiest UIDs in events ");
- EXPECT_TRUE(NULL != events_logs);
+ EXPECT_TRUE(nullptr != events_logs);
+
+ // Check if there is any " u0_a#### " as this means packagelistparser broken
+ char* used_getpwuid = nullptr;
+ int used_getpwuid_len;
+ char* uid_name = cp;
+ static const char getpwuid_prefix[] = " u0_a";
+ while ((uid_name = strstr(uid_name, getpwuid_prefix)) != nullptr) {
+ used_getpwuid = uid_name + 1;
+ uid_name += strlen(getpwuid_prefix);
+ while (isdigit(*uid_name)) ++uid_name;
+ used_getpwuid_len = uid_name - used_getpwuid;
+ if (isspace(*uid_name)) break;
+ used_getpwuid = nullptr;
+ }
+ EXPECT_TRUE(nullptr == used_getpwuid);
+ if (used_getpwuid) {
+ fprintf(stderr, "libpackagelistparser failed to pick up %.*s\n",
+ used_getpwuid_len, used_getpwuid);
+ }
delete[] buf;
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
+#ifdef __ANDROID__
static void caught_signal(int /* signum */) {
}
@@ -315,8 +348,10 @@
fprintf(stderr, "}\n");
fflush(stderr);
}
+#endif
TEST(logd, both) {
+#ifdef __ANDROID__
log_msg msg;
// check if we can read any logs from logd
@@ -343,7 +378,7 @@
}
alarm(old_alarm);
- sigaction(SIGALRM, &old_sigaction, NULL);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
close(fd);
}
@@ -390,8 +425,12 @@
EXPECT_EQ(0, !user_logger_available && !kernel_logger_available);
EXPECT_EQ(0, user_logger_content && kernel_logger_content);
EXPECT_EQ(0, !user_logger_content && !kernel_logger_content);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
+#ifdef __ANDROID__
// BAD ROBOT
// Benchmark threshold are generally considered bad form unless there is
// is some human love applied to the continued maintenance and whether the
@@ -421,12 +460,12 @@
// Introduce some extreme spam for the worst UID filter
ASSERT_TRUE(
- NULL !=
+ nullptr !=
(fp = popen("/data/nativetest/liblog-benchmarks/liblog-benchmarks"
" BM_log_maximum_retry"
" BM_log_maximum"
" BM_clock_overhead"
- " BM_log_overhead"
+ " BM_log_print_overhead"
" BM_log_latency"
" BM_log_delay",
"r")));
@@ -434,13 +473,13 @@
char buffer[5120];
static const char* benchmarks[] = {
- "BM_log_maximum_retry ", "BM_log_maximum ", "BM_clock_overhead ",
- "BM_log_overhead ", "BM_log_latency ", "BM_log_delay "
+ "BM_log_maximum_retry ", "BM_log_maximum ", "BM_clock_overhead ",
+ "BM_log_print_overhead ", "BM_log_latency ", "BM_log_delay "
};
static const unsigned int log_maximum_retry = 0;
static const unsigned int log_maximum = 1;
static const unsigned int clock_overhead = 2;
- static const unsigned int log_overhead = 3;
+ static const unsigned int log_print_overhead = 3;
static const unsigned int log_latency = 4;
static const unsigned int log_delay = 5;
@@ -469,31 +508,33 @@
}
EXPECT_GE(200000UL, ns[log_maximum_retry]); // 104734 user
+ EXPECT_NE(0UL, ns[log_maximum_retry]); // failure to parse
EXPECT_GE(90000UL, ns[log_maximum]); // 46913 user
+ EXPECT_NE(0UL, ns[log_maximum]); // failure to parse
EXPECT_GE(4096UL, ns[clock_overhead]); // 4095
+ EXPECT_NE(0UL, ns[clock_overhead]); // failure to parse
- EXPECT_GE(250000UL, ns[log_overhead]); // 126886 user
+ EXPECT_GE(250000UL, ns[log_print_overhead]); // 126886 user
+ EXPECT_NE(0UL, ns[log_print_overhead]); // failure to parse
EXPECT_GE(10000000UL,
ns[log_latency]); // 1453559 user space (background cgroup)
+ EXPECT_NE(0UL, ns[log_latency]); // failure to parse
EXPECT_GE(20000000UL, ns[log_delay]); // 10500289 user
-
- for (unsigned i = 0; i < arraysize(ns); ++i) {
- EXPECT_NE(0UL, ns[i]);
- }
+ EXPECT_NE(0UL, ns[log_delay]); // failure to parse
alloc_statistics(&buf, &len);
bool collected_statistics = !!buf;
EXPECT_EQ(true, collected_statistics);
- ASSERT_TRUE(NULL != buf);
+ ASSERT_TRUE(nullptr != buf);
char* benchmark_statistics_found = find_benchmark_spam(buf);
- ASSERT_TRUE(benchmark_statistics_found != NULL);
+ ASSERT_TRUE(benchmark_statistics_found != nullptr);
// Check how effective the SPAM filter is, parse out Now size.
// 0 root 54164 147569
@@ -558,9 +599,11 @@
// 50% threshold for SPAM filter (<20% typical, lots of engineering margin)
ASSERT_GT(totalSize, nowSpamSize * 2);
}
+#endif
// b/26447386 confirm fixed
void timeout_negative(const char* command) {
+#ifdef __ANDROID__
log_msg msg_wrap, msg_timeout;
bool content_wrap = false, content_timeout = false, written = false;
unsigned int alarm_wrap = 0, alarm_timeout = 0;
@@ -586,7 +629,7 @@
written = write(fd, ask.c_str(), len) == (ssize_t)len;
if (!written) {
alarm(old_alarm);
- sigaction(SIGALRM, &old_sigaction, NULL);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
close(fd);
continue;
}
@@ -603,16 +646,20 @@
recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
}
- alarm_timeout =
- alarm((old_alarm <= 0) ? old_alarm
- : (old_alarm > (1 + 3 - alarm_wrap))
- ? old_alarm - 3 + alarm_wrap
- : 2);
- sigaction(SIGALRM, &old_sigaction, NULL);
+ if (old_alarm > 0) {
+ unsigned int time_spent = 3 - alarm_wrap;
+ if (old_alarm > time_spent + 1) {
+ old_alarm -= time_spent;
+ } else {
+ old_alarm = 2;
+ }
+ }
+ alarm_timeout = alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
close(fd);
- if (!content_wrap && !alarm_wrap && content_timeout && alarm_timeout) {
+ if (content_wrap && alarm_wrap && content_timeout && alarm_timeout) {
break;
}
}
@@ -630,6 +677,10 @@
EXPECT_NE(0U, alarm_wrap);
EXPECT_TRUE(content_timeout);
EXPECT_NE(0U, alarm_timeout);
+#else
+ command = nullptr;
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
TEST(logd, timeout_no_start) {
@@ -643,6 +694,7 @@
// b/26447386 refined behavior
TEST(logd, timeout) {
+#ifdef __ANDROID__
// b/33962045 This test interferes with other log reader tests that
// follow because of file descriptor socket persistence in the same
// process. So let's fork it to isolate it from giving us pain.
@@ -662,8 +714,8 @@
// A few tries to get it right just in case wrap kicks in due to
// content providers being active during the test.
int i = 5;
- log_time now(android_log_clockid());
- now.tv_sec -= 30; // reach back a moderate period of time
+ log_time start(android_log_clockid());
+ start.tv_sec -= 30; // reach back a moderate period of time
while (--i) {
int fd = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
@@ -678,7 +730,7 @@
std::string ask = android::base::StringPrintf(
"dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=%" PRIu32
".%09" PRIu32,
- now.tv_sec, now.tv_nsec);
+ start.tv_sec, start.tv_nsec);
struct sigaction ignore, old_sigaction;
memset(&ignore, 0, sizeof(ignore));
@@ -691,7 +743,7 @@
written = write(fd, ask.c_str(), len) == (ssize_t)len;
if (!written) {
alarm(old_alarm);
- sigaction(SIGALRM, &old_sigaction, NULL);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
close(fd);
continue;
}
@@ -708,12 +760,16 @@
recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
}
- alarm_timeout =
- alarm((old_alarm <= 0) ? old_alarm
- : (old_alarm > (1 + 3 - alarm_wrap))
- ? old_alarm - 3 + alarm_wrap
- : 2);
- sigaction(SIGALRM, &old_sigaction, NULL);
+ if (old_alarm > 0) {
+ unsigned int time_spent = 3 - alarm_wrap;
+ if (old_alarm > time_spent + 1) {
+ old_alarm -= time_spent;
+ } else {
+ old_alarm = 2;
+ }
+ }
+ alarm_timeout = alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
close(fd);
@@ -725,23 +781,23 @@
// active _or_ inactive during the test.
if (content_timeout) {
log_time msg(msg_timeout.entry.sec, msg_timeout.entry.nsec);
- if (msg < now) {
+ if (msg < start) {
fprintf(stderr, "%u.%09u < %u.%09u\n", msg_timeout.entry.sec,
- msg_timeout.entry.nsec, (unsigned)now.tv_sec,
- (unsigned)now.tv_nsec);
+ msg_timeout.entry.nsec, (unsigned)start.tv_sec,
+ (unsigned)start.tv_nsec);
_exit(-1);
}
- if (msg > now) {
- now = msg;
- now.tv_sec += 30;
- msg = log_time(android_log_clockid());
- if (now > msg) {
- now = msg;
- --now.tv_sec;
+ if (msg > start) {
+ start = msg;
+ start.tv_sec += 30;
+ log_time now = log_time(android_log_clockid());
+ if (start > now) {
+ start = now;
+ --start.tv_sec;
}
}
} else {
- now.tv_sec -= 120; // inactive, reach further back!
+ start.tv_sec -= 120; // inactive, reach further back!
}
}
@@ -754,8 +810,8 @@
}
if (content_wrap || !content_timeout) {
- fprintf(stderr, "now=%" PRIu32 ".%09" PRIu32 "\n", now.tv_sec,
- now.tv_nsec);
+ fprintf(stderr, "start=%" PRIu32 ".%09" PRIu32 "\n", start.tv_sec,
+ start.tv_nsec);
}
EXPECT_TRUE(written);
@@ -766,10 +822,14 @@
_exit(!written + content_wrap + alarm_wrap + !content_timeout +
!alarm_timeout);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
// b/27242723 confirmed fixed
TEST(logd, SNDTIMEO) {
+#ifdef __ANDROID__
static const unsigned sndtimeo =
LOGD_SNDTIMEO; // <sigh> it has to be done!
static const unsigned sleep_time = sndtimeo + 3;
@@ -811,7 +871,7 @@
int save_errno = (recv_ret < 0) ? errno : 0;
EXPECT_NE(0U, alarm(old_alarm));
- sigaction(SIGALRM, &old_sigaction, NULL);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
EXPECT_EQ(0, recv_ret);
if (recv_ret > 0) {
@@ -820,6 +880,9 @@
EXPECT_EQ(0, save_errno);
close(fd);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
TEST(logd, getEventTag_list) {
@@ -847,8 +910,8 @@
char* cp;
long ret = strtol(buffer, &cp, 10);
EXPECT_GT(ret, 16);
- EXPECT_TRUE(strstr(buffer, "\t(to life the universe etc|3)") != NULL);
- EXPECT_TRUE(strstr(buffer, "answer") != NULL);
+ EXPECT_TRUE(strstr(buffer, "\t(to life the universe etc|3)") != nullptr);
+ EXPECT_TRUE(strstr(buffer, "answer") != nullptr);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
@@ -868,8 +931,8 @@
char* cp;
long ret = strtol(buffer, &cp, 10);
EXPECT_GT(ret, 16);
- EXPECT_TRUE(strstr(buffer, "\t(new|1)") != NULL);
- EXPECT_TRUE(strstr(buffer, name) != NULL);
+ EXPECT_TRUE(strstr(buffer, "\t(new|1)") != nullptr);
+ EXPECT_TRUE(strstr(buffer, name) != nullptr);
// ToDo: also look for this in /data/misc/logd/event-log-tags and
// /dev/event-log-tags.
#else
@@ -877,11 +940,18 @@
#endif
}
-static inline int32_t get4LE(const char* src) {
- return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+#ifdef __ANDROID__
+static inline uint32_t get4LE(const uint8_t* src) {
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
}
+static inline uint32_t get4LE(const char* src) {
+ return get4LE(reinterpret_cast<const uint8_t*>(src));
+}
+#endif
+
void __android_log_btwrite_multiple__helper(int count) {
+#ifdef __ANDROID__
log_time ts(CLOCK_MONOTONIC);
log_time ts1(CLOCK_MONOTONIC);
@@ -910,7 +980,7 @@
ASSERT_EQ(0, info.si_status);
struct logger_list* logger_list;
- ASSERT_TRUE(NULL !=
+ ASSERT_TRUE(nullptr !=
(logger_list = android_logger_list_open(
LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
0, pid)));
@@ -974,6 +1044,10 @@
EXPECT_EQ(expected_chatty_count, chatty_count);
EXPECT_EQ(expected_identical_count, identical_count);
EXPECT_EQ(expected_expire_count, expire_count);
+#else
+ count = 0;
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
}
TEST(logd, multiple_test_1) {
@@ -999,22 +1073,24 @@
if (pid) {
siginfo_t info = {};
- if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) return 0;
- if (info.si_status) return 0;
+ if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) return -1;
+ if (info.si_status) return -1;
return pid;
}
// We may have DAC, but let's not have MAC
- if (setcon("u:object_r:shell:s0") < 0) {
+ if ((setcon("u:object_r:shell:s0") < 0) && (setcon("u:r:shell:s0") < 0)) {
int save_errno = errno;
security_context_t context;
getcon(&context);
- fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n", context,
- strerror(save_errno));
- freecon(context);
- _exit(-1);
- // NOTREACHED
- return 0;
+ if (strcmp(context, "u:r:shell:s0")) {
+ fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
+ context, strerror(save_errno));
+ freecon(context);
+ _exit(-1);
+ // NOTREACHED
+ return -1;
+ }
}
// The key here is we are root, but we are in u:r:shell:s0,
@@ -1042,25 +1118,39 @@
if (access(android::base::StringPrintf(file, num).c_str(), F_OK) == 0) {
_exit(-1);
// NOTREACHED
- return 0;
+ return -1;
}
usleep(usec);
--num;
}
_exit(0);
// NOTREACHED
- return 0;
+ return -1;
}
+static constexpr int background_period = 10;
+
static int count_avc(pid_t pid) {
int count = 0;
- if (pid == 0) return count;
+ // pid=-1 skip as pid is in error
+ if (pid == (pid_t)-1) return count;
- struct logger_list* logger_list;
- if (!(logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, pid)))
+ // pid=0 means we want to report the background count of avc: activities
+ struct logger_list* logger_list =
+ pid ? android_logger_list_alloc(
+ ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, pid)
+ : android_logger_list_alloc_time(
+ ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+ log_time(android_log_clockid()) -
+ log_time(background_period, 0),
+ 0);
+ if (!logger_list) return count;
+ struct logger* logger = android_logger_open(logger_list, LOG_ID_EVENTS);
+ if (!logger) {
+ android_logger_list_close(logger_list);
return count;
+ }
for (;;) {
log_msg log_msg;
@@ -1092,56 +1182,64 @@
}
#endif
-TEST(logd, sepolicy_rate_limiter_maximum) {
+TEST(logd, sepolicy_rate_limiter) {
#ifdef __ANDROID__
- static const int rate = AUDIT_RATE_LIMIT_MAX;
- static const int duration = 2;
- // Two seconds of a liveable sustained rate
- EXPECT_EQ(rate * duration, count_avc(sepolicy_rate(rate, rate * duration)));
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
-TEST(logd, sepolicy_rate_limiter_sub_burst) {
-#ifdef __ANDROID__
- // maximum period below half way between sustainable and burst rate.
- static const int threshold =
- ((AUDIT_RATE_LIMIT_BURST_DURATION *
- (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
- 1) /
- 2;
- static const int rate = (threshold / AUDIT_RATE_LIMIT_BURST_DURATION) - 1;
- static const int duration = AUDIT_RATE_LIMIT_BURST_DURATION;
- EXPECT_EQ(rate * duration, count_avc(sepolicy_rate(rate, rate * duration)));
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
-TEST(logd, sepolicy_rate_limiter_spam) {
-#ifdef __ANDROID__
- // maximum period of double the maximum burst rate
- static const int threshold =
- ((AUDIT_RATE_LIMIT_BURST_DURATION *
- (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
- 1) /
- 2;
- static const int rate = AUDIT_RATE_LIMIT_DEFAULT * 2;
- static const int duration = threshold / AUDIT_RATE_LIMIT_DEFAULT;
- EXPECT_GE(
- ((AUDIT_RATE_LIMIT_DEFAULT * duration) * 115) / 100, // +15% margin
- count_avc(sepolicy_rate(rate, rate * duration)));
- // give logd another 3 seconds to react to the burst before checking
- sepolicy_rate(rate, rate * 3);
- // maximum period at double the maximum burst rate (spam filter kicked in)
- EXPECT_GE(
- threshold * 2,
- count_avc(sepolicy_rate(rate, rate * AUDIT_RATE_LIMIT_BURST_DURATION)));
- // cool down, and check unspammy rate still works
- sleep(2);
- EXPECT_LE(AUDIT_RATE_LIMIT_BURST_DURATION - 1, // allow _one_ to be lost
- count_avc(sepolicy_rate(1, AUDIT_RATE_LIMIT_BURST_DURATION)));
+ int background_selinux_activity_too_high = count_avc(0);
+ if (background_selinux_activity_too_high > 2) {
+ GTEST_LOG_(ERROR) << "Too much background selinux activity "
+ << background_selinux_activity_too_high * 60 /
+ background_period
+ << "/minute on the device, this test\n"
+ << "can not measure the functionality of the "
+ << "sepolicy rate limiter. Expect test to\n"
+ << "fail as this device is in a bad state, "
+ << "but is not strictly a unit test failure.";
+ }
+ // sepolicy_rate_limiter_maximum
+ { // maximum precharch test block.
+ static constexpr int rate = AUDIT_RATE_LIMIT_MAX;
+ static constexpr int duration = 2;
+ // Two seconds of a liveable sustained rate
+ EXPECT_EQ(rate * duration,
+ count_avc(sepolicy_rate(rate, rate * duration)));
+ }
+ // sepolicy_rate_limiter_sub_burst
+ { // maximum period below half way between sustainable and burst rate
+ static constexpr int threshold =
+ ((AUDIT_RATE_LIMIT_BURST_DURATION *
+ (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
+ 1) /
+ 2;
+ static constexpr int rate =
+ (threshold / AUDIT_RATE_LIMIT_BURST_DURATION) - 1;
+ static constexpr int duration = AUDIT_RATE_LIMIT_BURST_DURATION;
+ EXPECT_EQ(rate * duration,
+ count_avc(sepolicy_rate(rate, rate * duration)));
+ }
+ // sepolicy_rate_limiter_spam
+ { // hit avc: hard beyond reason block.
+ // maximum period of double the maximum burst rate
+ static constexpr int threshold =
+ ((AUDIT_RATE_LIMIT_BURST_DURATION *
+ (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
+ 1) /
+ 2;
+ static constexpr int rate = AUDIT_RATE_LIMIT_DEFAULT * 2;
+ static constexpr int duration = threshold / AUDIT_RATE_LIMIT_DEFAULT;
+ EXPECT_GE(
+ ((AUDIT_RATE_LIMIT_DEFAULT * duration) * 115) / 100, // +15% margin
+ count_avc(sepolicy_rate(rate, rate * duration)));
+ // give logd another 3 seconds to react to the burst before checking
+ sepolicy_rate(rate, rate * 3);
+ // maximum period at double maximum burst rate (spam filter kicked in)
+ EXPECT_GE(threshold * 2,
+ count_avc(sepolicy_rate(
+ rate, rate * AUDIT_RATE_LIMIT_BURST_DURATION)));
+ // cool down, and check unspammy rate still works
+ sleep(2);
+ EXPECT_LE(AUDIT_RATE_LIMIT_BURST_DURATION - 1, // allow _one_ lost
+ count_avc(sepolicy_rate(1, AUDIT_RATE_LIMIT_BURST_DURATION)));
+ }
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/platform_tools_tool_version.mk b/platform_tools_tool_version.mk
new file mode 100644
index 0000000..eed2ab5
--- /dev/null
+++ b/platform_tools_tool_version.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2017 Google Inc.
+#
+# 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.
+
+# We rewrite ${PLATFORM_SDK_VERSION} with 0 rather than $(PLATFORM_SDK_VERSION)
+# because on the actual platform tools release branches the file contains a
+# literal instead. Using 0 lets us easily distinguish non-canonical builds.
+platform_tools_version := $(shell sed \
+ 's/$${PLATFORM_SDK_VERSION}/0/ ; s/^Pkg.Revision=\(.*\)/\1/p ; d' \
+ development/sdk/plat_tools_source.prop_template \
+ )
+tool_version := $(platform_tools_version)-$(BUILD_NUMBER_FROM_FILE)
diff --git a/reboot/reboot.c b/reboot/reboot.c
index 007dfba..f0cf40c 100644
--- a/reboot/reboot.c
+++ b/reboot/reboot.c
@@ -56,6 +56,7 @@
if (argc > optind)
optarg = argv[optind];
+ if (!optarg || !optarg[0]) optarg = "shell";
prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
if (prop_len >= sizeof(property_val)) {
diff --git a/rootdir/asan.options b/rootdir/asan.options
index d728f12..a264d2d 100644
--- a/rootdir/asan.options
+++ b/rootdir/asan.options
@@ -5,3 +5,4 @@
detect_container_overflow=0
abort_on_error=1
include_if_exists=/system/asan.options.%b
+include_if_exists=/data/asan/system/asan.options.%b
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index e2c70f3..436589e 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -63,7 +63,7 @@
namespace.sphal.link.default.shared_libs = libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libvndksupport.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.base@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: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
# Renderscript gets separate namespace
namespace.sphal.link.rs.shared_libs = libRS_internal.so
@@ -85,7 +85,7 @@
namespace.rs.links = default,vndk
namespace.rs.link.default.shared_libs = libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libmediandk.so:libui.so:libvndksupport.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.base@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.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
###############################################################################
# "vndk" namespace
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index bcdecf2..c5e149c 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,3 +1,4 @@
+# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
libandroid.so
libaaudio.so
libc.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index f4da09d..a4ca683 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,3 +1,4 @@
+# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
libandroid.so
libaaudio.so
libc.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 3b996a4..fb1fbd4 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -27,6 +27,18 @@
# Set the security context of /postinstall if present.
restorecon /postinstall
+ # Mount cgroup mount point for cpu accounting
+ mount cgroup none /acct cpuacct
+ mkdir /acct/uid
+
+ # root memory control cgroup, used by lmkd
+ mkdir /dev/memcg 0700 root system
+ mount cgroup none /dev/memcg memory
+ # app mem cgroups, used by activity manager, lmkd and zygote
+ mkdir /dev/memcg/apps/ 0755 system system
+ # cgroup for system_server and surfaceflinger
+ mkdir /dev/memcg/system 0550 system system
+
start ueventd
on init
@@ -43,10 +55,6 @@
# Link /vendor to /system/vendor for devices without a vendor partition.
symlink /system/vendor /vendor
- # Mount cgroup mount point for cpu accounting
- mount cgroup none /acct cpuacct
- mkdir /acct/uid
-
# Create energy-aware scheduler tuning nodes
mkdir /dev/stune
mount cgroup none /dev/stune schedtune
@@ -99,12 +107,6 @@
symlink /storage/self/primary /mnt/sdcard
symlink /mnt/user/0/primary /mnt/runtime/default/self/primary
- # root memory control cgroup, used by lmkd
- mkdir /dev/memcg 0700 root system
- mount cgroup none /dev/memcg memory
- # app mem cgroups, used by activity manager, lmkd and zygote
- mkdir /dev/memcg/apps/ 0755 system system
-
write /proc/sys/kernel/panic_on_oops 1
write /proc/sys/kernel/hung_task_timeout_secs 0
write /proc/cpu/alignment 4
@@ -221,6 +223,8 @@
mount pstore pstore /sys/fs/pstore
chown system log /sys/fs/pstore/console-ramoops
chmod 0440 /sys/fs/pstore/console-ramoops
+ chown system log /sys/fs/pstore/console-ramoops-0
+ chmod 0440 /sys/fs/pstore/console-ramoops-0
chown system log /sys/fs/pstore/pmsg-ramoops-0
chmod 0440 /sys/fs/pstore/pmsg-ramoops-0
@@ -234,7 +238,10 @@
export DOWNLOAD_CACHE /data/cache
# set RLIMIT_NICE to allow priorities from 19 to -20
- setrlimit 13 40 40
+ setrlimit nice 40 40
+
+ # Allow up to 32K FDs per process
+ setrlimit nofile 32768 32768
# This allows the ledtrig-transient properties to be created here so
# that they can be chown'd to system:system later on boot
@@ -313,7 +320,6 @@
# Make sure /sys/kernel/debug (if present) is labeled properly
# Note that tracefs may be mounted under debug, so we need to cross filesystems
restorecon --recursive --cross-filesystems /sys/kernel/debug
- chmod 0755 /sys/kernel/debug/tracing
# We chown/chmod /cache again so because mount is run as root + defaults
chown system cache /cache
@@ -351,6 +357,10 @@
mkdir /cache/lost+found 0770 root root
on late-fs
+ # Ensure that tracefs has the correct permissions.
+ # This does not work correctly if it is called in post-fs.
+ chmod 0755 /sys/kernel/debug/tracing
+
# HALs required before storage encryption can get unlocked (FBE/FDE)
class_start early_hal
@@ -375,6 +385,20 @@
# create basic filesystem structure
mkdir /data/misc 01771 system misc
+ mkdir /data/misc/recovery 0770 system log
+ copy /data/misc/recovery/ro.build.fingerprint /data/misc/recovery/ro.build.fingerprint.1
+ chmod 0440 /data/misc/recovery/ro.build.fingerprint.1
+ chown system log /data/misc/recovery/ro.build.fingerprint.1
+ write /data/misc/recovery/ro.build.fingerprint ${ro.build.fingerprint}
+ chmod 0440 /data/misc/recovery/ro.build.fingerprint
+ chown system log /data/misc/recovery/ro.build.fingerprint
+ mkdir /data/misc/recovery/proc 0770 system log
+ copy /data/misc/recovery/proc/version /data/misc/recovery/proc/version.1
+ chmod 0440 /data/misc/recovery/proc/version.1
+ chown system log /data/misc/recovery/proc/version.1
+ copy /proc/version /data/misc/recovery/proc/version
+ chmod 0440 /data/misc/recovery/proc/version
+ chown system log /data/misc/recovery/proc/version
mkdir /data/misc/bluedroid 02770 bluetooth bluetooth
# Fix the access permissions and group ownership for 'bt_config.conf'
chmod 0660 /data/misc/bluedroid/bt_config.conf
@@ -409,7 +433,6 @@
mkdir /data/misc/boottrace 0771 system shell
mkdir /data/misc/update_engine 0700 root root
mkdir /data/misc/trace 0700 root root
- mkdir /data/misc/reboot 0700 system system
# profile file layout
mkdir /data/misc/profiles 0771 system system
mkdir /data/misc/profiles/cur 0771 system system
@@ -674,12 +697,17 @@
on property:security.perf_harden=1
write /proc/sys/kernel/perf_event_paranoid 3
+# on shutdown
+# In device's init.rc, this trigger can be used to do device-specific actions
+# before shutdown. e.g disable watchdog and mask error handling
+
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
class core
critical
seclabel u:r:ueventd:s0
+ shutdown critical
service healthd /system/bin/healthd
class core
diff --git a/rootdir/init.usb.configfs.rc b/rootdir/init.usb.configfs.rc
index 32f0198..de1aab3 100644
--- a/rootdir/init.usb.configfs.rc
+++ b/rootdir/init.usb.configfs.rc
@@ -2,6 +2,7 @@
write /config/usb_gadget/g1/UDC "none"
stop adbd
setprop sys.usb.ffs.ready 0
+ setprop sys.usb.ffs.mtp.ready 0
write /config/usb_gadget/g1/bDeviceClass 0
write /config/usb_gadget/g1/bDeviceSubClass 0
write /config/usb_gadget/g1/bDeviceProtocol 0
@@ -11,6 +12,9 @@
rmdir /config/usb_gadget/g1/functions/rndis.gs4
setprop sys.usb.state ${sys.usb.config}
+on property:init.svc.adbd=stopped
+ setprop sys.usb.ffs.ready 0
+
on property:sys.usb.config=adb && property:sys.usb.configfs=1
start adbd
@@ -20,7 +24,7 @@
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
setprop sys.usb.state ${sys.usb.config}
-on property:sys.usb.config=mtp && property:sys.usb.configfs=1
+on property:sys.usb.ffs.mtp.ready=1 && property:sys.usb.config=mtp && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp"
symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
@@ -29,14 +33,15 @@
on property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
start adbd
-on property:sys.usb.ffs.ready=1 && property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
+on property:sys.usb.ffs.ready=1 && property:sys.usb.ffs.mtp.ready=1 && \
+property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp_adb"
symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
setprop sys.usb.state ${sys.usb.config}
-on property:sys.usb.config=ptp && property:sys.usb.configfs=1
+on property:sys.usb.ffs.mtp.ready=1 && property:sys.usb.config=ptp && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp"
symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
@@ -45,7 +50,8 @@
on property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
start adbd
-on property:sys.usb.ffs.ready=1 && property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
+on property:sys.usb.ffs.ready=1 && property:sys.usb.ffs.mtp.ready=1 && \
+property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp_adb"
symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 915d159..3168f40 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -12,7 +12,7 @@
mkdir /data/adb 0700 root root
# adbd is controlled via property triggers in init.<platform>.usb.rc
-service adbd /sbin/adbd --root_seclabel=u:r:su:s0
+service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
class core
socket adbd stream 660 system system
disabled
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index f5c93b7..eadf219 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,5 +1,37 @@
subsystem adf
- devname uevent_devname
+ devname uevent_devname
+
+subsystem graphics
+ devname uevent_devpath
+ dirname /dev/graphics
+
+subsystem drm
+ devname uevent_devpath
+ dirname /dev/dri
+
+subsystem oncrpc
+ devname uevent_devpath
+ dirname /dev/oncrpc
+
+subsystem adsp
+ devname uevent_devpath
+ dirname /dev/adsp
+
+subsystem msm_camera
+ devname uevent_devpath
+ dirname /dev/msm_camera
+
+subsystem input
+ devname uevent_devpath
+ dirname /dev/input
+
+subsystem mtd
+ devname uevent_devpath
+ dirname /dev/mtd
+
+subsystem sound
+ devname uevent_devpath
+ dirname /dev/snd
# ueventd can only set permissions on device nodes and their associated
# sysfs attributes, not on arbitrary paths.
@@ -22,9 +54,6 @@
/dev/hwbinder 0666 root root
/dev/vndbinder 0666 root root
-# Anyone can read the logs, but if they're not in the "logs"
-# group, then they'll only see log entries for their UID.
-/dev/log/* 0666 root log
/dev/pmsg0 0222 root log
# the msm hw3d client device node is world writable/readable.
@@ -40,7 +69,7 @@
/dev/diag 0660 radio radio
/dev/diag_arm9 0660 radio radio
/dev/ttyMSM0 0600 bluetooth bluetooth
-/dev/uhid 0660 system bluetooth
+/dev/uhid 0660 uhid uhid
/dev/uinput 0660 system bluetooth
/dev/alarm 0664 system radio
/dev/rtc0 0640 system system
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
index 0c58574..5b4dc58 100644
--- a/sdcard/Android.mk
+++ b/sdcard/Android.mk
@@ -8,6 +8,5 @@
LOCAL_SHARED_LIBRARIES := libbase libcutils libminijail libpackagelistparser
LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
include $(BUILD_EXECUTABLE)
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index c342cf8..343a903 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -249,7 +249,9 @@
global.root.uid = AID_ROOT;
global.root.under_android = false;
- strcpy(global.source_path, source_path);
+ // Clang static analyzer think strcpy potentially overwrites other fields
+ // in global. Use snprintf() to mute the false warning.
+ snprintf(global.source_path, sizeof(global.source_path), "%s", source_path);
if (multi_user) {
global.root.perm = PERM_PRE_ROOT;
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 4f4fc5d..9620d63 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -3,7 +3,7 @@
required: [
"bzip2",
"grep",
- "gzip",
+ "grep_vendor",
"mkshrc",
"mkshrc_vendor",
"reboot",
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
new file mode 100644
index 0000000..c4e8aac
--- /dev/null
+++ b/shell_and_utilities/README.md
@@ -0,0 +1,166 @@
+Android's shell and utilities
+=============================
+
+Since IceCreamSandwich Android has used
+[mksh](https://www.mirbsd.org/mksh.htm) as its shell. Before then it used
+[ash](https://en.wikipedia.org/wiki/Almquist_shell) (which actually
+remained unused in the tree up to and including KitKat).
+
+Initially Android had a very limited command-line provided by its own
+"toolbox" binary. Since Marshmallow almost everything is supplied by
+[toybox](http://landley.net/toybox/) instead.
+
+We started moving a few of the more important tools to full
+BSD implementations in JellyBean, and continued this work in
+Lollipop. Lollipop was a major break with the past in many ways (LP64
+support and the switch to ART both having lots of knock-on effects around
+the system), so although this was the beginning of the end of toolbox it
+(a) didn't stand out given all the other systems-level changes and (b)
+in Marshmallow we changed direction and started the move to toybox.
+
+Not everything is provided by toybox, though. We currently still use
+the BSD dd and grep (because the toybox versions are still unfinished),
+and for the bzip2 command-line tools we use the ones that are part of
+the bzip2 distribution.
+
+The lists below show what tools were provided and where they came from in
+each release starting with Gingerbread. This doesn't tell the full story,
+because the toolbox implementations did have bugs fixed and options added
+over the years. Gingerbread's rm, for example, supported `-r`/`-R` but not
+`-f`. But this gives you an idea of what was available in any given release,
+and how usable it was likely to be.
+
+Also note that in any given release `toybox` probably contains more
+commands than there are symlinks for in `/system/bin`. You can get the
+full list for a release by running `toybox` directly.
+
+
+Android 2.3 (Gingerbread)
+-------------------------
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+umount uptime vmstat watchprops wipe
+
+
+Android 4.0 (IceCreamSandwich)
+------------------------------
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+touch umount uptime vmstat watchprops wipe
+
+
+Android 4.1-4.3 (JellyBean)
+---------------------------
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mount mv nandread netstat notify
+printenv ps reboot renice restorecon rm rmdir rmmod route runcon schedtop
+sendevent setconsole setenforce setprop setsebool sleep smd start stop
+sync top touch umount uptime vmstat watchprops wipe
+
+
+Android 4.4 (KitKat)
+--------------------
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mkswap mount mv nandread netstat
+notify printenv ps readlink renice restorecon rm rmdir rmmod route runcon
+schedtop sendevent setconsole setenforce setprop setsebool sleep smd start
+stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
+
+
+Android 5.0 (Lollipop)
+----------------------
+
+BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
+
+toolbox: chcon chmod clear cmp date df dmesg getenforce getevent getprop
+getsebool hd id ifconfig iftop insmod ioctl ionice load\_policy log ls
+lsmod lsof md5 mkdir mknod mkswap mount nandread netstat newfs\_msdos
+nohup notify ps readlink renice restorecon rmmod route runcon schedtop
+sendevent setenforce setprop setsebool smd start stop swapoff swapon
+top touch umount uptime vmstat watchprops wipe
+
+
+Android 6.0 (Marshmallow)
+-------------------------
+
+BSD: dd du grep
+
+toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
+newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
+
+toybox: acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
+env expand expr fallocate false find free getenforce getprop groups
+head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
+logname losetup lsmod lsusb md5sum mkdir mknod mkswap mktemp modinfo
+more mountpoint mv netstat nice nl nohup od paste patch pgrep pidof
+pkill pmap printenv printf pwd readlink realpath restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate umount uname uniq unix2dos usleep
+vmstat wc which whoami xargs yes
+
+
+Android 7.0 (Nougat)
+--------------------
+
+BSD: dd grep
+
+toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
+sendevent start stop top
+
+toybox: acpi base64 basename blockdev bzcat cal cat chcon chgrp chmod
+chown chroot cksum clear comm cmp cp cpio cut date df dirname dmesg
+dos2unix du echo env expand expr fallocate false find flock free
+getenforce getprop groups head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall load\_policy ln logname losetup ls
+lsmod lsof lsusb md5sum mkdir mknod mkswap mktemp modinfo more mount
+mountpoint mv netstat nice nl nohup od paste patch pgrep pidof pkill
+pmap printenv printf pwd readlink realpath renice restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate tty ulimit umount uname uniq unix2dos
+uptime usleep vmstat wc which whoami xargs xxd yes
+
+
+Android 8.0 (Oreo)
+------------------
+
+BSD: dd grep
+
+bzip2: bzcat bzip2 bunzip2
+
+toolbox: getevent newfs\_msdos
+
+toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
+dos2unix du echo env expand expr fallocate false file find flock free
+getenforce getprop groups gunzip gzip head hostname hwclock id ifconfig
+inotifyd insmod ionice iorenice kill killall ln load\_policy log logname
+losetup ls lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod
+mkswap mktemp modinfo modprobe more mount mountpoint mv netstat nice
+nl nohup od paste patch pgrep pidof pkill pmap printenv printf ps pwd
+readlink realpath renice restorecon rm rmdir rmmod runcon sed sendevent
+seq setenforce setprop setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split start stat stop strings swapoff swapon sync
+sysctl tac tail tar taskset tee time timeout top touch tr true truncate
+tty ulimit umount uname uniq unix2dos uptime usleep uudecode uuencode
+vmstat wc which whoami xargs xxd yes zcat
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
new file mode 100644
index 0000000..8db8327
--- /dev/null
+++ b/toolbox/Android.bp
@@ -0,0 +1,58 @@
+common_cflags = [
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wno-unused-const-variable",
+ "-include bsd-compatibility.h"
+]
+
+cc_library_static {
+ srcs: [
+ "upstream-netbsd/bin/dd/args.c",
+ "upstream-netbsd/bin/dd/conv.c",
+ "upstream-netbsd/bin/dd/dd.c",
+ "upstream-netbsd/bin/dd/dd_hostops.c",
+ "upstream-netbsd/bin/dd/misc.c",
+ "upstream-netbsd/bin/dd/position.c",
+ "upstream-netbsd/lib/libc/gen/getbsize.c",
+ "upstream-netbsd/lib/libc/gen/humanize_number.c",
+ "upstream-netbsd/lib/libc/stdlib/strsuftoll.c",
+ "upstream-netbsd/lib/libc/string/swab.c",
+ "upstream-netbsd/lib/libutil/raise_default_signal.c",
+ ],
+ cflags: common_cflags + [
+ "-Dmain=dd_main",
+ "-DNO_CONV",
+ ],
+ local_include_dirs: ["upstream-netbsd/include/"],
+ name: "libtoolbox_dd",
+}
+
+// We build BSD grep separately, so it can provide egrep and fgrep too.
+cc_defaults {
+ name: "grep_common",
+ srcs: [
+ "upstream-netbsd/usr.bin/grep/fastgrep.c",
+ "upstream-netbsd/usr.bin/grep/file.c",
+ "upstream-netbsd/usr.bin/grep/grep.c",
+ "upstream-netbsd/usr.bin/grep/queue.c",
+ "upstream-netbsd/usr.bin/grep/util.c",
+ ],
+ cflags: common_cflags,
+ local_include_dirs: ["upstream-netbsd/include/"],
+ symlinks: ["egrep", "fgrep"],
+}
+
+cc_binary {
+ name: "grep",
+ defaults: ["grep_common"],
+}
+
+// Build vendor grep.
+// TODO: Add vendor_available to "grep" module and remove "grep_vendor" module
+// when vendor_available is fully supported.
+cc_binary {
+ name: "grep_vendor",
+ stem: "grep",
+ vendor: true,
+ defaults: ["grep_common"],
+}
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index d6ead1a..94029d8 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -1,30 +1,9 @@
LOCAL_PATH:= $(call my-dir)
-
common_cflags := \
-Werror -Wno-unused-parameter -Wno-unused-const-variable \
-include bsd-compatibility.h \
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
- upstream-netbsd/bin/dd/args.c \
- upstream-netbsd/bin/dd/conv.c \
- upstream-netbsd/bin/dd/dd.c \
- upstream-netbsd/bin/dd/dd_hostops.c \
- upstream-netbsd/bin/dd/misc.c \
- upstream-netbsd/bin/dd/position.c \
- upstream-netbsd/lib/libc/gen/getbsize.c \
- upstream-netbsd/lib/libc/gen/humanize_number.c \
- upstream-netbsd/lib/libc/stdlib/strsuftoll.c \
- upstream-netbsd/lib/libc/string/swab.c \
- upstream-netbsd/lib/libutil/raise_default_signal.c
-LOCAL_CFLAGS += $(common_cflags) -Dmain=dd_main -DNO_CONV
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-netbsd/include/
-LOCAL_MODULE := libtoolbox_dd
-include $(BUILD_STATIC_LIBRARY)
-
-
include $(CLEAR_VARS)
BSD_TOOLS := \
@@ -80,18 +59,3 @@
$(INPUT_H_LABELS_H): $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/generate-input.h-labels.py $(UAPI_INPUT_EVENT_CODES_H)
$(INPUT_H_LABELS_H):
$(transform-generated-source)
-
-
-# We build BSD grep separately, so it can provide egrep and fgrep too.
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
- upstream-netbsd/usr.bin/grep/fastgrep.c \
- upstream-netbsd/usr.bin/grep/file.c \
- upstream-netbsd/usr.bin/grep/grep.c \
- upstream-netbsd/usr.bin/grep/queue.c \
- upstream-netbsd/usr.bin/grep/util.c
-LOCAL_CFLAGS += $(common_cflags)
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-netbsd/include/
-LOCAL_MODULE := grep
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,egrep fgrep,ln -sf grep $(TARGET_OUT)/bin/$(t);)
-include $(BUILD_EXECUTABLE)
diff --git a/toolbox/upstream-netbsd/include/sys/mtio.h b/toolbox/upstream-netbsd/include/sys/mtio.h
deleted file mode 100644
index 8fb5655..0000000
--- a/toolbox/upstream-netbsd/include/sys/mtio.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <linux/mtio.h>
diff --git a/trusty/Android.bp b/trusty/Android.bp
new file mode 100644
index 0000000..386fbe6
--- /dev/null
+++ b/trusty/Android.bp
@@ -0,0 +1,7 @@
+subdirs = [
+ "gatekeeper",
+ "keymaster",
+ "libtrusty",
+ "nvram",
+ "storage/*",
+]
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
new file mode 100644
index 0000000..a9566a1
--- /dev/null
+++ b/trusty/gatekeeper/Android.bp
@@ -0,0 +1,46 @@
+//
+// 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.
+//
+
+// WARNING: Everything listed here will be built on ALL platforms,
+// including x86, the emulator, and the SDK. Modules must be uniquely
+// named (liblights.panda), and must build everywhere, or limit themselves
+// to only building on ARM if they include assembly. Individual makefiles
+// are responsible for having their own logic, for fine-grained control.
+
+cc_library_shared {
+ name: "gatekeeper.trusty",
+
+ relative_install_path: "hw",
+
+ srcs: [
+ "module.cpp",
+ "trusty_gatekeeper_ipc.c",
+ "trusty_gatekeeper.cpp",
+ ],
+
+ cflags: [
+ "-fvisibility=hidden",
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "libgatekeeper",
+ "liblog",
+ "libcutils",
+ "libtrusty",
+ ],
+}
diff --git a/trusty/gatekeeper/Android.mk b/trusty/gatekeeper/Android.mk
deleted file mode 100644
index 3982c8f..0000000
--- a/trusty/gatekeeper/Android.mk
+++ /dev/null
@@ -1,46 +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.
-#
-
-# WARNING: Everything listed here will be built on ALL platforms,
-# including x86, the emulator, and the SDK. Modules must be uniquely
-# named (liblights.panda), and must build everywhere, or limit themselves
-# to only building on ARM if they include assembly. Individual makefiles
-# are responsible for having their own logic, for fine-grained control.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := gatekeeper.trusty
-
-LOCAL_MODULE_RELATIVE_PATH := hw
-
-LOCAL_SRC_FILES := \
- module.cpp \
- trusty_gatekeeper_ipc.c \
- trusty_gatekeeper.cpp
-
-LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
- libgatekeeper \
- liblog \
- libcutils \
- libtrusty
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
new file mode 100644
index 0000000..3b6867c
--- /dev/null
+++ b/trusty/keymaster/Android.bp
@@ -0,0 +1,67 @@
+//
+// 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.
+//
+
+// WARNING: Everything listed here will be built on ALL platforms,
+// including x86, the emulator, and the SDK. Modules must be uniquely
+// named (liblights.panda), and must build everywhere, or limit themselves
+// to only building on ARM if they include assembly. Individual makefiles
+// are responsible for having their own logic, for fine-grained control.
+
+// trusty_keymaster is a binary used only for on-device testing. It
+// runs Trusty Keymaster through a basic set of operations with RSA
+// and ECDSA keys.
+cc_binary {
+ name: "trusty_keymaster_tipc",
+ srcs: [
+ "trusty_keymaster_device.cpp",
+ "trusty_keymaster_ipc.cpp",
+ "trusty_keymaster_main.cpp",
+ ],
+ shared_libs: [
+ "libcrypto",
+ "libcutils",
+ "libkeymaster1",
+ "libtrusty",
+ "libkeymaster_messages",
+ "libsoftkeymasterdevice",
+ "liblog",
+ ],
+}
+
+// keystore.trusty is the HAL used by keystore on Trusty devices.
+cc_library_shared {
+ name: "keystore.trusty",
+ relative_install_path: "hw",
+ srcs: [
+ "module.cpp",
+ "trusty_keymaster_ipc.cpp",
+ "trusty_keymaster_device.cpp",
+ ],
+
+ cflags: [
+ "-fvisibility=hidden",
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "libcrypto",
+ "libkeymaster_messages",
+ "libtrusty",
+ "liblog",
+ "libcutils",
+ ],
+}
diff --git a/trusty/keymaster/Android.mk b/trusty/keymaster/Android.mk
deleted file mode 100644
index 0ebf52d..0000000
--- a/trusty/keymaster/Android.mk
+++ /dev/null
@@ -1,67 +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.
-#
-
-# WARNING: Everything listed here will be built on ALL platforms,
-# including x86, the emulator, and the SDK. Modules must be uniquely
-# named (liblights.panda), and must build everywhere, or limit themselves
-# to only building on ARM if they include assembly. Individual makefiles
-# are responsible for having their own logic, for fine-grained control.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-###
-# trusty_keymaster is a binary used only for on-device testing. It
-# runs Trusty Keymaster through a basic set of operations with RSA
-# and ECDSA keys.
-###
-LOCAL_MODULE := trusty_keymaster_tipc
-LOCAL_SRC_FILES := \
- trusty_keymaster_device.cpp \
- trusty_keymaster_ipc.c \
- trusty_keymaster_main.cpp
-LOCAL_SHARED_LIBRARIES := \
- libcrypto \
- libcutils \
- libkeymaster1 \
- libtrusty \
- libkeymaster_messages \
- liblog
-
-include $(BUILD_EXECUTABLE)
-
-###
-# keystore.trusty is the HAL used by keystore on Trusty devices.
-##
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := keystore.trusty
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := module.cpp \
- trusty_keymaster_ipc.c \
- trusty_keymaster_device.cpp
-LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
-LOCAL_SHARED_LIBRARIES := \
- libcrypto \
- libkeymaster_messages \
- libtrusty \
- liblog \
- libcutils
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/keymaster_ipc.h
index 48fa53d..d63757b 100644
--- a/trusty/keymaster/keymaster_ipc.h
+++ b/trusty/keymaster/keymaster_ipc.h
@@ -16,13 +16,16 @@
#pragma once
+// clang-format off
+
#define KEYMASTER_PORT "com.android.trusty.keymaster"
#define KEYMASTER_MAX_BUFFER_LENGTH 4096
// Commands
-enum keymaster_command {
- KEYMASTER_RESP_BIT = 1,
- KEYMASTER_REQ_SHIFT = 1,
+enum keymaster_command : uint32_t {
+ KEYMASTER_RESP_BIT = 1,
+ KEYMASTER_STOP_BIT = 2,
+ KEYMASTER_REQ_SHIFT = 2,
KM_GENERATE_KEY = (0 << KEYMASTER_REQ_SHIFT),
KM_BEGIN_OPERATION = (1 << KEYMASTER_REQ_SHIFT),
@@ -40,6 +43,9 @@
KM_GET_SUPPORTED_IMPORT_FORMATS = (13 << KEYMASTER_REQ_SHIFT),
KM_GET_SUPPORTED_EXPORT_FORMATS = (14 << KEYMASTER_REQ_SHIFT),
KM_GET_KEY_CHARACTERISTICS = (15 << KEYMASTER_REQ_SHIFT),
+ KM_ATTEST_KEY = (16 << KEYMASTER_REQ_SHIFT),
+ KM_UPGRADE_KEY = (17 << KEYMASTER_REQ_SHIFT),
+ KM_CONFIGURE = (18 << KEYMASTER_REQ_SHIFT),
};
#ifdef __ANDROID__
@@ -50,8 +56,8 @@
* @payload: start of the serialized command specific payload
*/
struct keymaster_message {
- uint32_t cmd;
- uint8_t payload[0];
+ uint32_t cmd;
+ uint8_t payload[0];
};
#endif
diff --git a/trusty/keymaster/module.cpp b/trusty/keymaster/module.cpp
index 81597d9..b472680 100644
--- a/trusty/keymaster/module.cpp
+++ b/trusty/keymaster/module.cpp
@@ -26,14 +26,15 @@
/*
* Generic device handling
*/
-static int trusty_keymaster_open(const hw_module_t* module, const char* name,
- hw_device_t** device) {
- if (strcmp(name, KEYSTORE_KEYMASTER) != 0)
+static int trusty_keymaster_open(const hw_module_t* module, const char* name, hw_device_t** device) {
+ if (strcmp(name, KEYSTORE_KEYMASTER) != 0) {
return -EINVAL;
+ }
TrustyKeymasterDevice* dev = new TrustyKeymasterDevice(module);
- if (dev == NULL)
+ if (dev == NULL) {
return -ENOMEM;
+ }
*device = dev->hw_device();
// Do not delete dev; it will get cleaned up when the caller calls device->close(), and must
// exist until then.
@@ -47,14 +48,14 @@
struct keystore_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
.common =
{
- .tag = HARDWARE_MODULE_TAG,
- .module_api_version = KEYMASTER_MODULE_API_VERSION_0_3,
- .hal_api_version = HARDWARE_HAL_API_VERSION,
- .id = KEYSTORE_HARDWARE_MODULE_ID,
- .name = "Trusty Keymaster HAL",
- .author = "The Android Open Source Project",
- .methods = &keystore_module_methods,
- .dso = 0,
- .reserved = {},
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = KEYMASTER_MODULE_API_VERSION_2_0,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = KEYSTORE_HARDWARE_MODULE_ID,
+ .name = "Trusty Keymaster HAL",
+ .author = "The Android Open Source Project",
+ .methods = &keystore_module_methods,
+ .dso = 0,
+ .reserved = {},
},
};
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
index 1368f88..55a03bd 100644
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -25,49 +25,54 @@
#include <string.h>
#include <time.h>
+#include <algorithm>
#include <type_traits>
-#include <hardware/keymaster0.h>
+#include <hardware/keymaster2.h>
#include <keymaster/authorization_set.h>
#include <log/log.h>
+#include "keymaster_ipc.h"
#include "trusty_keymaster_device.h"
#include "trusty_keymaster_ipc.h"
-#include "keymaster_ipc.h"
-const uint32_t SEND_BUF_SIZE = 8192;
-const uint32_t RECV_BUF_SIZE = 8192;
+// Maximum size of message from Trusty is 8K (for RSA attestation key and chain)
+const uint32_t RECV_BUF_SIZE = 2*PAGE_SIZE;
+const uint32_t SEND_BUF_SIZE = (PAGE_SIZE - sizeof(struct keymaster_message) - 16 /* tipc header */);
+
+const size_t kMaximumAttestationChallengeLength = 128;
+const size_t kMaximumFinishInputLength = 2048;
namespace keymaster {
static keymaster_error_t translate_error(int err) {
switch (err) {
- case 0:
- return KM_ERROR_OK;
- case -EPERM:
- case -EACCES:
- return KM_ERROR_SECURE_HW_ACCESS_DENIED;
+ case 0:
+ return KM_ERROR_OK;
+ case -EPERM:
+ case -EACCES:
+ return KM_ERROR_SECURE_HW_ACCESS_DENIED;
- case -ECANCELED:
- return KM_ERROR_OPERATION_CANCELLED;
+ case -ECANCELED:
+ return KM_ERROR_OPERATION_CANCELLED;
- case -ENODEV:
- return KM_ERROR_UNIMPLEMENTED;
+ case -ENODEV:
+ return KM_ERROR_UNIMPLEMENTED;
- case -ENOMEM:
- return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ case -ENOMEM:
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
- case -EBUSY:
- return KM_ERROR_SECURE_HW_BUSY;
+ case -EBUSY:
+ return KM_ERROR_SECURE_HW_BUSY;
- case -EIO:
- return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ case -EIO:
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
- case -EOVERFLOW:
- return KM_ERROR_INVALID_INPUT_LENGTH;
+ case -EOVERFLOW:
+ return KM_ERROR_INVALID_INPUT_LENGTH;
- default:
- return KM_ERROR_UNKNOWN_ERROR;
+ default:
+ return KM_ERROR_UNKNOWN_ERROR;
}
}
@@ -75,31 +80,36 @@
static_assert(std::is_standard_layout<TrustyKeymasterDevice>::value,
"TrustyKeymasterDevice must be standard layout");
static_assert(offsetof(TrustyKeymasterDevice, device_) == 0,
- "device_ must be the first member of KeymasterOpenSsl");
+ "device_ must be the first member of TrustyKeymasterDevice");
static_assert(offsetof(TrustyKeymasterDevice, device_.common) == 0,
- "common must be the first member of keymaster_device");
+ "common must be the first member of keymaster2_device");
ALOGI("Creating device");
ALOGD("Device address: %p", this);
- memset(&device_, 0, sizeof(device_));
+ device_ = {};
device_.common.tag = HARDWARE_DEVICE_TAG;
device_.common.version = 1;
device_.common.module = const_cast<hw_module_t*>(module);
device_.common.close = close_device;
- device_.flags = KEYMASTER_BLOBS_ARE_STANDALONE | KEYMASTER_SUPPORTS_EC;
+ device_.flags = KEYMASTER_SUPPORTS_EC;
- device_.generate_keypair = generate_keypair;
- device_.import_keypair = import_keypair;
- device_.get_keypair_public = get_keypair_public;
- device_.delete_keypair = NULL;
- device_.delete_all = NULL;
- device_.sign_data = sign_data;
- device_.verify_data = verify_data;
-
- device_.context = NULL;
+ device_.configure = configure;
+ device_.add_rng_entropy = add_rng_entropy;
+ device_.generate_key = generate_key;
+ device_.get_key_characteristics = get_key_characteristics;
+ device_.import_key = import_key;
+ device_.export_key = export_key;
+ device_.attest_key = attest_key;
+ device_.upgrade_key = upgrade_key;
+ device_.delete_key = nullptr;
+ device_.delete_all_keys = nullptr;
+ device_.begin = begin;
+ device_.update = update;
+ device_.finish = finish;
+ device_.abort = abort;
int rc = trusty_keymaster_connect();
error_ = translate_error(rc);
@@ -110,11 +120,11 @@
GetVersionRequest version_request;
GetVersionResponse version_response;
- error_ = Send(version_request, &version_response);
+ error_ = Send(KM_GET_VERSION, version_request, &version_response);
if (error_ == KM_ERROR_INVALID_ARGUMENT || error_ == KM_ERROR_UNIMPLEMENTED) {
- ALOGI("\"Bad parameters\" error on GetVersion call. Assuming version 0.");
- message_version_ = 0;
- error_ = KM_ERROR_OK;
+ ALOGE("\"Bad parameters\" error on GetVersion call. Version 0 is not supported.");
+ error_ = KM_ERROR_VERSION_MISMATCH;
+ return;
}
message_version_ = MessageVersion(version_response.major_ver, version_response.minor_ver,
version_response.subminor_ver);
@@ -130,261 +140,512 @@
trusty_keymaster_disconnect();
}
-const uint64_t HUNDRED_YEARS = 1000LL * 60 * 60 * 24 * 365 * 100;
+namespace {
-int TrustyKeymasterDevice::generate_keypair(const keymaster_keypair_t key_type,
- const void* key_params, uint8_t** key_blob,
- size_t* key_blob_length) {
- ALOGD("Device received generate_keypair");
+// Allocates a new buffer with malloc and copies the contents of |buffer| to it. Caller takes
+// ownership of the returned buffer.
+uint8_t* DuplicateBuffer(const uint8_t* buffer, size_t size) {
+ uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(size));
+ if (tmp) {
+ memcpy(tmp, buffer, size);
+ }
+ return tmp;
+}
- if (error_ != KM_ERROR_OK)
+template <typename RequestType>
+void AddClientAndAppData(const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+ RequestType* request) {
+ request->additional_params.Clear();
+ if (client_id) {
+ request->additional_params.push_back(TAG_APPLICATION_ID, *client_id);
+ }
+ if (app_data) {
+ request->additional_params.push_back(TAG_APPLICATION_DATA, *app_data);
+ }
+}
+
+} // unnamed namespace
+
+keymaster_error_t TrustyKeymasterDevice::configure(const keymaster_key_param_set_t* params) {
+ ALOGD("Device received configure\n");
+
+ if (error_ != KM_ERROR_OK) {
return error_;
-
- GenerateKeyRequest req(message_version_);
- StoreNewKeyParams(&req.key_description);
-
- switch (key_type) {
- case TYPE_RSA: {
- req.key_description.push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA);
- const keymaster_rsa_keygen_params_t* rsa_params =
- static_cast<const keymaster_rsa_keygen_params_t*>(key_params);
- ALOGD("Generating RSA pair, modulus size: %u, public exponent: %lu",
- rsa_params->modulus_size, rsa_params->public_exponent);
- req.key_description.push_back(TAG_KEY_SIZE, rsa_params->modulus_size);
- req.key_description.push_back(TAG_RSA_PUBLIC_EXPONENT, rsa_params->public_exponent);
- break;
+ }
+ if (!params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
}
- case TYPE_EC: {
- req.key_description.push_back(TAG_ALGORITHM, KM_ALGORITHM_EC);
- const keymaster_ec_keygen_params_t* ec_params =
- static_cast<const keymaster_ec_keygen_params_t*>(key_params);
- ALOGD("Generating ECDSA pair, key size: %u", ec_params->field_size);
- req.key_description.push_back(TAG_KEY_SIZE, ec_params->field_size);
- break;
- }
- default:
- ALOGD("Received request for unsuported key type %d", key_type);
- return KM_ERROR_UNSUPPORTED_ALGORITHM;
+ AuthorizationSet params_copy(*params);
+ ConfigureRequest request(message_version_);
+ if (!params_copy.GetTagValue(TAG_OS_VERSION, &request.os_version) ||
+ !params_copy.GetTagValue(TAG_OS_PATCHLEVEL, &request.os_patchlevel)) {
+ ALOGD("Configuration parameters must contain OS version and patch level");
+ return KM_ERROR_INVALID_ARGUMENT;
}
- GenerateKeyResponse rsp(message_version_);
- ALOGD("Sending generate request");
- keymaster_error_t err = Send(req, &rsp);
+ ConfigureResponse response(message_version_);
+ keymaster_error_t err = Send(KM_CONFIGURE, request, &response);
if (err != KM_ERROR_OK) {
- ALOGE("Got error %d from send", err);
return err;
}
- *key_blob_length = rsp.key_blob.key_material_size;
- *key_blob = static_cast<uint8_t*>(malloc(*key_blob_length));
- memcpy(*key_blob, rsp.key_blob.key_material, *key_blob_length);
- ALOGD("Returning %d bytes in key blob\n", (int)*key_blob_length);
-
return KM_ERROR_OK;
}
-struct EVP_PKEY_Delete {
- void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
-};
+keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const uint8_t* data, size_t data_length) {
+ ALOGD("Device received add_rng_entropy");
-struct PKCS8_PRIV_KEY_INFO_Delete {
- void operator()(PKCS8_PRIV_KEY_INFO* p) const { PKCS8_PRIV_KEY_INFO_free(p); }
-};
-
-int TrustyKeymasterDevice::import_keypair(const uint8_t* key, const size_t key_length,
- uint8_t** key_blob, size_t* key_blob_length) {
- ALOGD("Device received import_keypair");
- if (error_ != KM_ERROR_OK)
+ if (error_ != KM_ERROR_OK) {
return error_;
+ }
- if (!key)
+ AddEntropyRequest request(message_version_);
+ request.random_data.Reinitialize(data, data_length);
+ AddEntropyResponse response(message_version_);
+ return Send(KM_ADD_RNG_ENTROPY, request, &response);
+}
+
+keymaster_error_t TrustyKeymasterDevice::generate_key(
+ const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics) {
+ ALOGD("Device received generate_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!params) {
return KM_ERROR_UNEXPECTED_NULL_POINTER;
-
- if (!key_blob || !key_blob_length)
+ }
+ if (!key_blob) {
return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ GenerateKeyRequest request(message_version_);
+ request.key_description.Reinitialize(*params);
+ request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+
+ GenerateKeyResponse response(message_version_);
+ keymaster_error_t err = Send(KM_GENERATE_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ key_blob->key_material_size = response.key_blob.key_material_size;
+ key_blob->key_material =
+ DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+ if (!key_blob->key_material) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ if (characteristics) {
+ response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+ response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
+ const keymaster_key_blob_t* key_blob, const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data, keymaster_key_characteristics_t* characteristics) {
+ ALOGD("Device received get_key_characteristics");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key_blob || !key_blob->key_material) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!characteristics) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ GetKeyCharacteristicsRequest request(message_version_);
+ request.SetKeyMaterial(*key_blob);
+ AddClientAndAppData(client_id, app_data, &request);
+
+ GetKeyCharacteristicsResponse response(message_version_);
+ keymaster_error_t err = Send(KM_GET_KEY_CHARACTERISTICS, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+ response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::import_key(
+ const keymaster_key_param_set_t* params, keymaster_key_format_t key_format,
+ const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics) {
+ ALOGD("Device received import_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!params || !key_data) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!key_blob) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
ImportKeyRequest request(message_version_);
- StoreNewKeyParams(&request.key_description);
- keymaster_algorithm_t algorithm;
- keymaster_error_t err = GetPkcs8KeyAlgorithm(key, key_length, &algorithm);
- if (err != KM_ERROR_OK)
- return err;
- request.key_description.push_back(TAG_ALGORITHM, algorithm);
+ request.key_description.Reinitialize(*params);
+ request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
- request.SetKeyMaterial(key, key_length);
- request.key_format = KM_KEY_FORMAT_PKCS8;
+ request.key_format = key_format;
+ request.SetKeyMaterial(key_data->data, key_data->data_length);
+
ImportKeyResponse response(message_version_);
- err = Send(request, &response);
- if (err != KM_ERROR_OK)
+ keymaster_error_t err = Send(KM_IMPORT_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
return err;
+ }
- *key_blob_length = response.key_blob.key_material_size;
- *key_blob = static_cast<uint8_t*>(malloc(*key_blob_length));
- memcpy(*key_blob, response.key_blob.key_material, *key_blob_length);
- printf("Returning %d bytes in key blob\n", (int)*key_blob_length);
+ key_blob->key_material_size = response.key_blob.key_material_size;
+ key_blob->key_material =
+ DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+ if (!key_blob->key_material) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ if (characteristics) {
+ response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+ response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+ }
return KM_ERROR_OK;
}
-keymaster_error_t TrustyKeymasterDevice::GetPkcs8KeyAlgorithm(const uint8_t* key, size_t key_length,
- keymaster_algorithm_t* algorithm) {
- if (key == NULL) {
- ALOGE("No key specified for import");
+keymaster_error_t TrustyKeymasterDevice::export_key(keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_blob_t* export_data) {
+ ALOGD("Device received export_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key_to_export || !key_to_export->key_material) {
return KM_ERROR_UNEXPECTED_NULL_POINTER;
}
-
- UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> pkcs8(
- d2i_PKCS8_PRIV_KEY_INFO(NULL, &key, key_length));
- if (pkcs8.get() == NULL) {
- ALOGE("Could not parse PKCS8 key blob");
- return KM_ERROR_INVALID_KEY_BLOB;
+ if (!export_data) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
}
- UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKCS82PKEY(pkcs8.get()));
- if (pkey.get() == NULL) {
- ALOGE("Could not extract key from PKCS8 key blob");
- return KM_ERROR_INVALID_KEY_BLOB;
- }
-
- switch (EVP_PKEY_type(pkey->type)) {
- case EVP_PKEY_RSA:
- *algorithm = KM_ALGORITHM_RSA;
- break;
- case EVP_PKEY_EC:
- *algorithm = KM_ALGORITHM_EC;
- break;
- default:
- ALOGE("Unsupported algorithm %d", EVP_PKEY_type(pkey->type));
- return KM_ERROR_UNSUPPORTED_ALGORITHM;
- }
-
- return KM_ERROR_OK;
-}
-
-int TrustyKeymasterDevice::get_keypair_public(const uint8_t* key_blob, const size_t key_blob_length,
- uint8_t** x509_data, size_t* x509_data_length) {
- ALOGD("Device received get_keypair_public");
- if (error_ != KM_ERROR_OK)
- return error_;
+ export_data->data = nullptr;
+ export_data->data_length = 0;
ExportKeyRequest request(message_version_);
- request.SetKeyMaterial(key_blob, key_blob_length);
- request.key_format = KM_KEY_FORMAT_X509;
+ request.key_format = export_format;
+ request.SetKeyMaterial(*key_to_export);
+ AddClientAndAppData(client_id, app_data, &request);
+
ExportKeyResponse response(message_version_);
- keymaster_error_t err = Send(request, &response);
- if (err != KM_ERROR_OK)
+ keymaster_error_t err = Send(KM_EXPORT_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
return err;
+ }
- *x509_data_length = response.key_data_length;
- *x509_data = static_cast<uint8_t*>(malloc(*x509_data_length));
- memcpy(*x509_data, response.key_data, *x509_data_length);
- printf("Returning %d bytes in x509 key\n", (int)*x509_data_length);
+ export_data->data_length = response.key_data_length;
+ export_data->data = DuplicateBuffer(response.key_data, response.key_data_length);
+ if (!export_data->data) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
return KM_ERROR_OK;
}
-int TrustyKeymasterDevice::sign_data(const void* signing_params, const uint8_t* key_blob,
- const size_t key_blob_length, const uint8_t* data,
- const size_t data_length, uint8_t** signed_data,
- size_t* signed_data_length) {
- ALOGD("Device received sign_data, %d", error_);
- if (error_ != KM_ERROR_OK)
+keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain) {
+ ALOGD("Device received attest_key");
+
+ if (error_ != KM_ERROR_OK) {
return error_;
+ }
+ if (!key_to_attest || !attest_params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!cert_chain) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
- BeginOperationRequest begin_request(message_version_);
- begin_request.purpose = KM_PURPOSE_SIGN;
- begin_request.SetKeyMaterial(key_blob, key_blob_length);
- keymaster_error_t err = StoreSigningParams(signing_params, key_blob, key_blob_length,
- &begin_request.additional_params);
+ cert_chain->entry_count = 0;
+ cert_chain->entries = nullptr;
+
+ AttestKeyRequest request(message_version_);
+ request.SetKeyMaterial(*key_to_attest);
+ request.attest_params.Reinitialize(*attest_params);
+
+ keymaster_blob_t attestation_challenge = {};
+ request.attest_params.GetTagValue(TAG_ATTESTATION_CHALLENGE, &attestation_challenge);
+ if (attestation_challenge.data_length > kMaximumAttestationChallengeLength) {
+ ALOGE("%zu-byte attestation challenge; only %zu bytes allowed",
+ attestation_challenge.data_length, kMaximumAttestationChallengeLength);
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ }
+
+ AttestKeyResponse response(message_version_);
+ keymaster_error_t err = Send(KM_ATTEST_KEY, request, &response);
if (err != KM_ERROR_OK) {
- ALOGE("Error extracting signing params: %d", err);
return err;
}
- BeginOperationResponse begin_response(message_version_);
- ALOGD("Sending signing request begin");
- err = Send(begin_request, &begin_response);
- if (err != KM_ERROR_OK) {
- ALOGE("Error sending sign begin: %d", err);
- return err;
+ // Allocate and clear storage for cert_chain.
+ keymaster_cert_chain_t& rsp_chain = response.certificate_chain;
+ cert_chain->entries = reinterpret_cast<keymaster_blob_t*>(
+ malloc(rsp_chain.entry_count * sizeof(*cert_chain->entries)));
+ if (!cert_chain->entries) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ cert_chain->entry_count = rsp_chain.entry_count;
+ for (keymaster_blob_t& entry : array_range(cert_chain->entries, cert_chain->entry_count)) {
+ entry = {};
}
- UpdateOperationRequest update_request(message_version_);
- update_request.op_handle = begin_response.op_handle;
- update_request.input.Reinitialize(data, data_length);
- UpdateOperationResponse update_response(message_version_);
- ALOGD("Sending signing request update");
- err = Send(update_request, &update_response);
- if (err != KM_ERROR_OK) {
- ALOGE("Error sending sign update: %d", err);
- return err;
+ // Copy cert_chain contents
+ size_t i = 0;
+ for (keymaster_blob_t& entry : array_range(rsp_chain.entries, rsp_chain.entry_count)) {
+ cert_chain->entries[i].data = DuplicateBuffer(entry.data, entry.data_length);
+ if (!cert_chain->entries[i].data) {
+ keymaster_free_cert_chain(cert_chain);
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ cert_chain->entries[i].data_length = entry.data_length;
+ ++i;
}
- FinishOperationRequest finish_request(message_version_);
- finish_request.op_handle = begin_response.op_handle;
- FinishOperationResponse finish_response(message_version_);
- ALOGD("Sending signing request finish");
- err = Send(finish_request, &finish_response);
- if (err != KM_ERROR_OK) {
- ALOGE("Error sending sign finish: %d", err);
- return err;
- }
-
- *signed_data_length = finish_response.output.available_read();
- *signed_data = static_cast<uint8_t*>(malloc(*signed_data_length));
- if (!finish_response.output.read(*signed_data, *signed_data_length)) {
- ALOGE("Error reading response data: %d", err);
- return KM_ERROR_UNKNOWN_ERROR;
- }
return KM_ERROR_OK;
}
-int TrustyKeymasterDevice::verify_data(const void* signing_params, const uint8_t* key_blob,
- const size_t key_blob_length, const uint8_t* signed_data,
- const size_t signed_data_length, const uint8_t* signature,
- const size_t signature_length) {
- ALOGD("Device received verify_data");
- if (error_ != KM_ERROR_OK)
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
+ const keymaster_key_param_set_t* upgrade_params,
+ keymaster_key_blob_t* upgraded_key) {
+ ALOGD("Device received upgrade_key");
+
+ if (error_ != KM_ERROR_OK) {
return error_;
+ }
+ if (!key_to_upgrade || !upgrade_params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!upgraded_key) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
- BeginOperationRequest begin_request(message_version_);
- begin_request.purpose = KM_PURPOSE_VERIFY;
- begin_request.SetKeyMaterial(key_blob, key_blob_length);
- keymaster_error_t err = StoreSigningParams(signing_params, key_blob, key_blob_length,
- &begin_request.additional_params);
- if (err != KM_ERROR_OK)
- return err;
+ UpgradeKeyRequest request(message_version_);
+ request.SetKeyMaterial(*key_to_upgrade);
+ request.upgrade_params.Reinitialize(*upgrade_params);
- BeginOperationResponse begin_response(message_version_);
- err = Send(begin_request, &begin_response);
- if (err != KM_ERROR_OK)
+ UpgradeKeyResponse response(message_version_);
+ keymaster_error_t err = Send(KM_UPGRADE_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
return err;
+ }
- UpdateOperationRequest update_request(message_version_);
- update_request.op_handle = begin_response.op_handle;
- update_request.input.Reinitialize(signed_data, signed_data_length);
- UpdateOperationResponse update_response(message_version_);
- err = Send(update_request, &update_response);
- if (err != KM_ERROR_OK)
- return err;
+ upgraded_key->key_material_size = response.upgraded_key.key_material_size;
+ upgraded_key->key_material = DuplicateBuffer(response.upgraded_key.key_material,
+ response.upgraded_key.key_material_size);
+ if (!upgraded_key->key_material) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
- FinishOperationRequest finish_request(message_version_);
- finish_request.op_handle = begin_response.op_handle;
- finish_request.signature.Reinitialize(signature, signature_length);
- FinishOperationResponse finish_response(message_version_);
- err = Send(finish_request, &finish_response);
- if (err != KM_ERROR_OK)
- return err;
return KM_ERROR_OK;
}
+keymaster_error_t TrustyKeymasterDevice::begin(keymaster_purpose_t purpose,
+ const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle) {
+ ALOGD("Device received begin");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key || !key->key_material) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!operation_handle) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ if (out_params) {
+ *out_params = {};
+ }
+
+ BeginOperationRequest request(message_version_);
+ request.purpose = purpose;
+ request.SetKeyMaterial(*key);
+ request.additional_params.Reinitialize(*in_params);
+
+ BeginOperationResponse response(message_version_);
+ keymaster_error_t err = Send(KM_BEGIN_OPERATION, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ if (response.output_params.size() > 0) {
+ if (out_params) {
+ response.output_params.CopyToParamSet(out_params);
+ } else {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+ }
+ *operation_handle = response.op_handle;
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::update(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input,
+ size_t* input_consumed,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ ALOGD("Device received update");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!input) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!input_consumed) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ if (out_params) {
+ *out_params = {};
+ }
+ if (output) {
+ *output = {};
+ }
+
+ UpdateOperationRequest request(message_version_);
+ request.op_handle = operation_handle;
+ if (in_params) {
+ request.additional_params.Reinitialize(*in_params);
+ }
+ if (input && input->data_length > 0) {
+ size_t max_input_size = SEND_BUF_SIZE - request.SerializedSize();
+ request.input.Reinitialize(input->data, std::min(input->data_length, max_input_size));
+ }
+
+ UpdateOperationResponse response(message_version_);
+ keymaster_error_t err = Send(KM_UPDATE_OPERATION, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ if (response.output_params.size() > 0) {
+ if (out_params) {
+ response.output_params.CopyToParamSet(out_params);
+ } else {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+ }
+ *input_consumed = response.input_consumed;
+ if (output) {
+ output->data_length = response.output.available_read();
+ output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+ if (!output->data) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ } else if (response.output.available_read() > 0) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::finish(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ ALOGD("Device received finish");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (input && input->data_length > kMaximumFinishInputLength) {
+ ALOGE("%zu-byte input to finish; only %zu bytes allowed",
+ input->data_length, kMaximumFinishInputLength);
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ }
+
+ if (out_params) {
+ *out_params = {};
+ }
+ if (output) {
+ *output = {};
+ }
+
+ FinishOperationRequest request(message_version_);
+ request.op_handle = operation_handle;
+ if (signature && signature->data && signature->data_length > 0) {
+ request.signature.Reinitialize(signature->data, signature->data_length);
+ }
+ if (input && input->data && input->data_length) {
+ request.input.Reinitialize(input->data, input->data_length);
+ }
+ if (in_params) {
+ request.additional_params.Reinitialize(*in_params);
+ }
+
+ FinishOperationResponse response(message_version_);
+ keymaster_error_t err = Send(KM_FINISH_OPERATION, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ if (response.output_params.size() > 0) {
+ if (out_params) {
+ response.output_params.CopyToParamSet(out_params);
+ } else {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+ }
+ if (output) {
+ output->data_length = response.output.available_read();
+ output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+ if (!output->data) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ } else if (response.output.available_read() > 0) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::abort(keymaster_operation_handle_t operation_handle) {
+ ALOGD("Device received abort");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+
+ AbortOperationRequest request(message_version_);
+ request.op_handle = operation_handle;
+ AbortOperationResponse response(message_version_);
+ return Send(KM_ABORT_OPERATION, request, &response);
+}
+
hw_device_t* TrustyKeymasterDevice::hw_device() {
return &device_.common;
}
-static inline TrustyKeymasterDevice* convert_device(const keymaster0_device_t* dev) {
- return reinterpret_cast<TrustyKeymasterDevice*>(const_cast<keymaster0_device_t*>(dev));
+static inline TrustyKeymasterDevice* convert_device(const keymaster2_device_t* dev) {
+ return reinterpret_cast<TrustyKeymasterDevice*>(const_cast<keymaster2_device_t*>(dev));
}
/* static */
@@ -394,52 +655,111 @@
}
/* static */
-int TrustyKeymasterDevice::generate_keypair(const keymaster0_device_t* dev,
- const keymaster_keypair_t key_type,
- const void* key_params, uint8_t** keyBlob,
- size_t* keyBlobLength) {
- ALOGD("Generate keypair, sending to device: %p", convert_device(dev));
- return convert_device(dev)->generate_keypair(key_type, key_params, keyBlob, keyBlobLength);
+keymaster_error_t TrustyKeymasterDevice::configure(const keymaster2_device_t* dev,
+ const keymaster_key_param_set_t* params) {
+ return convert_device(dev)->configure(params);
}
/* static */
-int TrustyKeymasterDevice::import_keypair(const keymaster0_device_t* dev, const uint8_t* key,
- const size_t key_length, uint8_t** key_blob,
- size_t* key_blob_length) {
- return convert_device(dev)->import_keypair(key, key_length, key_blob, key_blob_length);
+keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const keymaster2_device_t* dev,
+ const uint8_t* data, size_t data_length) {
+ return convert_device(dev)->add_rng_entropy(data, data_length);
}
/* static */
-int TrustyKeymasterDevice::get_keypair_public(const keymaster0_device_t* dev,
- const uint8_t* key_blob, const size_t key_blob_length,
- uint8_t** x509_data, size_t* x509_data_length) {
- return convert_device(dev)
- ->get_keypair_public(key_blob, key_blob_length, x509_data, x509_data_length);
+keymaster_error_t TrustyKeymasterDevice::generate_key(
+ const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+ return convert_device(dev)->generate_key(params, key_blob, characteristics);
}
/* static */
-int TrustyKeymasterDevice::sign_data(const keymaster0_device_t* dev, const void* params,
- const uint8_t* keyBlob, const size_t keyBlobLength,
- const uint8_t* data, const size_t dataLength,
- uint8_t** signedData, size_t* signedDataLength) {
- return convert_device(dev)
- ->sign_data(params, keyBlob, keyBlobLength, data, dataLength, signedData, signedDataLength);
+keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
+ const keymaster2_device_t* dev, const keymaster_key_blob_t* key_blob,
+ const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+ keymaster_key_characteristics_t* characteristics) {
+ return convert_device(dev)->get_key_characteristics(key_blob, client_id, app_data,
+ characteristics);
}
/* static */
-int TrustyKeymasterDevice::verify_data(const keymaster0_device_t* dev, const void* params,
- const uint8_t* keyBlob, const size_t keyBlobLength,
- const uint8_t* signedData, const size_t signedDataLength,
- const uint8_t* signature, const size_t signatureLength) {
- return convert_device(dev)->verify_data(params, keyBlob, keyBlobLength, signedData,
- signedDataLength, signature, signatureLength);
+keymaster_error_t TrustyKeymasterDevice::import_key(
+ const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+ keymaster_key_format_t key_format, const keymaster_blob_t* key_data,
+ keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+ return convert_device(dev)->import_key(params, key_format, key_data, key_blob, characteristics);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::export_key(const keymaster2_device_t* dev,
+ keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_blob_t* export_data) {
+ return convert_device(dev)->export_key(export_format, key_to_export, client_id, app_data,
+ export_data);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain) {
+ return convert_device(dev)->attest_key(key_to_attest, attest_params, cert_chain);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_to_upgrade,
+ const keymaster_key_param_set_t* upgrade_params,
+ keymaster_key_blob_t* upgraded_key) {
+ return convert_device(dev)->upgrade_key(key_to_upgrade, upgrade_params, upgraded_key);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::begin(const keymaster2_device_t* dev,
+ keymaster_purpose_t purpose,
+ const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle) {
+ return convert_device(dev)->begin(purpose, key, in_params, out_params, operation_handle);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::update(
+ const keymaster2_device_t* dev, keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params, const keymaster_blob_t* input,
+ size_t* input_consumed, keymaster_key_param_set_t* out_params, keymaster_blob_t* output) {
+ return convert_device(dev)->update(operation_handle, in_params, input, input_consumed,
+ out_params, output);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::finish(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ return convert_device(dev)->finish(operation_handle, in_params, input, signature, out_params,
+ output);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::abort(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle) {
+ return convert_device(dev)->abort(operation_handle);
}
keymaster_error_t TrustyKeymasterDevice::Send(uint32_t command, const Serializable& req,
KeymasterResponse* rsp) {
uint32_t req_size = req.SerializedSize();
- if (req_size > SEND_BUF_SIZE)
+ if (req_size > SEND_BUF_SIZE) {
return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
uint8_t send_buf[SEND_BUF_SIZE];
Eraser send_buf_eraser(send_buf, SEND_BUF_SIZE);
req.Serialize(send_buf, send_buf + req_size);
@@ -448,9 +768,12 @@
uint8_t recv_buf[RECV_BUF_SIZE];
Eraser recv_buf_eraser(recv_buf, RECV_BUF_SIZE);
uint32_t rsp_size = RECV_BUF_SIZE;
- printf("Sending %d byte request\n", (int)req.SerializedSize());
+ ALOGV("Sending %d byte request\n", (int)req.SerializedSize());
int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
if (rc < 0) {
+ // Reset the connection on tipc error
+ trusty_keymaster_disconnect();
+ trusty_keymaster_connect();
ALOGE("tipc error: %d\n", rc);
// TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
return translate_error(rc);
@@ -458,8 +781,7 @@
ALOGV("Received %d byte response\n", rsp_size);
}
- const keymaster_message* msg = (keymaster_message *) recv_buf;
- const uint8_t *p = msg->payload;
+ const uint8_t* p = recv_buf;
if (!rsp->Deserialize(&p, p + rsp_size)) {
ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
return KM_ERROR_UNKNOWN_ERROR;
@@ -470,65 +792,4 @@
return rsp->error;
}
-keymaster_error_t TrustyKeymasterDevice::StoreSigningParams(const void* signing_params,
- const uint8_t* key_blob,
- size_t key_blob_length,
- AuthorizationSet* auth_set) {
- uint8_t* pub_key_data;
- size_t pub_key_data_length;
- int err = get_keypair_public(&device_, key_blob, key_blob_length, &pub_key_data,
- &pub_key_data_length);
- if (err < 0) {
- ALOGE("Error %d extracting public key to determine algorithm", err);
- return KM_ERROR_INVALID_KEY_BLOB;
- }
- UniquePtr<uint8_t, Malloc_Delete> pub_key(pub_key_data);
-
- const uint8_t* p = pub_key_data;
- UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(
- d2i_PUBKEY(nullptr /* allocate new struct */, &p, pub_key_data_length));
-
- switch (EVP_PKEY_type(pkey->type)) {
- case EVP_PKEY_RSA: {
- const keymaster_rsa_sign_params_t* rsa_params =
- reinterpret_cast<const keymaster_rsa_sign_params_t*>(signing_params);
- if (rsa_params->digest_type != DIGEST_NONE)
- return KM_ERROR_UNSUPPORTED_DIGEST;
- if (rsa_params->padding_type != PADDING_NONE)
- return KM_ERROR_UNSUPPORTED_PADDING_MODE;
- if (!auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE) ||
- !auth_set->push_back(TAG_PADDING, KM_PAD_NONE))
- return KM_ERROR_MEMORY_ALLOCATION_FAILED;
- } break;
- case EVP_PKEY_EC: {
- const keymaster_ec_sign_params_t* ecdsa_params =
- reinterpret_cast<const keymaster_ec_sign_params_t*>(signing_params);
- if (ecdsa_params->digest_type != DIGEST_NONE)
- return KM_ERROR_UNSUPPORTED_DIGEST;
- if (!auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE))
- return KM_ERROR_MEMORY_ALLOCATION_FAILED;
- } break;
- default:
- return KM_ERROR_UNSUPPORTED_ALGORITHM;
- }
- return KM_ERROR_OK;
-}
-
-void TrustyKeymasterDevice::StoreNewKeyParams(AuthorizationSet* auth_set) {
- auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_SIGN);
- auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_VERIFY);
- auth_set->push_back(TAG_ALL_USERS);
- auth_set->push_back(TAG_NO_AUTH_REQUIRED);
- uint64_t now = java_time(time(NULL));
- auth_set->push_back(TAG_CREATION_DATETIME, now);
- auth_set->push_back(TAG_ORIGINATION_EXPIRE_DATETIME, now + HUNDRED_YEARS);
- if (message_version_ == 0) {
- auth_set->push_back(TAG_DIGEST_OLD, KM_DIGEST_NONE);
- auth_set->push_back(TAG_PADDING_OLD, KM_PAD_NONE);
- } else {
- auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE);
- auth_set->push_back(TAG_PADDING, KM_PAD_NONE);
- }
-}
-
} // namespace keymaster
diff --git a/trusty/keymaster/trusty_keymaster_device.h b/trusty/keymaster/trusty_keymaster_device.h
index 68cf40c..cfada1b 100644
--- a/trusty/keymaster/trusty_keymaster_device.h
+++ b/trusty/keymaster/trusty_keymaster_device.h
@@ -14,19 +14,16 @@
* limitations under the License.
*/
-#ifndef EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
-#define EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#ifndef TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
-#include <hardware/keymaster0.h>
-
+#include <hardware/keymaster2.h>
#include <keymaster/android_keymaster_messages.h>
-#include "keymaster_ipc.h"
-
namespace keymaster {
/**
- * Software OpenSSL-based Keymaster device.
+ * Trusty Keymaster device.
*
* IMPORTANT MAINTAINER NOTE: Pointers to instances of this class must be castable to hw_device_t
* and keymaster_device. This means it must remain a standard layout class (no virtual functions and
@@ -46,79 +43,111 @@
keymaster_error_t session_error() { return error_; }
- int generate_keypair(const keymaster_keypair_t key_type, const void* key_params,
- uint8_t** key_blob, size_t* key_blob_length);
- int import_keypair(const uint8_t* key, const size_t key_length, uint8_t** key_blob,
- size_t* key_blob_length);
- int get_keypair_public(const uint8_t* key_blob, const size_t key_blob_length,
- uint8_t** x509_data, size_t* x509_data_length);
- int sign_data(const void* signing_params, const uint8_t* key_blob, const size_t key_blob_length,
- const uint8_t* data, const size_t data_length, uint8_t** signed_data,
- size_t* signed_data_length);
- int verify_data(const void* signing_params, const uint8_t* key_blob,
- const size_t key_blob_length, const uint8_t* signed_data,
- const size_t signed_data_length, const uint8_t* signature,
- const size_t signature_length);
+ keymaster_error_t configure(const keymaster_key_param_set_t* params);
+ keymaster_error_t add_rng_entropy(const uint8_t* data, size_t data_length);
+ keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics);
+ keymaster_error_t get_key_characteristics(const keymaster_key_blob_t* key_blob,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_key_characteristics_t* character);
+ keymaster_error_t import_key(const keymaster_key_param_set_t* params,
+ keymaster_key_format_t key_format,
+ const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics);
+ keymaster_error_t export_key(keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data, keymaster_blob_t* export_data);
+ keymaster_error_t attest_key(const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain);
+ keymaster_error_t upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
+ const keymaster_key_param_set_t* upgrade_params,
+ keymaster_key_blob_t* upgraded_key);
+ keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle);
+ keymaster_error_t update(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, size_t* input_consumed,
+ keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+ keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+ keymaster_error_t abort(keymaster_operation_handle_t operation_handle);
private:
keymaster_error_t Send(uint32_t command, const Serializable& request,
KeymasterResponse* response);
- keymaster_error_t Send(const GenerateKeyRequest& request, GenerateKeyResponse* response) {
- return Send(KM_GENERATE_KEY, request, response);
- }
- keymaster_error_t Send(const BeginOperationRequest& request, BeginOperationResponse* response) {
- return Send(KM_BEGIN_OPERATION, request, response);
- }
- keymaster_error_t Send(const UpdateOperationRequest& request,
- UpdateOperationResponse* response) {
- return Send(KM_UPDATE_OPERATION, request, response);
- }
- keymaster_error_t Send(const FinishOperationRequest& request,
- FinishOperationResponse* response) {
- return Send(KM_FINISH_OPERATION, request, response);
- }
- keymaster_error_t Send(const ImportKeyRequest& request, ImportKeyResponse* response) {
- return Send(KM_IMPORT_KEY, request, response);
- }
- keymaster_error_t Send(const ExportKeyRequest& request, ExportKeyResponse* response) {
- return Send(KM_EXPORT_KEY, request, response);
- }
- keymaster_error_t Send(const GetVersionRequest& request, GetVersionResponse* response) {
- return Send(KM_GET_VERSION, request, response);
- }
-
- keymaster_error_t StoreSigningParams(const void* signing_params, const uint8_t* key_blob,
- size_t key_blob_length, AuthorizationSet* auth_set);
- void StoreNewKeyParams(AuthorizationSet* auth_set);
- keymaster_error_t GetPkcs8KeyAlgorithm(const uint8_t* key, size_t key_length,
- keymaster_algorithm_t* algorithm);
/*
* These static methods are the functions referenced through the function pointers in
* keymaster_device. They're all trivial wrappers.
*/
static int close_device(hw_device_t* dev);
- static int generate_keypair(const keymaster0_device_t* dev, const keymaster_keypair_t key_type,
- const void* key_params, uint8_t** keyBlob, size_t* keyBlobLength);
- static int import_keypair(const keymaster0_device_t* dev, const uint8_t* key,
- const size_t key_length, uint8_t** key_blob, size_t* key_blob_length);
- static int get_keypair_public(const keymaster0_device_t* dev, const uint8_t* key_blob,
- const size_t key_blob_length, uint8_t** x509_data,
- size_t* x509_data_length);
- static int sign_data(const keymaster0_device_t* dev, const void* signing_params,
- const uint8_t* key_blob, const size_t key_blob_length, const uint8_t* data,
- const size_t data_length, uint8_t** signed_data,
- size_t* signed_data_length);
- static int verify_data(const keymaster0_device_t* dev, const void* signing_params,
- const uint8_t* key_blob, const size_t key_blob_length,
- const uint8_t* signed_data, const size_t signed_data_length,
- const uint8_t* signature, const size_t signature_length);
+ static keymaster_error_t configure(const keymaster2_device_t* dev,
+ const keymaster_key_param_set_t* params);
+ static keymaster_error_t add_rng_entropy(const keymaster2_device_t* dev, const uint8_t* data,
+ size_t data_length);
+ static keymaster_error_t generate_key(const keymaster2_device_t* dev,
+ const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics);
+ static keymaster_error_t get_key_characteristics(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_blob,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_key_characteristics_t* character);
+ static keymaster_error_t import_key(const keymaster2_device_t* dev,
+ const keymaster_key_param_set_t* params,
+ keymaster_key_format_t key_format,
+ const keymaster_blob_t* key_data,
+ keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics);
+ static keymaster_error_t export_key(const keymaster2_device_t* dev,
+ keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_blob_t* export_data);
+ static keymaster_error_t attest_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain);
+ static keymaster_error_t upgrade_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_to_upgrade,
+ const keymaster_key_param_set_t* upgrade_params,
+ keymaster_key_blob_t* upgraded_key);
+ static keymaster_error_t delete_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key);
+ static keymaster_error_t delete_all_keys(const keymaster2_device_t* dev);
+ static keymaster_error_t begin(const keymaster2_device_t* dev, keymaster_purpose_t purpose,
+ const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle);
+ static keymaster_error_t update(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, size_t* input_consumed,
+ keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+ static keymaster_error_t finish(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+ static keymaster_error_t abort(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle);
- keymaster0_device_t device_;
+ keymaster2_device_t device_;
keymaster_error_t error_;
int32_t message_version_;
};
} // namespace keymaster
-#endif // EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
+#endif // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
diff --git a/trusty/keymaster/trusty_keymaster_device_test.cpp b/trusty/keymaster/trusty_keymaster_device_test.cpp
index 3bb5430..e8f5c0b 100644
--- a/trusty/keymaster/trusty_keymaster_device_test.cpp
+++ b/trusty/keymaster/trusty_keymaster_device_test.cpp
@@ -16,8 +16,8 @@
#include <algorithm>
#include <fstream>
-#include <UniquePtr.h>
#include <gtest/gtest.h>
+#include <nativehelper/UniquePtr.h>
#include <openssl/engine.h>
#include <hardware/keymaster0.h>
diff --git a/trusty/keymaster/trusty_keymaster_ipc.c b/trusty/keymaster/trusty_keymaster_ipc.c
deleted file mode 100644
index 88546af..0000000
--- a/trusty/keymaster/trusty_keymaster_ipc.c
+++ /dev/null
@@ -1,95 +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.
- */
-
-#define LOG_TAG "TrustyKeymaster"
-
-// TODO: make this generic in libtrusty
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <log/log.h>
-#include <trusty/tipc.h>
-
-#include "trusty_keymaster_ipc.h"
-#include "keymaster_ipc.h"
-
-#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
-
-static int handle_ = 0;
-
-int trusty_keymaster_connect() {
- int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
- if (rc < 0) {
- return rc;
- }
-
- handle_ = rc;
- return 0;
-}
-
-int trusty_keymaster_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
- uint32_t *out_size) {
- if (handle_ == 0) {
- ALOGE("not connected\n");
- return -EINVAL;
- }
-
- size_t msg_size = in_size + sizeof(struct keymaster_message);
- struct keymaster_message *msg = malloc(msg_size);
- msg->cmd = cmd;
- memcpy(msg->payload, in, in_size);
-
- ssize_t rc = write(handle_, msg, msg_size);
- free(msg);
-
- if (rc < 0) {
- ALOGE("failed to send cmd (%d) to %s: %s\n", cmd,
- KEYMASTER_PORT, strerror(errno));
- return -errno;
- }
-
- rc = read(handle_, out, *out_size);
- if (rc < 0) {
- ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n",
- cmd, KEYMASTER_PORT, strerror(errno));
- return -errno;
- }
-
- if ((size_t) rc < sizeof(struct keymaster_message)) {
- ALOGE("invalid response size (%d)\n", (int) rc);
- return -EINVAL;
- }
-
- msg = (struct keymaster_message *) out;
-
- if ((cmd | KEYMASTER_RESP_BIT) != msg->cmd) {
- ALOGE("invalid command (%d)", msg->cmd);
- return -EINVAL;
- }
-
- *out_size = ((size_t) rc) - sizeof(struct keymaster_message);
- return rc;
-}
-
-void trusty_keymaster_disconnect() {
- if (handle_ != 0) {
- tipc_close(handle_);
- }
-}
-
diff --git a/trusty/keymaster/trusty_keymaster_ipc.cpp b/trusty/keymaster/trusty_keymaster_ipc.cpp
new file mode 100644
index 0000000..fbd0eb3
--- /dev/null
+++ b/trusty/keymaster/trusty_keymaster_ipc.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "TrustyKeymaster"
+
+// TODO: make this generic in libtrusty
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include <log/log.h>
+#include <trusty/tipc.h>
+
+#include "keymaster_ipc.h"
+#include "trusty_keymaster_ipc.h"
+
+#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
+
+static int handle_ = -1;
+
+int trusty_keymaster_connect() {
+ int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
+ if (rc < 0) {
+ return rc;
+ }
+
+ handle_ = rc;
+ return 0;
+}
+
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+ uint32_t* out_size) {
+ if (handle_ < 0) {
+ ALOGE("not connected\n");
+ return -EINVAL;
+ }
+
+ size_t msg_size = in_size + sizeof(struct keymaster_message);
+ struct keymaster_message* msg = reinterpret_cast<struct keymaster_message*>(malloc(msg_size));
+ msg->cmd = cmd;
+ memcpy(msg->payload, in, in_size);
+
+ ssize_t rc = write(handle_, msg, msg_size);
+ free(msg);
+
+ if (rc < 0) {
+ ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno));
+ return -errno;
+ }
+ size_t out_max_size = *out_size;
+ *out_size = 0;
+ struct iovec iov[2];
+ struct keymaster_message header;
+ iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)};
+ while (true) {
+ iov[1] = {
+ .iov_base = out + *out_size,
+ .iov_len = std::min<uint32_t>(KEYMASTER_MAX_BUFFER_LENGTH, out_max_size - *out_size)};
+ rc = readv(handle_, iov, 2);
+ if (rc < 0) {
+ ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
+ strerror(errno));
+ return -errno;
+ }
+
+ if ((size_t)rc < sizeof(struct keymaster_message)) {
+ ALOGE("invalid response size (%d)\n", (int)rc);
+ return -EINVAL;
+ }
+
+ if ((cmd | KEYMASTER_RESP_BIT) != (header.cmd & ~(KEYMASTER_STOP_BIT))) {
+ ALOGE("invalid command (%d)", header.cmd);
+ return -EINVAL;
+ }
+ *out_size += ((size_t)rc - sizeof(struct keymaster_message));
+ if (header.cmd & KEYMASTER_STOP_BIT) {
+ break;
+ }
+ }
+
+ return rc;
+}
+
+void trusty_keymaster_disconnect() {
+ if (handle_ >= 0) {
+ tipc_close(handle_);
+ }
+ handle_ = -1;
+}
diff --git a/trusty/keymaster/trusty_keymaster_ipc.h b/trusty/keymaster/trusty_keymaster_ipc.h
index 9785247..c15f7c1 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.h
+++ b/trusty/keymaster/trusty_keymaster_ipc.h
@@ -14,11 +14,16 @@
* limitations under the License.
*/
+#ifndef TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
+#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
+
__BEGIN_DECLS
int trusty_keymaster_connect(void);
-int trusty_keymaster_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
- uint32_t *out_size);
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+ uint32_t* out_size);
void trusty_keymaster_disconnect(void);
__END_DECLS
+
+#endif // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_
diff --git a/trusty/keymaster/trusty_keymaster_main.cpp b/trusty/keymaster/trusty_keymaster_main.cpp
index 7ed880e..9c2ae2d 100644
--- a/trusty/keymaster/trusty_keymaster_main.cpp
+++ b/trusty/keymaster/trusty_keymaster_main.cpp
@@ -14,7 +14,10 @@
* limitations under the License.
*/
+#include <keymaster/keymaster_configuration.h>
+
#include <stdio.h>
+#include <memory>
#include <openssl/evp.h>
#include <openssl/x509.h>
@@ -102,6 +105,28 @@
0xd1, 0x1f, 0xd4, 0x49, 0x49, 0xe0, 0xb2, 0x18, 0x3b, 0xfe};
unsigned int ec_privkey_pk8_der_len = 138;
+keymaster_key_param_t ec_params[] = {
+ keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_EC),
+ keymaster_param_long(KM_TAG_EC_CURVE, KM_EC_CURVE_P_521),
+ keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+ keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+ keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t ec_param_set = {ec_params, sizeof(ec_params) / sizeof(*ec_params)};
+
+keymaster_key_param_t rsa_params[] = {
+ keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ keymaster_param_int(KM_TAG_KEY_SIZE, 1024),
+ keymaster_param_long(KM_TAG_RSA_PUBLIC_EXPONENT, 65537),
+ keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+ keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+ keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+ keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t rsa_param_set = {rsa_params, sizeof(rsa_params) / sizeof(*rsa_params)};
+
struct EVP_PKEY_Delete {
void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
};
@@ -110,41 +135,70 @@
void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
};
+static bool do_operation(TrustyKeymasterDevice* device, keymaster_purpose_t purpose,
+ keymaster_key_blob_t* key, keymaster_blob_t* input,
+ keymaster_blob_t* signature, keymaster_blob_t* output) {
+ keymaster_key_param_t params[] = {
+ keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+ keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+ };
+ keymaster_key_param_set_t param_set = {params, sizeof(params) / sizeof(*params)};
+ keymaster_operation_handle_t op_handle;
+ keymaster_error_t error = device->begin(purpose, key, ¶m_set, nullptr, &op_handle);
+ if (error != KM_ERROR_OK) {
+ printf("Keymaster begin() failed: %d\n", error);
+ return false;
+ }
+ size_t input_consumed;
+ error = device->update(op_handle, nullptr, input, &input_consumed, nullptr, nullptr);
+ if (error != KM_ERROR_OK) {
+ printf("Keymaster update() failed: %d\n", error);
+ return false;
+ }
+ if (input_consumed != input->data_length) {
+ // This should never happen. If it does, it's a bug in the keymaster implementation.
+ printf("Keymaster update() did not consume all data.\n");
+ device->abort(op_handle);
+ return false;
+ }
+ error = device->finish(op_handle, nullptr, nullptr, signature, nullptr, output);
+ if (error != KM_ERROR_OK) {
+ printf("Keymaster finish() failed: %d\n", error);
+ return false;
+ }
+ return true;
+}
+
static bool test_import_rsa(TrustyKeymasterDevice* device) {
printf("===================\n");
printf("= RSA Import Test =\n");
printf("===================\n\n");
printf("=== Importing RSA keypair === \n");
- uint8_t* key;
- size_t size;
- int error = device->import_keypair(rsa_privkey_pk8_der, rsa_privkey_pk8_der_len, &key, &size);
+ keymaster_key_blob_t key;
+ keymaster_blob_t private_key = {rsa_privkey_pk8_der, rsa_privkey_pk8_der_len};
+ int error = device->import_key(&rsa_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
if (error != KM_ERROR_OK) {
- printf("Error importing key pair: %d\n\n", error);
+ printf("Error importing RSA key: %d\n\n", error);
return false;
}
- UniquePtr<uint8_t[]> key_deleter(key);
+ std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
printf("=== Signing with imported RSA key ===\n");
- keymaster_rsa_sign_params_t sign_params = {DIGEST_NONE, PADDING_NONE};
size_t message_len = 1024 / 8;
- UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
memset(message.get(), 'a', message_len);
- uint8_t* signature;
- size_t signature_len;
- error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
- &signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error signing data with imported RSA key: %d\n\n", error);
+ keymaster_blob_t input = {message.get(), message_len}, signature;
+
+ if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+ printf("Error signing data with imported RSA key\n\n");
return false;
}
- UniquePtr<uint8_t[]> signature_deleter(signature);
+ std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
printf("=== Verifying with imported RSA key === \n");
- error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
- signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error verifying data with imported RSA key: %d\n\n", error);
+ if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+ printf("Error verifying data with imported RSA key\n\n");
return false;
}
@@ -158,67 +212,58 @@
printf("============\n\n");
printf("=== Generating RSA key pair ===\n");
- keymaster_rsa_keygen_params_t params;
- params.public_exponent = 65537;
- params.modulus_size = 2048;
-
- uint8_t* key;
- size_t size;
- int error = device->generate_keypair(TYPE_RSA, ¶ms, &key, &size);
+ keymaster_key_blob_t key;
+ int error = device->generate_key(&rsa_param_set, &key, nullptr);
if (error != KM_ERROR_OK) {
printf("Error generating RSA key pair: %d\n\n", error);
return false;
}
- UniquePtr<uint8_t[]> deleter(key);
+ std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
printf("=== Signing with RSA key === \n");
- keymaster_rsa_sign_params_t sign_params = {DIGEST_NONE, PADDING_NONE};
- size_t message_len = params.modulus_size / 8;
- UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ size_t message_len = 1024 / 8;
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
memset(message.get(), 'a', message_len);
- uint8_t* signature;
- size_t signature_len;
- error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
- &signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error signing data with RSA key: %d\n\n", error);
+ keymaster_blob_t input = {message.get(), message_len}, signature;
+
+ if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+ printf("Error signing data with RSA key\n\n");
return false;
}
- UniquePtr<uint8_t[]> signature_deleter(signature);
+ std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
printf("=== Verifying with RSA key === \n");
- error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
- signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error verifying data with RSA key: %d\n\n", error);
+ if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+ printf("Error verifying data with RSA key\n\n");
return false;
}
printf("=== Exporting RSA public key ===\n");
- uint8_t* exported_key;
- size_t exported_size;
- error = device->get_keypair_public(key, size, &exported_key, &exported_size);
+ keymaster_blob_t exported_key;
+ error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
if (error != KM_ERROR_OK) {
printf("Error exporting RSA public key: %d\n\n", error);
return false;
}
printf("=== Verifying with exported key ===\n");
- const uint8_t* tmp = exported_key;
- UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &tmp, exported_size));
- UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ const uint8_t* tmp = exported_key.data;
+ std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+ d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+ std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
if (EVP_PKEY_verify_init(ctx.get()) != 1) {
- printf("Error initializing openss EVP context\n");
+ printf("Error initializing openss EVP context\n\n");
return false;
}
if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
- printf("Exported key was the wrong type?!?\n");
+ printf("Exported key was the wrong type?!?\n\n");
return false;
}
EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
- if (EVP_PKEY_verify(ctx.get(), signature, signature_len, message.get(), message_len) != 1) {
- printf("Verification with exported pubkey failed.\n");
+ if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+ message_len) != 1) {
+ printf("Verification with exported pubkey failed.\n\n");
return false;
} else {
printf("Verification succeeded\n");
@@ -234,35 +279,31 @@
printf("=====================\n\n");
printf("=== Importing ECDSA keypair === \n");
- uint8_t* key;
- size_t size;
- int error = device->import_keypair(ec_privkey_pk8_der, ec_privkey_pk8_der_len, &key, &size);
+ keymaster_key_blob_t key;
+ keymaster_blob_t private_key = {ec_privkey_pk8_der, ec_privkey_pk8_der_len};
+ int error = device->import_key(&ec_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
if (error != KM_ERROR_OK) {
- printf("Error importing key pair: %d\n\n", error);
+ printf("Error importing ECDSA key: %d\n\n", error);
return false;
}
- UniquePtr<uint8_t[]> deleter(key);
+ std::unique_ptr<const uint8_t[]> deleter(key.key_material);
printf("=== Signing with imported ECDSA key ===\n");
keymaster_ec_sign_params_t sign_params = {DIGEST_NONE};
size_t message_len = 30 /* arbitrary */;
- UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
memset(message.get(), 'a', message_len);
- uint8_t* signature;
- size_t signature_len;
- error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
- &signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error signing data with imported ECDSA key: %d\n\n", error);
+ keymaster_blob_t input = {message.get(), message_len}, signature;
+
+ if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+ printf("Error signing data with imported ECDSA key\n\n");
return false;
}
- UniquePtr<uint8_t[]> signature_deleter(signature);
+ std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
printf("=== Verifying with imported ECDSA key === \n");
- error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
- signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error verifying data with imported ECDSA key: %d\n\n", error);
+ if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+ printf("Error verifying data with imported ECDSA key\n\n");
return false;
}
@@ -276,64 +317,57 @@
printf("==============\n\n");
printf("=== Generating ECDSA key pair ===\n");
- keymaster_ec_keygen_params_t params;
- params.field_size = 521;
- uint8_t* key;
- size_t size;
- int error = device->generate_keypair(TYPE_EC, ¶ms, &key, &size);
- if (error != 0) {
+ keymaster_key_blob_t key;
+ int error = device->generate_key(&ec_param_set, &key, nullptr);
+ if (error != KM_ERROR_OK) {
printf("Error generating ECDSA key pair: %d\n\n", error);
return false;
}
- UniquePtr<uint8_t[]> deleter(key);
+ std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
printf("=== Signing with ECDSA key === \n");
- keymaster_ec_sign_params_t sign_params = {DIGEST_NONE};
size_t message_len = 30 /* arbitrary */;
- UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
memset(message.get(), 'a', message_len);
- uint8_t* signature;
- size_t signature_len;
- error = device->sign_data(&sign_params, key, size, message.get(), message_len, &signature,
- &signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error signing data with ECDSA key: %d\n\n", error);
+ keymaster_blob_t input = {message.get(), message_len}, signature;
+
+ if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+ printf("Error signing data with ECDSA key\n\n");
return false;
}
- UniquePtr<uint8_t[]> signature_deleter(signature);
+ std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
printf("=== Verifying with ECDSA key === \n");
- error = device->verify_data(&sign_params, key, size, message.get(), message_len, signature,
- signature_len);
- if (error != KM_ERROR_OK) {
- printf("Error verifying data with ECDSA key: %d\n\n", error);
+ if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+ printf("Error verifying data with ECDSA key\n\n");
return false;
}
printf("=== Exporting ECDSA public key ===\n");
- uint8_t* exported_key;
- size_t exported_size;
- error = device->get_keypair_public(key, size, &exported_key, &exported_size);
+ keymaster_blob_t exported_key;
+ error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
if (error != KM_ERROR_OK) {
printf("Error exporting ECDSA public key: %d\n\n", error);
return false;
}
printf("=== Verifying with exported key ===\n");
- const uint8_t* tmp = exported_key;
- UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &tmp, exported_size));
- UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ const uint8_t* tmp = exported_key.data;
+ std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+ d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+ std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
if (EVP_PKEY_verify_init(ctx.get()) != 1) {
- printf("Error initializing openss EVP context\n");
+ printf("Error initializing openssl EVP context\n\n");
return false;
}
if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) {
- printf("Exported key was the wrong type?!?\n");
+ printf("Exported key was the wrong type?!?\n\n");
return false;
}
- if (EVP_PKEY_verify(ctx.get(), signature, signature_len, message.get(), message_len) != 1) {
- printf("Verification with exported pubkey failed.\n");
+ if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+ message_len) != 1) {
+ printf("Verification with exported pubkey failed.\n\n");
return false;
} else {
printf("Verification succeeded\n");
@@ -344,8 +378,8 @@
}
int main(void) {
-
TrustyKeymasterDevice device(NULL);
+ keymaster::ConfigureDevice(reinterpret_cast<keymaster2_device_t*>(&device));
if (device.session_error() != KM_ERROR_OK) {
printf("Failed to initialize Trusty session: %d\n", device.session_error());
return 1;
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
new file mode 100644
index 0000000..f316da2
--- /dev/null
+++ b/trusty/libtrusty/Android.bp
@@ -0,0 +1,26 @@
+// 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.
+
+subdirs = [
+ "tipc-test",
+]
+
+cc_library {
+ name: "libtrusty",
+
+ srcs: ["trusty.c"],
+ export_include_dirs: ["include"],
+
+ shared_libs: ["liblog"],
+}
diff --git a/trusty/libtrusty/Android.mk b/trusty/libtrusty/Android.mk
deleted file mode 100644
index 45fc079..0000000
--- a/trusty/libtrusty/Android.mk
+++ /dev/null
@@ -1,36 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-# == libtrusty Static library ==
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libtrusty
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := trusty.c
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-include $(BUILD_STATIC_LIBRARY)
-
-# == libtrusty shared library ==
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libtrusty
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := trusty.c
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := liblog
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
new file mode 100644
index 0000000..cb00fe7
--- /dev/null
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -0,0 +1,26 @@
+// 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.
+
+cc_test {
+ name: "tipc-test",
+ static_executable: true,
+
+ srcs: ["tipc_test.c"],
+ static_libs: [
+ "libc",
+ "libtrusty",
+ "liblog",
+ ],
+ gtest: false,
+}
diff --git a/trusty/libtrusty/tipc-test/Android.mk b/trusty/libtrusty/tipc-test/Android.mk
deleted file mode 100644
index 80030fe..0000000
--- a/trusty/libtrusty/tipc-test/Android.mk
+++ /dev/null
@@ -1,29 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := tipc-test
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := tipc_test.c
-LOCAL_STATIC_LIBRARIES := libc libtrusty liblog
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-include $(BUILD_EXECUTABLE)
diff --git a/trusty/nvram/Android.bp b/trusty/nvram/Android.bp
new file mode 100644
index 0000000..15e6c3e
--- /dev/null
+++ b/trusty/nvram/Android.bp
@@ -0,0 +1,61 @@
+//
+// Copyright (C) 2016 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.
+//
+
+// nvram.trusty is the Trusty NVRAM HAL module.
+cc_library_shared {
+ name: "nvram.trusty",
+ relative_install_path: "hw",
+ srcs: [
+ "module.c",
+ "trusty_nvram_device.cpp",
+ "trusty_nvram_implementation.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ "-fvisibility=hidden",
+ ],
+ static_libs: ["libnvram-hal"],
+ shared_libs: [
+ "libtrusty",
+ "libnvram-messages",
+ "liblog",
+ ],
+}
+
+// nvram-wipe is a helper tool for clearing NVRAM state.
+cc_binary {
+ name: "nvram-wipe",
+ srcs: [
+ "nvram_wipe.cpp",
+ "trusty_nvram_implementation.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ "-fvisibility=hidden",
+ ],
+ static_libs: ["libnvram-hal"],
+ shared_libs: [
+ "libtrusty",
+ "libnvram-messages",
+ "liblog",
+ ],
+}
diff --git a/trusty/nvram/Android.mk b/trusty/nvram/Android.mk
deleted file mode 100644
index 44e2212..0000000
--- a/trusty/nvram/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# nvram.trusty is the Trusty NVRAM HAL module.
-include $(CLEAR_VARS)
-LOCAL_MODULE := nvram.trusty
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := \
- module.c \
- trusty_nvram_device.cpp \
- trusty_nvram_implementation.cpp
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -Wall -Werror -Wextra -fvisibility=hidden
-LOCAL_STATIC_LIBRARIES := libnvram-hal
-LOCAL_SHARED_LIBRARIES := libtrusty libnvram-messages liblog
-include $(BUILD_SHARED_LIBRARY)
-
-# nvram-wipe is a helper tool for clearing NVRAM state.
-include $(CLEAR_VARS)
-LOCAL_MODULE := nvram-wipe
-LOCAL_SRC_FILES := \
- nvram_wipe.cpp \
- trusty_nvram_implementation.cpp
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -Wall -Werror -Wextra -fvisibility=hidden
-LOCAL_STATIC_LIBRARIES := libnvram-hal
-LOCAL_SHARED_LIBRARIES := libtrusty libnvram-messages liblog
-include $(BUILD_EXECUTABLE)
diff --git a/trusty/storage/interface/Android.bp b/trusty/storage/interface/Android.bp
new file mode 100644
index 0000000..a551c37
--- /dev/null
+++ b/trusty/storage/interface/Android.bp
@@ -0,0 +1,20 @@
+//
+// 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.
+//
+
+cc_library_static {
+ name: "libtrustystorageinterface",
+ export_include_dirs: ["include"],
+}
diff --git a/trusty/storage/lib/Android.bp b/trusty/storage/lib/Android.bp
new file mode 100644
index 0000000..5eb3f07
--- /dev/null
+++ b/trusty/storage/lib/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+
+cc_library_static {
+ name: "libtrustystorage",
+
+ srcs: ["storage.c"],
+
+ export_include_dirs: ["include"],
+
+ static_libs: [
+ "liblog",
+ "libtrusty",
+ "libtrustystorageinterface",
+ ],
+
+ cflags: [
+ "-fvisibility=hidden",
+ "-Wall",
+ "-Werror",
+ ]
+}
diff --git a/trusty/storage/lib/Android.mk b/trusty/storage/lib/Android.mk
deleted file mode 100644
index 7e0fc9d..0000000
--- a/trusty/storage/lib/Android.mk
+++ /dev/null
@@ -1,37 +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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libtrustystorage
-
-LOCAL_SRC_FILES := \
- storage.c \
-
-LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-LOCAL_STATIC_LIBRARIES := \
- liblog \
- libtrusty \
- libtrustystorageinterface
-
-include $(BUILD_STATIC_LIBRARY)
-
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
new file mode 100644
index 0000000..eb34df0
--- /dev/null
+++ b/trusty/storage/proxy/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2016 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.
+//
+
+cc_binary {
+ name: "storageproxyd",
+
+ srcs: [
+ "ipc.c",
+ "rpmb.c",
+ "storage.c",
+ "proxy.c",
+ ],
+
+ shared_libs: ["liblog"],
+
+ static_libs: [
+ "libtrustystorageinterface",
+ "libtrusty",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ]
+}
diff --git a/trusty/storage/proxy/Android.mk b/trusty/storage/proxy/Android.mk
deleted file mode 100644
index 745e302..0000000
--- a/trusty/storage/proxy/Android.mk
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := storageproxyd
-
-LOCAL_C_INCLUDES += bionic/libc/kernel/uapi
-
-LOCAL_SRC_FILES := \
- ipc.c \
- rpmb.c \
- storage.c \
- proxy.c
-
-LOCAL_CLFAGS = -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
-
-LOCAL_STATIC_LIBRARIES := \
- libtrustystorageinterface \
- libtrusty
-
-include $(BUILD_EXECUTABLE)
-
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index d645ac0..27e5891 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -229,7 +229,6 @@
int main(int argc, char *argv[])
{
int rc;
- uint retry_cnt;
/* drop privileges */
if (drop_privs() < 0)
diff --git a/trusty/storage/tests/Android.bp b/trusty/storage/tests/Android.bp
new file mode 100644
index 0000000..1e4fced
--- /dev/null
+++ b/trusty/storage/tests/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2016 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.
+//
+
+cc_test {
+ name: "secure-storage-unit-test",
+
+ cflags: [
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ ],
+
+ static_libs: [
+ "libtrustystorageinterface",
+ "libtrustystorage",
+ "libtrusty",
+ "liblog",
+ ],
+
+ srcs: ["main.cpp"],
+}
diff --git a/trusty/storage/tests/Android.mk b/trusty/storage/tests/Android.mk
deleted file mode 100644
index 71c904d..0000000
--- a/trusty/storage/tests/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := secure-storage-unit-test
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
-LOCAL_STATIC_LIBRARIES := \
- libtrustystorageinterface \
- libtrustystorage \
- libtrusty \
- liblog
-LOCAL_SRC_FILES := main.cpp
-include $(BUILD_NATIVE_TEST)
-
diff --git a/tzdatacheck/Android.bp b/tzdatacheck/Android.bp
deleted file mode 100644
index 00ad141..0000000
--- a/tzdatacheck/Android.bp
+++ /dev/null
@@ -1,14 +0,0 @@
-// ========================================================
-// Executable
-// ========================================================
-cc_binary {
- name: "tzdatacheck",
- host_supported: true,
- srcs: ["tzdatacheck.cpp"],
- shared_libs: [
- "libbase",
- "libcutils",
- "liblog",
- ],
- cflags: ["-Werror"],
-}
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
deleted file mode 100644
index 8fcd17f..0000000
--- a/tzdatacheck/tzdatacheck.cpp
+++ /dev/null
@@ -1,601 +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 <ctype.h>
-#include <errno.h>
-#include <ftw.h>
-#include <libgen.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <iostream>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "android-base/logging.h"
-
-// The name of the directory that holds a staged time zone update distro. If this exists it should
-// replace the one in CURRENT_DIR_NAME.
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* STAGED_DIR_NAME = "/staged";
-
-// The name of the directory that holds the (optional) installed time zone update distro.
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* CURRENT_DIR_NAME = "/current";
-
-// The name of a file in the staged dir that indicates the staged operation is an "uninstall".
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* UNINSTALL_TOMBSTONE_FILE_NAME = "/STAGED_UNINSTALL_TOMBSTONE";
-
-// The name of the file containing the distro version information.
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char* DISTRO_VERSION_FILENAME = "/distro_version";
-
-// distro_version is an ASCII file consisting of 17 bytes in the form: AAA.BBB|CCCCC|DDD
-// AAA.BBB is the major/minor version of the distro format (e.g. 001.001),
-// CCCCC is the rules version (e.g. 2016g)
-// DDD is the android revision for this rules version to allow for distro corrections (e.g. 001)
-// We only need the first 13 to determine if it is suitable for the device.
-static const int DISTRO_VERSION_LENGTH = 13;
-
-// The major version of the distro format supported by this code as a null-terminated char[].
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char SUPPORTED_DISTRO_MAJOR_VERSION[] = "001";
-
-// The length of the distro format major version excluding the \0
-static const size_t SUPPORTED_DISTRO_MAJOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MAJOR_VERSION) - 1;
-
-// The minor version of the distro format supported by this code as a null-terminated char[].
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char SUPPORTED_DISTRO_MINOR_VERSION[] = "001";
-
-// The length of the distro format minor version excluding the \0
-static const size_t SUPPORTED_DISTRO_MINOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MINOR_VERSION) - 1;
-
-// The length of the distro format version. e.g. 001.001
-static const size_t SUPPORTED_DISTRO_VERSION_LEN =
- SUPPORTED_DISTRO_MAJOR_VERSION_LEN + SUPPORTED_DISTRO_MINOR_VERSION_LEN + 1;
-
-// The length of the IANA rules version bytes. e.g. 2016a
-static const size_t RULES_VERSION_LEN = 5;
-
-// Distro version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC
-static const size_t DISTRO_VERSION_RULES_IDX = 8;
-
-// See also libcore.tzdata.shared2.TimeZoneDistro.
-static const char* TZDATA_FILENAME = "/tzdata";
-
-// tzdata file header (as much as we need for the version):
-// byte[11] tzdata_version -- e.g. "tzdata2012f"
-static const int TZ_HEADER_LENGTH = 11;
-
-static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
-static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
-
-static void usage() {
- std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
- "\n"
- "Checks whether any timezone update distro in DATA_TZ_DIR is compatible with the\n"
- "current Android release and better than or the same as base system timezone rules in\n"
- "SYSTEM_TZ_DIR. If the timezone rules in SYSTEM_TZ_DIR are a higher version than the\n"
- "one in DATA_TZ_DIR the DATA_TZ_DIR is renamed and then deleted.\n";
- exit(1);
-}
-
-/*
- * Opens a file and fills buffer with the first byteCount bytes from the file.
- * If the file does not exist or cannot be opened or is too short then false is returned.
- * If the bytes were read successfully then true is returned.
- */
-static bool readBytes(const std::string& fileName, char* buffer, size_t byteCount) {
- FILE* file = fopen(fileName.c_str(), "r");
- if (file == nullptr) {
- if (errno != ENOENT) {
- PLOG(WARNING) << "Error opening file " << fileName;
- }
- return false;
- }
- size_t bytesRead = fread(buffer, 1, byteCount, file);
- fclose(file);
- if (bytesRead != byteCount) {
- LOG(WARNING) << fileName << " is too small. " << byteCount << " bytes required";
- return false;
- }
- return true;
-}
-
-/*
- * Checks the contents of headerBytes. Returns true if it is valid (starts with "tzdata"), false
- * otherwise.
- */
-static bool checkValidTzDataHeader(const std::string& fileName, const char* headerBytes) {
- if (strncmp("tzdata", headerBytes, 6) != 0) {
- LOG(WARNING) << fileName << " does not start with the expected bytes (tzdata)";
- return false;
- }
- return true;
-}
-
-static bool checkDigits(const char* buffer, const size_t count, size_t* i) {
- for (size_t j = 0; j < count; j++) {
- char toCheck = buffer[(*i)++];
- if (!isdigit(toCheck)) {
- return false;
- }
- }
- return true;
-}
-
-static bool checkValidDistroVersion(const char* buffer) {
- // See DISTRO_VERSION_LENGTH comments above for a description of the format.
- size_t i = 0;
- if (!checkDigits(buffer, 3, &i)) {
- return false;
- }
- if (buffer[i++] != '.') {
- return false;
- }
- if (!checkDigits(buffer, 3, &i)) {
- return false;
- }
- if (buffer[i++] != '|') {
- return false;
- }
- if (!checkDigits(buffer, 4, &i)) {
- return false;
- }
- // Ignore the last character. It is assumed to be a letter but we don't check because it's not
- // obvious what would happen at 'z'.
- return true;
-}
-
-/* Return the parent directory of dirName. */
-static std::string getParentDir(const std::string& dirName) {
- std::unique_ptr<char> mutable_dirname(strdup(dirName.c_str()));
- return dirname(mutable_dirname.get());
-}
-
-/* Deletes a single file, symlink or directory. Called from nftw(). */
-static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct FTW*) {
- LOG(DEBUG) << "Inspecting " << fpath;
- switch (typeflag) {
- case FTW_F:
- case FTW_SL:
- LOG(DEBUG) << "Unlinking " << fpath;
- if (unlink(fpath)) {
- PLOG(WARNING) << "Failed to unlink file/symlink " << fpath;
- }
- break;
- case FTW_D:
- case FTW_DP:
- LOG(DEBUG) << "Removing dir " << fpath;
- if (rmdir(fpath)) {
- PLOG(WARNING) << "Failed to remove dir " << fpath;
- }
- break;
- default:
- LOG(WARNING) << "Unsupported file type " << fpath << ": " << typeflag;
- break;
- }
- return 0;
-}
-
-enum PathStatus { ERR, NONE, IS_DIR, IS_REG, UNKNOWN };
-
-static PathStatus checkPath(const std::string& path) {
- struct stat buf;
- if (stat(path.c_str(), &buf) != 0) {
- if (errno != ENOENT) {
- PLOG(WARNING) << "Unable to stat " << path;
- return ERR;
- }
- return NONE;
- }
- return S_ISDIR(buf.st_mode) ? IS_DIR : S_ISREG(buf.st_mode) ? IS_REG : UNKNOWN;
-}
-
-/*
- * Deletes fileToDelete and returns true if it is successful. If fileToDelete is not a file or
- * cannot be accessed this method returns false.
- */
-static bool deleteFile(const std::string& fileToDelete) {
- // Check whether the file exists.
- PathStatus pathStatus = checkPath(fileToDelete);
- if (pathStatus == NONE) {
- LOG(INFO) << "Path " << fileToDelete << " does not exist";
- return true;
- }
- if (pathStatus != IS_REG) {
- LOG(WARNING) << "Path " << fileToDelete << " failed to stat() or is not a file.";
- return false;
- }
-
- // Attempt the deletion.
- int rc = unlink(fileToDelete.c_str());
- if (rc != 0) {
- PLOG(WARNING) << "unlink() failed for " << fileToDelete;
- }
- return rc == 0;
-}
-
-/*
- * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out
- * of the way. If dirToDelete does not exist this function does nothing and returns true. If
- * dirToDelete is not a directory or cannot be accessed this method returns false.
- *
- * During deletion, this function first renames the directory to a temporary name. If the temporary
- * directory cannot be created, or the directory cannot be renamed, false is returned. After the
- * rename, deletion of files and subdirs beneath the directory is performed on a "best effort"
- * basis. Symlinks beneath the directory are not followed.
- */
-static bool deleteDir(const std::string& dirToDelete) {
- // Check whether the dir exists.
- int pathStatus = checkPath(dirToDelete);
- if (pathStatus == NONE) {
- LOG(INFO) << "Path " << dirToDelete << " does not exist";
- return true;
- }
- if (pathStatus != IS_DIR) {
- LOG(WARNING) << "Path " << dirToDelete << " failed to stat() or is not a directory.";
- return false;
- }
-
- // First, rename dirToDelete.
-
- std::string tempDirNameTemplate = getParentDir(dirToDelete);
- tempDirNameTemplate += "/tempXXXXXX";
-
- // Create an empty directory with the temporary name. For this we need a non-const char*.
- std::vector<char> tempDirName(tempDirNameTemplate.length() + 1);
- strcpy(&tempDirName[0], tempDirNameTemplate.c_str());
- if (mkdtemp(&tempDirName[0]) == nullptr) {
- PLOG(WARNING) << "Unable to create a temporary directory: " << tempDirNameTemplate;
- return false;
- }
-
- // Rename dirToDelete to tempDirName (replacing the empty tempDirName directory created above).
- int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
- if (rc == -1) {
- PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
- << &tempDirName[0];
- return false;
- }
-
- // Recursively delete contents of tempDirName.
-
- rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
- FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
- if (rc == -1) {
- LOG(INFO) << "Could not delete directory: " << &tempDirName[0];
- }
- return true;
-}
-
-/*
- * Deletes the ConfigInstaller metadata directory.
- * TODO(nfuller). http://b/31008728 Remove this when ConfigInstaller is no longer used.
- */
-static void deleteConfigUpdaterMetadataDir(const char* dataZoneInfoDir) {
- // Delete the update metadata
- std::string dataUpdatesDirName(dataZoneInfoDir);
- dataUpdatesDirName += "/updates";
- LOG(INFO) << "Removing: " << dataUpdatesDirName;
- if (!deleteDir(dataUpdatesDirName)) {
- LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
- << " was not successful";
- }
-}
-
-/*
- * Deletes the timezone update distro directory.
- */
-static void deleteUpdateDistroDir(const std::string& distroDirName) {
- LOG(INFO) << "Removing: " << distroDirName;
- if (!deleteDir(distroDirName)) {
- LOG(WARNING) << "Deletion of distro dir " << distroDirName << " was not successful";
- }
-}
-
-static void handleStagedUninstall(const std::string& dataStagedDirName,
- const std::string& dataCurrentDirName,
- const PathStatus dataCurrentDirStatus) {
- LOG(INFO) << "Staged operation is an uninstall.";
-
- // Delete the current install directory.
- switch (dataCurrentDirStatus) {
- case NONE:
- // This is unexpected: No uninstall should be staged if there is nothing to
- // uninstall. Carry on anyway.
- LOG(WARNING) << "No current install to delete.";
- break;
- case IS_DIR:
- // This is normal. Delete the current install dir.
- if (!deleteDir(dataCurrentDirName)) {
- LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
- << " was not successful";
- // If this happens we don't know whether we were able to delete or not. We don't
- // delete the staged operation so it will be retried next boot unless overridden.
- return;
- }
- break;
- case IS_REG:
- default:
- // This is unexpected: We can try to delete the unexpected file and carry on.
- LOG(WARNING) << "Current distro dir " << dataCurrentDirName
- << " is not actually a directory. Attempting deletion.";
- if (!deleteFile(dataCurrentDirName)) {
- LOG(WARNING) << "Could not delete " << dataCurrentDirName;
- return;
- }
- break;
- }
-
- // Delete the staged uninstall dir.
- if (!deleteDir(dataStagedDirName)) {
- LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
- << " was not successful";
- // If this happens we don't know whether we were able to delete the staged operation
- // or not.
- return;
- }
- LOG(INFO) << "Staged uninstall complete.";
-}
-
-static void handleStagedInstall(const std::string& dataStagedDirName,
- const std::string& dataCurrentDirName,
- const PathStatus dataCurrentDirStatus) {
- LOG(INFO) << "Staged operation is an install.";
-
- switch (dataCurrentDirStatus) {
- case NONE:
- // This is expected: This is the first install.
- LOG(INFO) << "No current install to replace.";
- break;
- case IS_DIR:
- // This is expected: We are replacing an existing install.
- // Delete the current dir so we can replace it.
- if (!deleteDir(dataCurrentDirName)) {
- LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
- << " was not successful";
- // If this happens, we cannot proceed.
- return;
- }
- break;
- case IS_REG:
- default:
- // This is unexpected: We can try to delete the unexpected file and carry on.
- LOG(WARNING) << "Current distro dir " << dataCurrentDirName
- << " is not actually a directory. Attempting deletion.";
- if (!deleteFile(dataCurrentDirName)) {
- LOG(WARNING) << "Could not delete " << dataCurrentDirName;
- return;
- }
- break;
- }
-
- // Move the staged dir so it is the new current dir, completing the install.
- LOG(INFO) << "Moving " << dataStagedDirName << " to " << dataCurrentDirName;
- int rc = rename(dataStagedDirName.c_str(), dataCurrentDirName.c_str());
- if (rc == -1) {
- PLOG(WARNING) << "Unable to rename directory from " << dataStagedDirName << " to "
- << &dataCurrentDirName[0];
- return;
- }
-
- LOG(INFO) << "Staged install complete.";
-}
-/*
- * Process a staged operation if there is one.
- */
-static void processStagedOperation(const std::string& dataStagedDirName,
- const std::string& dataCurrentDirName) {
- PathStatus dataStagedDirStatus = checkPath(dataStagedDirName);
-
- // Exit early for the common case.
- if (dataStagedDirStatus == NONE) {
- LOG(DEBUG) << "No staged time zone operation.";
- return;
- }
-
- // Check known directory names are in a good starting state.
- if (dataStagedDirStatus != IS_DIR) {
- LOG(WARNING) << "Staged distro dir " << dataStagedDirName
- << " could not be accessed or is not a directory."
- << " stagedDirStatus=" << dataStagedDirStatus;
- return;
- }
-
- // dataStagedDirStatus == IS_DIR.
-
- // Work out whether there is anything currently installed.
- PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
- if (dataCurrentDirStatus == ERR) {
- LOG(WARNING) << "Current install dir " << dataCurrentDirName << " could not be accessed"
- << " dataCurrentDirStatus=" << dataCurrentDirStatus;
- return;
- }
-
- // We must perform the staged operation.
-
- // Check to see if the staged directory contains an uninstall or an install operation.
- std::string uninstallTombStoneFile(dataStagedDirName);
- uninstallTombStoneFile += UNINSTALL_TOMBSTONE_FILE_NAME;
- int uninstallTombStoneFileStatus = checkPath(uninstallTombStoneFile);
- if (uninstallTombStoneFileStatus != IS_REG && uninstallTombStoneFileStatus != NONE) {
- // Error case.
- LOG(WARNING) << "Unable to determine if the staged operation is an uninstall.";
- return;
- }
- if (uninstallTombStoneFileStatus == IS_REG) {
- handleStagedUninstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
- } else {
- // uninstallTombStoneFileStatus == NONE meaning this is a staged install.
- handleStagedInstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
- }
-}
-
-/*
- * After a platform update it is likely that timezone data found on the system partition will be
- * newer than the version found in the data partition. This tool detects this case and removes the
- * version in /data.
- *
- * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
- * paths for the metadata and current timezone data must match.
- *
- * Typically on device the two args will be:
- * /system/usr/share/zoneinfo /data/misc/zoneinfo
- *
- * See usage() for usage notes.
- */
-int main(int argc, char* argv[]) {
- if (argc != 3) {
- usage();
- return 1;
- }
-
- const char* systemZoneInfoDir = argv[1];
- const char* dataZoneInfoDir = argv[2];
-
- std::string dataStagedDirName(dataZoneInfoDir);
- dataStagedDirName += STAGED_DIR_NAME;
-
- std::string dataCurrentDirName(dataZoneInfoDir);
- dataCurrentDirName += CURRENT_DIR_NAME;
-
- // Check for an process any staged operation.
- // If the staged operation could not be handled we still have to validate the current installed
- // directory so we do not check for errors and do not quit early.
- processStagedOperation(dataStagedDirName, dataCurrentDirName);
-
- // Check the distro directory exists. If it does not, exit quickly: nothing to do.
- PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
- if (dataCurrentDirStatus == NONE) {
- LOG(INFO) << "timezone distro dir " << dataCurrentDirName
- << " does not exist. No action required.";
- return 0;
- }
-
- // If the distro directory path is not a directory or we can't stat() the path, exit with a
- // warning: either there's a problem accessing storage or the world is not as it should be;
- // nothing to do.
- if (dataCurrentDirStatus != IS_DIR) {
- LOG(WARNING) << "Current distro dir " << dataCurrentDirName
- << " could not be accessed or is not a directory. result=" << dataCurrentDirStatus;
- return 2;
- }
-
- // Check the installed distro version.
- std::string distroVersionFileName(dataCurrentDirName);
- distroVersionFileName += DISTRO_VERSION_FILENAME;
- std::vector<char> distroVersion;
- distroVersion.reserve(DISTRO_VERSION_LENGTH);
- bool distroVersionReadOk =
- readBytes(distroVersionFileName, distroVersion.data(), DISTRO_VERSION_LENGTH);
- if (!distroVersionReadOk) {
- LOG(WARNING) << "distro version file " << distroVersionFileName
- << " does not exist or is too short. Deleting distro dir.";
- // Implies the contents of the data partition is corrupt in some way. Try to clean up.
- deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateDistroDir(dataCurrentDirName);
- return 3;
- }
-
- if (!checkValidDistroVersion(distroVersion.data())) {
- LOG(WARNING) << "distro version file " << distroVersionFileName
- << " is not valid. Deleting distro dir.";
- // Implies the contents of the data partition is corrupt in some way. Try to clean up.
- deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateDistroDir(dataCurrentDirName);
- return 4;
- }
-
- std::string actualDistroVersion =
- std::string(distroVersion.data(), SUPPORTED_DISTRO_VERSION_LEN);
- // Check the first 3 bytes of the distro version: these are the major version (e.g. 001).
- // It must match the one we support exactly to be ok.
- if (strncmp(
- &distroVersion[0],
- SUPPORTED_DISTRO_MAJOR_VERSION,
- SUPPORTED_DISTRO_MAJOR_VERSION_LEN) != 0) {
-
- LOG(INFO) << "distro version file " << distroVersionFileName
- << " major version is not the required version " << SUPPORTED_DISTRO_MAJOR_VERSION
- << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
- // This implies there has been an OTA and the installed distro is not compatible with the
- // new version of Android. Remove the installed distro.
- deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateDistroDir(dataCurrentDirName);
- return 5;
- }
-
- // Check the last 3 bytes of the distro version: these are the minor version (e.g. 001).
- // If the version in the distro is < the minor version required by this device it cannot be
- // used.
- if (strncmp(
- &distroVersion[4],
- SUPPORTED_DISTRO_MINOR_VERSION,
- SUPPORTED_DISTRO_MINOR_VERSION_LEN) < 0) {
-
- LOG(INFO) << "distro version file " << distroVersionFileName
- << " minor version is not the required version " << SUPPORTED_DISTRO_MINOR_VERSION
- << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
- // This implies there has been an OTA and the installed distro is not compatible with the
- // new version of Android. Remove the installed distro.
- deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateDistroDir(dataCurrentDirName);
- return 5;
- }
-
- // Read the system rules version out of the /system tzdata file.
- std::string systemTzDataFileName(systemZoneInfoDir);
- systemTzDataFileName += TZDATA_FILENAME;
- std::vector<char> systemTzDataHeader;
- systemTzDataHeader.reserve(TZ_HEADER_LENGTH);
- bool systemFileExists =
- readBytes(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
- if (!systemFileExists) {
- // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
- LOG(WARNING) << systemTzDataFileName << " does not exist or could not be opened";
- return 6;
- }
- if (!checkValidTzDataHeader(systemTzDataFileName, systemTzDataHeader.data())) {
- // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
- LOG(WARNING) << systemTzDataFileName << " does not have a valid header.";
- return 7;
- }
-
- // Compare the distro rules version against the system rules version.
- if (strncmp(
- &systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN],
- &distroVersion[DISTRO_VERSION_RULES_IDX],
- RULES_VERSION_LEN) <= 0) {
- LOG(INFO) << "Found an installed distro but it is valid. No action taken.";
- // Implies there is an installed update, but it is good.
- return 0;
- }
-
- // Implies there has been an OTA and the system version of the timezone rules is now newer
- // than the version installed in /data. Remove the installed distro.
- LOG(INFO) << "timezone distro in " << dataCurrentDirName << " is older than data in "
- << systemTzDataFileName << "; fixing...";
-
- deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateDistroDir(dataCurrentDirName);
- return 0;
-}