combine wait for exec log with service exit log
am: 6e53199191
Change-Id: I8ec71493683b5ddea2995e719a4bc2de84ba4828
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.mk b/adb/Android.mk
index e841205..d17b063 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -81,12 +81,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 \
@@ -123,6 +125,7 @@
$(LIBADB_SRC_FILES) \
adbd_auth.cpp \
jdwp_service.cpp \
+ sysdeps/posix/network.cpp \
LOCAL_SANITIZE := $(adb_target_sanitize)
@@ -217,9 +220,9 @@
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 \
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 577e9b9..39e71e5 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -59,10 +59,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"
+ "Revision %s\n"
+ "Installed as %s\n",
+ ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_REVISION,
+ android::base::GetExecutablePath().c_str());
}
void fatal(const char *fmt, ...) {
@@ -251,6 +253,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 +312,29 @@
const std::string& type = pieces[0];
if (type == "bootloader") {
D("setting connection_state to kCsBootloader");
- t->connection_state = kCsBootloader;
+ t->SetConnectionState(kCsBootloader);
update_transports();
} else if (type == "device") {
D("setting connection_state to kCsDevice");
- t->connection_state = kCsDevice;
+ t->SetConnectionState(kCsDevice);
update_transports();
} else if (type == "recovery") {
D("setting connection_state to kCsRecovery");
- t->connection_state = kCsRecovery;
+ t->SetConnectionState(kCsRecovery);
update_transports();
} else if (type == "sideload") {
D("setting connection_state to kCsSideload");
- t->connection_state = kCsSideload;
+ t->SetConnectionState(kCsSideload);
update_transports();
} 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);
}
@@ -353,10 +368,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 +385,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 +406,7 @@
break;
#endif
default:
- t->connection_state = kCsOffline;
+ t->SetConnectionState(kCsOffline);
handle_offline(t);
break;
}
@@ -505,8 +522,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 +538,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 +569,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 +582,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 +590,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 +598,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 +607,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 +624,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 +635,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;
}
}
@@ -661,7 +679,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 +741,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 +751,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 +776,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 +806,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 +815,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
@@ -843,22 +860,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,7 +907,7 @@
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);
+ fprintf(stderr, "adb: execl returned %d: %s\n", result, strerror(errno));
} else {
// parent side of the fork
@@ -1032,7 +1047,6 @@
SendProtocolString(fd, s);
return 0;
}
-#endif
int handle_host_request(const char* service, TransportType type,
const char* serial, int reply_fd, asocket* s) {
@@ -1051,7 +1065,6 @@
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
@@ -1096,16 +1109,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;
@@ -1129,7 +1136,6 @@
return 0;
}
-#if ADB_HOST
if (!strcmp(service, "host-features")) {
FeatureSet features = supported_features();
// Abuse features to report libusb status.
@@ -1139,7 +1145,6 @@
SendOkay(reply_fd, FeatureSetToString(features));
return 0;
}
-#endif
// remove TCP transport
if (!strncmp(service, "disconnect:", 11)) {
@@ -1209,15 +1214,19 @@
}
if (!strcmp(service, "reconnect")) {
- if (s->transport != nullptr) {
- kick_transport(s->transport);
+ std::string response;
+ atransport* t = acquire_one_transport(type, serial, 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);
if (ret >= 0)
return ret - 1;
return -1;
}
+#endif // ADB_HOST
diff --git a/adb/adb.h b/adb/adb.h
index aea5fb8..e3675d8 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -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
@@ -222,6 +222,9 @@
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);
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index ef52189..b656887 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -136,8 +136,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,11 +146,9 @@
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);
@@ -164,21 +161,21 @@
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);
@@ -213,8 +210,8 @@
}
if (version != ADB_SERVER_VERSION) {
- printf("adb server version (%d) doesn't match this client (%d); killing...\n",
- version, ADB_SERVER_VERSION);
+ fprintf(stderr, "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);
@@ -240,7 +237,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);
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..6f2403d 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -267,8 +267,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);
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index e0ad103..c1d5549 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -21,7 +21,7 @@
#include <android-base/macros.h>
-int usage(const char*, ...);
+int syntax_error(const char*, ...);
void close_stdin();
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index 9dc9811..da2cfa2 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -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,7 +195,7 @@
};
int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
- if (argc > 2) return usage("usage: adb bugreport [PATH]");
+ if (argc > 2) return syntax_error("adb bugreport [PATH]");
// Gets bugreportz version.
std::string bugz_stdout, bugz_stderr;
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 7adb262..fec4742 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -62,12 +62,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 +74,7 @@
const char* name;
libusb_transfer* transfer;
+ bool is_bulk_out;
bool transfer_complete;
std::condition_variable cv;
std::mutex mutex;
@@ -96,12 +96,11 @@
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) {}
~usb_handle() {
Close();
@@ -306,14 +305,6 @@
}
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) {
@@ -373,11 +364,6 @@
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();
});
}
@@ -405,7 +391,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 +486,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) {
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 13b7674..6efed27 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -401,7 +401,6 @@
}
}
-
int usb_write(usb_handle *h, const void *_data, int len)
{
D("++ usb_write ++");
@@ -429,19 +428,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 +445,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) {
@@ -574,7 +570,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 +589,6 @@
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();
}
} // namespace native
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index d4fc7c0..fcd0bc0 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -405,7 +405,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";
@@ -436,9 +436,7 @@
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 +518,7 @@
}
if (kIOReturnSuccess == result)
- return 0;
+ return numBytes;
else {
LOG(ERROR) << "usb_read failed with status: " << std::hex << result;
}
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 640e91e..ee7f802 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -103,7 +103,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 +174,7 @@
return 1;
}
-void device_poll_thread(void*) {
+void device_poll_thread() {
adb_thread_setname("Device Poll");
D("Created device thread");
@@ -203,7 +203,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,12 +258,8 @@
}
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();
}
usb_handle* do_usb_open(const wchar_t* interface_name) {
@@ -419,6 +415,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 +424,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 +433,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
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 4979eef..5f55ab9 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -169,15 +169,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 +212,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"
@@ -663,13 +656,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|.
@@ -703,8 +691,7 @@
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;
@@ -856,7 +843,10 @@
* we hang up.
*/
static int adb_sideload_host(const char* filename) {
- fprintf(stderr, "opening '%s'...\n", filename);
+ // TODO: use a LinePrinter instead...
+ fprintf(stdout, "opening '%s'...\n", filename);
+ fflush(stdout);
+
struct stat sb;
if (stat(filename, &sb) == -1) {
fprintf(stderr, "failed to stat file %s: %s\n", filename, strerror(errno));
@@ -868,7 +858,8 @@
return -1;
}
- fprintf(stderr, "connecting...\n");
+ fprintf(stdout, "connecting...\n");
+ fflush(stdout);
std::string service = android::base::StringPrintf(
"sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
std::string error;
@@ -957,19 +948,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;
}
@@ -1188,10 +1173,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 +1185,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 +1210,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 +1221,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 +1238,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
@@ -1351,7 +1332,7 @@
} else if (!strcmp(*arg, "--")) {
ignore_flags = true;
} else {
- fprintf(stderr, "adb: unrecognized option '%s'\n", *arg);
+ syntax_error("unrecognized option '%s'", *arg);
exit(1);
}
}
@@ -1450,7 +1431,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++;
@@ -1462,7 +1443,7 @@
} 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");
+ if (argc < 2) return syntax_error("-p requires an argument");
product = argv[1];
argc--;
argv++;
@@ -1478,7 +1459,7 @@
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++;
@@ -1491,7 +1472,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 +1481,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 +1489,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 +1502,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.
@@ -1617,8 +1597,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 +1605,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 +1626,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];
@@ -1704,7 +1674,7 @@
}
}
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 {
@@ -1738,7 +1708,7 @@
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 +1728,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,7 +1774,7 @@
}
/* 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")) {
@@ -1813,7 +1783,7 @@
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");
+ if (srcs.empty() || !dst) return syntax_error("push requires an argument");
return do_sync_push(srcs, dst) ? 0 : 1;
}
else if (!strcmp(argv[0], "pull")) {
@@ -1822,22 +1792,22 @@
const char* dst = ".";
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
- if (srcs.empty()) return usage("pull requires an argument");
+ 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(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "install-multiple")) {
- if (argc < 2) return usage("install-multiple requires an argument");
+ if (argc < 2) return syntax_error("install-multiple requires an argument");
return install_multiple_app(transport_type, serial, 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);
}
@@ -1860,12 +1830,12 @@
// 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 != "" &&
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");
@@ -1917,7 +1887,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 +1930,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], transport_type, serial));
} else if (argc == 2) {
if (!strcmp(argv[1], "device")) {
std::string err;
@@ -1970,12 +1940,12 @@
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;
}
@@ -2002,19 +1972,18 @@
// 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 +2001,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,12 +2013,12 @@
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,
@@ -2071,10 +2040,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 +2058,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 +2075,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 +2086,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 +2098,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 +2106,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 +2119,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,20 +2133,19 @@
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) {
@@ -2238,10 +2203,7 @@
}
}
- 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]};
diff --git a/adb/commandline.h b/adb/commandline.h
index 0cf655c..9ba69a3 100644
--- a/adb/commandline.h
+++ b/adb/commandline.h
@@ -87,7 +87,6 @@
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.
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..d3b2f3d 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -64,7 +64,7 @@
#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;
@@ -451,9 +451,7 @@
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/fdevent.cpp b/adb/fdevent.cpp
index 04cd865..72c9eef 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -75,13 +75,13 @@
static bool main_thread_valid;
static unsigned long main_thread_id;
-static void check_main_thread() {
+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();
}
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 207f9b7..e32845a 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -76,9 +76,12 @@
*/
void fdevent_loop();
+void check_main_thread();
+
// 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..bdb973a 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,6 @@
}
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();
}
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index ef65b74..f4215ae 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"
@@ -59,10 +61,10 @@
#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/services.cpp b/adb/services.cpp
index c9ea039..9605e6e 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>
@@ -259,13 +261,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];
@@ -351,7 +347,7 @@
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)) {
+ if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
SendOkay(fd);
break;
} else if (!is_ambiguous) {
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_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..14ad1ff 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -794,7 +794,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..49c7847 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,55 +578,6 @@
#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());
diff --git a/bootstat/uptime_parser.h b/adb/sysdeps/network.h
similarity index 65%
copy from bootstat/uptime_parser.h
copy to adb/sysdeps/network.h
index 756ae9b..83ce371 100644
--- a/bootstat/uptime_parser.h
+++ b/adb/sysdeps/network.h
@@ -1,5 +1,7 @@
+#pragma once
+
/*
- * 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,16 +16,7 @@
* limitations under the License.
*/
-#ifndef UPTIME_PARSER_H_
-#define UPTIME_PARSER_H_
+#include <string>
-#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
+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..edb0fb3 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);
@@ -254,13 +206,13 @@
static std::mutex &m = *new std::mutex();
m.lock();
ASSERT_FALSE(m.try_lock());
- adb_thread_create([](void*) {
+ std::thread thread([]() {
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);
@@ -270,6 +222,8 @@
m.lock();
ASSERT_TRUE(finished.load());
m.unlock();
+
+ thread.join();
}
TEST(sysdeps_mutex, recursive_mutex_smoke) {
@@ -279,12 +233,12 @@
ASSERT_TRUE(m.try_lock());
m.unlock();
- adb_thread_create([](void*) {
+ std::thread thread([]() {
ASSERT_FALSE(m.try_lock());
m.lock();
std::this_thread::sleep_for(500ms);
m.unlock();
- }, nullptr);
+ });
std::this_thread::sleep_for(100ms);
m.unlock();
@@ -292,6 +246,8 @@
ASSERT_FALSE(m.try_lock());
m.lock();
m.unlock();
+
+ thread.join();
}
TEST(sysdeps_condition_variable, smoke) {
@@ -300,14 +256,16 @@
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_device.py b/adb/test_device.py
index e76aaed..a30972e 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -1188,6 +1188,77 @@
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 main():
random.seed(0)
if len(adb.get_devices()) > 0:
diff --git a/adb/transport.cpp b/adb/transport.cpp
index c951f5b..cc8c162 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -29,9 +29,11 @@
#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>
@@ -40,6 +42,7 @@
#include "adb_trace.h"
#include "adb_utils.h"
#include "diagnose_usb.h"
+#include "fdevent.h"
static void transport_unref(atransport *t);
@@ -208,6 +211,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 +278,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);
}
@@ -492,7 +504,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,13 +521,8 @@
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();
}
{
@@ -542,6 +549,15 @@
transport_registration_func, 0);
fdevent_set(&transport_registration_fde, FDE_READ);
+#if ADB_HOST
+ android::base::at_quick_exit([]() {
+ // To avoid only writing part of a packet to a transport after exit, kick all transports.
+ std::lock_guard<std::mutex> lock(transport_lock);
+ for (auto t : transport_list) {
+ t->Kick();
+ }
+ });
+#endif
}
/* the fdevent select pump is single threaded */
@@ -604,7 +620,7 @@
}
atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
- std::string* error_out) {
+ std::string* error_out, bool accept_any_state) {
atransport* result = nullptr;
if (serial) {
@@ -619,7 +635,7 @@
std::unique_lock<std::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
@@ -668,7 +684,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 +696,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 +708,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:
@@ -967,10 +1005,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);
}
@@ -991,12 +1029,13 @@
void unregister_usb_transport(usb_handle* usb) {
std::lock_guard<std::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) {
if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
- VLOG(RWX) << "check_header(): invalid magic";
+ VLOG(RWX) << "check_header(): invalid magic command = " << std::hex << p->msg.command
+ << ", magic = " << p->msg.magic;
return -1;
}
@@ -1024,4 +1063,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..8c15d66 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>
@@ -57,31 +59,35 @@
// 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) : 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 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);
+
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 +120,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 +165,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 +176,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);
@@ -181,8 +193,8 @@
* 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, bool* is_ambiguous,
+ std::string* error_out, bool accept_any_state = false);
void kick_transport(atransport* t);
void update_transports(void);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 4198a52..3ee286a 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -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;
}
@@ -394,7 +392,7 @@
void local_init(int port)
{
- adb_thread_func_t func;
+ void (*func)(int);
const char* debug_name = "";
#if ADB_HOST
@@ -414,9 +412,7 @@
#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 +515,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_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..ce419b8 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -25,9 +25,115 @@
#include "adb.h"
+#if ADB_HOST
+
+static constexpr size_t MAX_USB_BULK_PACKET_SIZE = 1024u;
+
+// Call usb_read using a buffer having a multiple of MAX_USB_BULK_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");
+ char buffer[MAX_USB_BULK_PACKET_SIZE];
+ int n = usb_read(h, buffer, sizeof(buffer));
+ if (n == sizeof(*msg)) {
+ memcpy(msg, buffer, sizeof(*msg));
+ }
+ return n;
+}
+
+// Call usb_read using a buffer having a multiple of MAX_USB_BULK_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");
+ size_t need_size = p->msg.data_length;
+ size_t data_pos = 0u;
+ while (need_size > 0u) {
+ int n = 0;
+ if (data_pos + MAX_USB_BULK_PACKET_SIZE <= sizeof(p->data)) {
+ // Read directly to p->data.
+ size_t rem_size = need_size % MAX_USB_BULK_PACKET_SIZE;
+ size_t direct_read_size = need_size - rem_size;
+ if (rem_size &&
+ data_pos + direct_read_size + MAX_USB_BULK_PACKET_SIZE <= sizeof(p->data)) {
+ direct_read_size += MAX_USB_BULK_PACKET_SIZE;
+ }
+ n = usb_read(h, &p->data[data_pos], direct_read_size);
+ if (n < 0) {
+ D("usb_read(size %zu) failed", direct_read_size);
+ return n;
+ }
+ } else {
+ // Read indirectly using a buffer.
+ char buffer[MAX_USB_BULK_PACKET_SIZE];
+ n = usb_read(h, buffer, sizeof(buffer));
+ if (n < 0) {
+ D("usb_read(size %zu) failed", sizeof(buffer));
+ return -1;
+ }
+ size_t copy_size = std::min(static_cast<size_t>(n), need_size);
+ D("usb read %d bytes, need %zu bytes, copy %zu bytes", n, need_size, copy_size);
+ memcpy(&p->data[data_pos], buffer, copy_size);
+ }
+ data_pos += n;
+ need_size -= std::min(static_cast<size_t>(n), need_size);
+ }
+ return static_cast<int>(data_pos);
+}
+
+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;
+
+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))){
+ if (usb_read(t->usb, &p->msg, sizeof(amessage))) {
D("remote usb: read terminated (message)");
return -1;
}
@@ -38,7 +144,7 @@
}
if(p->msg.data_length) {
- if(usb_read(t->usb, p->data, p->msg.data_length)){
+ if (usb_read(t->usb, p->data, p->msg.data_length)) {
D("remote usb: terminated (data)");
return -1;
}
@@ -51,17 +157,18 @@
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))) {
+ if (usb_write(t->usb, &p->msg, sizeof(amessage))) {
D("remote usb: 1 - write terminated");
return -1;
}
if(p->msg.data_length == 0) return 0;
- if(usb_write(t->usb, &p->data, size)) {
+ if (usb_write(t->usb, &p->data, size)) {
D("remote usb: 2 - write terminated");
return -1;
}
@@ -75,20 +182,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;
}
diff --git a/base/Android.bp b/base/Android.bp
index a8dda5d..81b96db 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,
@@ -54,7 +51,10 @@
"test_utils.cpp",
],
- header_libs: ["libbase_headers"],
+ header_libs: [
+ "libbase_headers",
+ "libutils_headers",
+ ],
export_header_lib_headers: ["libbase_headers"],
cppflags: libbase_cppflags,
@@ -64,24 +64,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: [
@@ -108,17 +120,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..5eedf3b
--- /dev/null
+++ b/base/chrono_utils.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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__
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/chrono_utils_test.cpp b/base/chrono_utils_test.cpp
new file mode 100644
index 0000000..057132d
--- /dev/null
+++ b/base/chrono_utils_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/chrono_utils.h"
+
+#include <time.h>
+
+#include <chrono>
+
+#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);
+}
+
+} // namespace base
+} // namespace android
\ No newline at end of file
diff --git a/base/file.cpp b/base/file.cpp
index d4e5894..7fbebc5 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -28,8 +28,9 @@
#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"
@@ -69,13 +70,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 +105,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 +125,6 @@
PLOG(ERROR) << "android::WriteStringToFile write failed";
return CleanUpAfterFailedWrite(path);
}
- close(fd);
return true;
}
#endif
@@ -135,14 +133,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, DEFFILEMODE)));
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) {
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
new file mode 100644
index 0000000..0086425
--- /dev/null
+++ b/base/include/android-base/chrono_utils.h
@@ -0,0 +1,37 @@
+/*
+ * 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>
+
+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();
+};
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_CHRONO_UTILS_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/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/bootstat/Android.bp b/bootstat/Android.bp
index f744ad1..bc90a6e 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 {
@@ -75,6 +74,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/bootstat.cpp b/bootstat/bootstat.cpp
index a4cc5f2..6f25d96 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -21,6 +21,7 @@
#include <getopt.h>
#include <unistd.h>
+#include <chrono>
#include <cmath>
#include <cstddef>
#include <cstdio>
@@ -30,15 +31,15 @@
#include <string>
#include <vector>
-#include <android/log.h>
+#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <android/log.h>
#include <cutils/properties.h>
#include <metricslogger/metrics_logger.h>
#include "boot_event_record_store.h"
-#include "uptime_parser.h"
namespace {
@@ -255,7 +256,8 @@
BootEventRecordStore boot_event_store;
BootEventRecordStore::BootEventRecord record;
- time_t uptime = bootstat::ParseUptime();
+ auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
+ android::base::boot_clock::now().time_since_epoch());
time_t current_time_utc = time(nullptr);
if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
@@ -282,22 +284,21 @@
// 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");
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/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 3b84853..2be13c6 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -140,7 +140,9 @@
}
bool backtrace = dump_type == kDebuggerdBacktrace;
- send_signal(pid, backtrace);
+ if (!send_signal(pid, backtrace)) {
+ return false;
+ }
rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
if (rc == 0) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 22fde5e..7f450e6 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -22,16 +22,22 @@
#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/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/fastboot/Android.mk b/fastboot/Android.mk
index 5610cc0..2c578a9 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -51,7 +51,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
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..56ee816 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;
@@ -282,7 +298,7 @@
return 0;
}
-void fb_queue_query_save(const char *var, char *dest, unsigned dest_size)
+void fb_queue_query_save(const char *var, char *dest, uint32_t dest_size)
{
Action *a;
a = queue_action(OP_QUERY, "getvar:%s", var);
@@ -309,7 +325,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,6 +333,22 @@
a->msg = mkmsg("downloading '%s'", name);
}
+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(char *outfile)
+{
+ Action *a = queue_action(OP_UPLOAD, "");
+ a->data = outfile;
+ a->msg = mkmsg("uploading '%s'", outfile);
+}
+
void fb_queue_notice(const char *notice)
{
Action *a = queue_action(OP_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..982545c 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -55,6 +55,7 @@
#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <sparse/sparse.h>
#include <ziparchive/zip_archive.h>
@@ -67,6 +68,8 @@
#include "udp.h"
#include "usb.h"
+using android::base::unique_fd;
+
#ifndef O_BINARY
#define O_BINARY 0
#endif
@@ -78,6 +81,10 @@
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;
@@ -91,7 +98,7 @@
static const std::string convert_fbe_marker_filename("convert_fbe");
enum fb_buffer_type {
- FB_BUFFER,
+ FB_BUFFER_FD,
FB_BUFFER_SPARSE,
};
@@ -99,6 +106,7 @@
enum fb_buffer_type type;
void* data;
int64_t sz;
+ int fd;
};
static struct {
@@ -115,6 +123,7 @@
{"system_other.img", "system.sig", "system", true, true},
{"vendor.img", "vendor.sig", "vendor", true, false},
{"vendor_other.img", "vendor.sig", "vendor", true, true},
+ {"vbmeta.img", "vbmeta.sig", "vbmeta", true, false},
};
static std::string find_item_given_name(const char* img_name, const char* product) {
@@ -144,6 +153,8 @@
fn = "system.img";
} else if(!strcmp(item,"vendor")) {
fn = "vendor.img";
+ } else if(!strcmp(item,"vbmeta")) {
+ fn = "vbmeta.img";
} else if(!strcmp(item,"userdata")) {
fn = "userdata.img";
} else if(!strcmp(item,"cache")) {
@@ -360,6 +371,13 @@
" 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"
@@ -483,8 +501,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 +511,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 +560,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() {
@@ -592,8 +627,8 @@
}
static int unzip_to_file(ZipArchiveHandle zip, char* entry_name) {
- FILE* fp = tmpfile();
- if (fp == nullptr) {
+ 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 +638,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 +820,7 @@
}
if (size > limit) {
- return limit;
+ return std::min(limit, RESPARSE_LIMIT);
}
return 0;
@@ -819,10 +853,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,11 +863,22 @@
}
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 flash_buf(const char *pname, struct fastboot_buffer *buf)
@@ -857,9 +901,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);
@@ -1131,7 +1174,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.
*/
};
@@ -1392,7 +1435,8 @@
return;
}
- fd = fileno(tmpfile());
+ fd = make_temporary_fd();
+ if (fd == -1) return;
unsigned eraseBlkSize, logicalBlkSize;
eraseBlkSize = fb_get_flash_block_size(transport, "erase-block-size");
@@ -1536,6 +1580,7 @@
setvbuf(stderr, nullptr, _IONBF, 0);
} else if (strcmp("version", longopts[longindex].name) == 0) {
fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
+ 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);
@@ -1785,6 +1830,20 @@
}
fb_set_active(slot.c_str());
skip(2);
+ } else if(!strcmp(*argv, "stage")) {
+ require(2);
+ std::string infile(argv[1]);
+ skip(2);
+ struct fastboot_buffer buf;
+ if (!load_buf(transport, infile.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
+ die("cannot load '%s'", infile.c_str());
+ }
+ fb_queue_download_fd(infile.c_str(), buf.fd, buf.sz);
+ } else if(!strcmp(*argv, "get_staged")) {
+ require(2);
+ char *outfile = argv[1];
+ skip(2);
+ fb_queue_upload(outfile);
} else if(!strcmp(*argv, "oem")) {
argc = do_oem_command(argc, argv);
} else if(!strcmp(*argv, "flashing")) {
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 6699b6a..e30c6de 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(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/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/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index e3d4f87..247768a 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1367,7 +1367,8 @@
std::string mount_point;
if (system_root && !strcmp(fstab->recs[i].mount_point, "/")) {
- mount_point = "system";
+ // In AVB, the dm device name is vroot instead of system.
+ mount_point = fs_mgr_is_avb(&fstab->recs[i]) ? "vroot" : "system";
} else {
mount_point = basename(fstab->recs[i].mount_point);
}
@@ -1386,6 +1387,10 @@
status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
+ // To be consistent in vboot 1.0 and vboot 2.0 (AVB), change the mount_point
+ // back to 'system' for the callback. So it has property [partition.system.verified]
+ // instead of [partition.vroot.verified].
+ if (mount_point == "vroot") mount_point = "system";
if (*status == 'C' || *status == 'V') {
callback(&fstab->recs[i], mount_point.c_str(), mode, *status);
}
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index cffa6ce..ab5beed 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -55,8 +55,6 @@
if (android::base::ReadFileToString(file_name, out_val)) {
return true;
}
-
- LINFO << "Error finding '" << key << "' in device tree";
}
return false;
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.mk b/init/Android.mk
index 3c878b5..de3d076 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
@@ -64,6 +66,7 @@
action.cpp \
capabilities.cpp \
descriptors.cpp \
+ devices.cpp \
import_parser.cpp \
init_parser.cpp \
log.cpp \
@@ -83,14 +86,13 @@
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 \
ueventd.cpp \
- ueventd_parser.cpp \
watchdogd.cpp \
LOCAL_MODULE:= init
@@ -139,18 +141,21 @@
include $(CLEAR_VARS)
LOCAL_MODULE := init_tests
LOCAL_SRC_FILES := \
+ devices_test.cpp \
init_parser_test.cpp \
+ init_test.cpp \
property_service_test.cpp \
util_test.cpp \
LOCAL_SHARED_LIBRARIES += \
- libcutils \
libbase \
+ libcutils \
+ libselinux \
LOCAL_STATIC_LIBRARIES := libinit
LOCAL_SANITIZE := integer
LOCAL_CLANG := true
-LOCAL_CPPFLAGS := -Wall -Wextra -Werror
+LOCAL_CPPFLAGS := -Wall -Wextra -Werror -std=gnu++1z
include $(BUILD_NATIVE_TEST)
diff --git a/init/README.md b/init/README.md
index 8cb1e52..9fc8d47 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
--------
@@ -276,7 +310,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
@@ -401,6 +436,16 @@
`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 +492,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..21abe02 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -16,25 +16,18 @@
#include "action.h"
-#include <errno.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) {
-}
+Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
+ : func_(f), args_(args), line_(line) {}
int Command::InvokeFunc() const {
std::vector<std::string> expanded_args;
@@ -54,50 +47,28 @@
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) {
+bool Action::AddCommand(const std::vector<std::string>& args, int line, std::string* err) {
if (!function_map_) {
*err = "no function map available";
return false;
}
- if (args.empty()) {
- *err = "command needed, but not provided";
- return false;
- }
-
- auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
+ auto function = function_map_->FindFunction(args, err);
if (!function) {
return false;
}
- AddCommand(function, args, filename, line);
+ AddCommand(function, args, line);
return true;
}
-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 {
@@ -127,7 +98,7 @@
android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
- std::string source = command.BuildSourceString();
+ std::string source = StringPrintf(" (%s:%d)", filename_.c_str(), command.line());
LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
<< " returned " << result << " took " << duration_ms << "ms.";
@@ -228,20 +199,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 +235,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 +244,45 @@
}
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)) {
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 +293,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 +316,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,15 +325,15 @@
}
}
-bool ActionParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
if (triggers.size() < 1) {
*err = "actions must have a trigger";
return false;
}
- auto action = std::make_unique<Action>(false);
+ auto action = std::make_unique<Action>(false, filename, line);
if (!action->InitTriggers(triggers, err)) {
return false;
}
@@ -419,14 +342,12 @@
return true;
}
-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;
+bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+ return action_ ? action_->AddCommand(std::move(args), line, err) : false;
}
void ActionParser::EndSection() {
if (action_ && action_->NumCommands() > 0) {
- ActionManager::GetInstance().AddAction(std::move(action_));
+ action_manager_->AddAction(std::move(action_));
}
}
diff --git a/init/action.h b/init/action.h
index 0bae9f0..d006c50 100644
--- a/init/action.h
+++ b/init/action.h
@@ -20,6 +20,7 @@
#include <map>
#include <queue>
#include <string>
+#include <variant>
#include <vector>
#include "builtins.h"
@@ -27,44 +28,44 @@
#include "keyword_map.h"
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;
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);
+class Action {
+ public:
+ explicit Action(bool oneshot, const std::string& filename, int line);
+
+ bool AddCommand(const std::vector<std::string>& args, int line, std::string* err);
+ void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
bool InitTriggers(const std::vector<std::string>& args, std::string* err);
bool 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;
}
@@ -80,53 +81,48 @@
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;
-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,
+ public:
+ ActionParser(ActionManager* action_manager)
+ : action_manager_(action_manager), action_(nullptr) {}
+ bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override;
+ bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
void EndSection() override;
- void EndFile(const std::string&) override {
- }
-private:
+
+ private:
+ ActionManager* action_manager_;
std::unique_ptr<Action> action_;
};
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index beabea1..825603a 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>
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 7ee02d0..1d98ef1 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -19,33 +19,28 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.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/file.h>
+#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -55,14 +50,14 @@
#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 "action.h"
#include "bootchart.h"
-#include "devices.h"
#include "init.h"
#include "init_parser.h"
-#include "log.h"
#include "property_service.h"
#include "reboot.h"
#include "service.h"
@@ -395,7 +390,7 @@
// 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
@@ -843,7 +838,7 @@
return e4crypt_do_init_user0();
}
-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 = {
diff --git a/init/builtins.h b/init/builtins.h
index 53f4a71..e1f0567 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -17,19 +17,20 @@
#ifndef _INIT_BUILTINS_H
#define _INIT_BUILTINS_H
+#include <functional>
#include <map>
#include <string>
#include <vector>
#include "keyword_map.h"
-using BuiltinFunction = int (*) (const std::vector<std::string>& args);
+using BuiltinFunction = std::function<int(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;
};
#endif
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
index 6e457cd..bc6bc8d 100644
--- a/init/descriptors.cpp
+++ b/init/descriptors.cpp
@@ -19,16 +19,15 @@
#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/unique_fd.h>
#include <cutils/android_get_control_file.h>
#include <cutils/sockets.h>
#include "init.h"
-#include "log.h"
#include "util.h"
DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
diff --git a/init/devices.cpp b/init/devices.cpp
index 405f92e..07d28d0 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -14,166 +14,236 @@
* limitations under the License.
*/
+#include "devices.h"
+
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
+#include <grp.h>
#include <libgen.h>
+#include <linux/netlink.h>
+#include <pwd.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 <unistd.h>
-#include <linux/netlink.h>
-
+#include <algorithm>
#include <memory>
#include <thread>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/uevent.h>
+#include <private/android_filesystem_config.h>
#include <selinux/android.h>
#include <selinux/avc.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
-#include <private/android_filesystem_config.h>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <cutils/list.h>
-#include <cutils/uevent.h>
-
-#include "devices.h"
-#include "ueventd_parser.h"
+#include "keyword_map.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" };
extern struct selabel_handle *sehandle;
static android::base::unique_fd device_fd;
-struct perms_ {
- char *name;
- char *attr;
- mode_t perm;
- unsigned int uid;
- unsigned int gid;
- unsigned short prefix;
- unsigned short wildcard;
-};
-
-struct perm_node {
- struct perms_ dp;
- struct listnode plist;
-};
-
-struct platform_node {
- char *name;
- char *path;
- int path_len;
- struct listnode list;
-};
-
-static list_declare(sys_perms);
-static list_declare(dev_perms);
-static list_declare(platform_names);
-
-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;
-
- node->dp.name = strdup(name);
- if (!node->dp.name) {
- free(node);
- return -ENOMEM;
+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) {
+ // If the first * is the last character, then we'll treat name_ as a prefix
+ // Otherwise, if a * is present, then we do a full fnmatch().
+ auto wildcard_position = name_.find('*');
+ if (wildcard_position == name_.length() - 1) {
+ prefix_ = true;
+ name_.pop_back();
+ } else if (wildcard_position != std::string::npos) {
+ wildcard_ = true;
}
-
- 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;
}
-static bool perm_path_matches(const char *path, struct perms_ *dp)
-{
- if (dp->prefix) {
- if (strncmp(path, dp->name, strlen(dp->name)) == 0)
- return true;
- } else if (dp->wildcard) {
- if (fnmatch(dp->name, path, FNM_PATHNAME) == 0)
- return true;
+bool Permissions::Match(const std::string& path) const {
+ if (prefix_) {
+ return android::base::StartsWith(path, name_.c_str());
+ } else if (wildcard_) {
+ return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
} else {
- if (strcmp(path, dp->name) == 0)
- return true;
+ return path == name_;
}
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) {
+bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
+ const std::string& subsystem) const {
+ std::string path_basename = android::base::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(INFO) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
+ << perm();
+ chown(attribute_file.c_str(), uid(), gid());
+ chmod(attribute_file.c_str(), perm());
+}
+
+// TODO: Move these to be member variables of a future devices class.
+std::vector<Permissions> dev_permissions;
+std::vector<SysfsPermissions> sysfs_permissions;
+
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs) {
+ if (is_sysfs && args.size() != 5) {
+ *err = "/sys/ lines must have 5 entries";
return false;
}
- std::string subsys_path = android::base::StringPrintf(pattern, subsystem, basename(path));
- return perm_path_matches(subsys_path.c_str(), dp);
+ if (!is_sysfs && args.size() != 4) {
+ *err = "/dev/ lines must have 4 entries";
+ return false;
+ }
+
+ auto it = args.begin();
+ const std::string& name = *it++;
+
+ std::string sysfs_attribute;
+ if (is_sysfs) sysfs_attribute = *it++;
+
+ // 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') {
+ *err = "invalid mode '" + perm_string + "'";
+ return false;
+ }
+
+ std::string& uid_string = *it++;
+ passwd* pwd = getpwnam(uid_string.c_str());
+ if (!pwd) {
+ *err = "invalid uid '" + uid_string + "'";
+ return false;
+ }
+ uid_t uid = pwd->pw_uid;
+
+ std::string& gid_string = *it++;
+ struct group* grp = getgrnam(gid_string.c_str());
+ if (!grp) {
+ *err = "invalid gid '" + gid_string + "'";
+ return false;
+ }
+ gid_t gid = grp->gr_gid;
+
+ if (is_sysfs) {
+ sysfs_permissions.emplace_back(name, sysfs_attribute, perm, uid, gid);
+ } else {
+ dev_permissions.emplace_back(name, perm, uid, gid);
+ }
+ return true;
}
-static void fixup_sys_perms(const char* upath, const char* subsystem) {
+// TODO: Move this to be a member variable of a future devices class.
+static std::vector<Subsystem> subsystems;
+
+std::string Subsystem::ParseDevPath(uevent* uevent) const {
+ std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
+ ? uevent->device_name
+ : android::base::Basename(uevent->path);
+
+ return dir_name_ + "/" + devname;
+}
+
+bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) {
+ if (args.size() != 2) {
+ *err = "subsystems must have exactly one name";
+ return false;
+ }
+
+ if (std::find(subsystems.begin(), subsystems.end(), args[1]) != subsystems.end()) {
+ *err = "ignoring duplicate subsystem entry";
+ return false;
+ }
+
+ subsystem_.name_ = args[1];
+
+ return true;
+}
+
+bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
+ if (args[1] == "uevent_devname") {
+ subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
+ return true;
+ }
+ if (args[1] == "uevent_devpath") {
+ subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
+ return true;
+ }
+
+ *err = "invalid devname '" + args[1] + "'";
+ return false;
+}
+
+bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
+ if (args[1].front() != '/') {
+ *err = "dirname '" + args[1] + " ' does not start with '/'";
+ return false;
+ }
+
+ subsystem_.dir_name_ = args[1];
+ return true;
+}
+
+bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+ using OptionParser =
+ bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
+ 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, err);
+
+ if (!parser) {
+ return false;
+ }
+
+ return (this->*parser)(std::move(args), err);
+}
+
+void SubsystemParser::EndSection() {
+ subsystems.emplace_back(std::move(subsystem_));
+}
+
+static void fixup_sys_permissions(const std::string& upath, const std::string& subsystem) {
// 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) {
@@ -182,63 +252,34 @@
}
}
-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;
+static std::tuple<mode_t, uid_t, gid_t> get_device_permissions(
+ const std::string& path, const std::vector<std::string>& links) {
+ // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
+ for (auto it = dev_permissions.rbegin(); it != dev_permissions.rend(); ++it) {
+ if (it->Match(path) || std::any_of(links.begin(), links.end(),
+ [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;
+static void make_device(const std::string& path, int block, int major, int minor,
+ const std::vector<std::string>& links) {
dev_t dev;
char *secontext = NULL;
- mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
+ auto [mode, uid, gid] = get_device_permissions(path, links);
+ mode |= (block ? S_IFBLK : S_IFCHR);
if (sehandle) {
- if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) {
+ std::vector<const char*> c_links;
+ for (const auto& link : links) {
+ c_links.emplace_back(link.c_str());
+ }
+ c_links.emplace_back(nullptr);
+ if (selabel_lookup_best_match(sehandle, &secontext, path.c_str(), &c_links[0], mode)) {
PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
return;
}
@@ -257,10 +298,9 @@
}
/* 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) {
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;
@@ -269,13 +309,13 @@
bool different = strcmp(fcon, secontext) != 0;
freecon(fcon);
- if (different && lsetfilecon(path, secontext)) {
+ if (different && lsetfilecon(path.c_str(), secontext)) {
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";
}
@@ -286,151 +326,86 @@
}
}
-static void add_platform_device(const char *path)
-{
- int path_len = strlen(path);
- struct platform_node *bus;
- const char *name = path;
+// TODO: Move this to be a member variable of a future devices class.
+std::vector<std::string> platform_devices;
- if (!strncmp(path, "/devices/", 9)) {
- name += 9;
- if (!strncmp(name, "platform/", 9))
- name += 9;
- }
-
- 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;
+// 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 false
+bool find_platform_device(const std::string& path, std::string* out_path) {
+ out_path->clear();
+ // platform_devices is searched backwards, since parents are added before their children,
+ // and we want to match as deep of a child as we can.
+ for (auto it = platform_devices.rbegin(); it != platform_devices.rend(); ++it) {
+ auto platform_device_path_length = it->length();
+ if (platform_device_path_length < path.length() &&
+ path[platform_device_path_length] == '/' &&
+ android::base::StartsWith(path, it->c_str())) {
+ *out_path = *it;
+ return true;
}
}
-}
-
-static void destroy_platform_devices() {
- struct listnode* node;
- struct listnode* n;
- struct platform_node* bus;
-
- 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);
- }
+ return false;
}
/* 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;
+static bool find_pci_device_prefix(const std::string& path, std::string* result) {
+ result->clear();
- if (strncmp(path, "/devices/pci", 12))
- return -1;
+ if (!android::base::StartsWith(path, "/devices/pci")) return false;
/* Beginning of the prefix is the initial "pci" after "/devices/" */
- start = path + 9;
+ std::string::size_type start = 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;
+ auto end = path.find('/', start);
+ if (end == std::string::npos) return false;
- /* Make sure we have enough room for the string plus null terminator */
- if (end - start + 1 > buf_sz)
- return -1;
+ end = path.find('/', end + 1);
+ if (end == std::string::npos) return false;
- strncpy(buf, start, end - start);
- buf[end - start] = '\0';
- return 0;
+ 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;
+ }
+
+ *result = path.substr(start, length);
+ return true;
}
/* 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;
+static bool find_vbd_device_prefix(const std::string& path, std::string* result) {
+ result->clear();
+
+ if (!android::base::StartsWith(path, "/devices/vbd-")) return false;
/* Beginning of the prefix is the initial "vbd-" after "/devices/" */
- if (strncmp(path, "/devices/vbd-", 13))
- return -1;
+ std::string::size_type start = 13;
/* 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;
+ auto end = path.find('/', start);
+ if (end == std::string::npos) return false;
- /* Make sure we have enough room for the string plus null terminator */
- if (end - start + 1 > buf_sz)
- return -1;
+ auto length = end - start;
+ if (length == 0) return false;
- strncpy(buf, start, end - start);
- buf[end - start] = '\0';
- return 0;
+ *result = path.substr(start, length);
+ return true;
}
-static void parse_event(const char *msg, struct uevent *uevent)
-{
- uevent->action = "";
- uevent->path = "";
- uevent->subsystem = "";
- uevent->firmware = "";
+void parse_event(const char* msg, uevent* uevent) {
+ uevent->partition_num = -1;
uevent->major = -1;
uevent->minor = -1;
- uevent->partition_name = NULL;
- uevent->partition_num = -1;
- uevent->device_name = NULL;
-
- /* currently ignoring SEQNUM */
+ // currently ignoring SEQNUM
while(*msg) {
if(!strncmp(msg, "ACTION=", 7)) {
msg += 7;
@@ -461,371 +436,228 @@
uevent->device_name = msg;
}
- /* advance to after the next \0 */
+ // 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);
+ LOG(INFO) << "event { '" << 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;
+std::vector<std::string> get_character_device_symlinks(uevent* uevent) {
+ std::string parent_device;
+ if (!find_platform_device(uevent->path, &parent_device)) return {};
- pdev = find_platform_device(uevent->path);
- if (!pdev)
- return NULL;
+ // skip path to the parent driver
+ std::string path = uevent->path.substr(parent_device.length());
- links = (char**) malloc(sizeof(char *) * 2);
- if (!links)
- return NULL;
- memset(links, 0, sizeof(char *) * 2);
+ if (!android::base::StartsWith(path, "/usb")) return {};
- /* skip "/devices/platform/<driver>" */
- parent = strchr(uevent->path + pdev->path_len, '/');
- if (!parent)
- goto err;
+ // skip root hub name and device. use device interface
+ // skip 3 slashes, including the first / by starting the search at the 1st character, not 0th.
+ // then extract what comes between the 3rd and 4th slash
+ // e.g. "/usb/usb_device/name/tty2-1:1.0" -> "name"
- 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;
+ std::string::size_type start = 0;
+ start = path.find('/', start + 1);
+ if (start == std::string::npos) return {};
- 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;
- }
+ start = path.find('/', start + 1);
+ if (start == std::string::npos) return {};
+
+ auto end = path.find('/', start + 1);
+ if (end == std::string::npos) return {};
+
+ start++; // Skip the first '/'
+
+ auto length = end - start;
+ if (length == 0) return {};
+
+ auto name_string = path.substr(start, length);
+
+ std::vector<std::string> links;
+ links.emplace_back("/dev/usb/" + uevent->subsystem + name_string);
+
+ mkdir("/dev/usb", 0755);
return links;
-err:
- free(links);
- return NULL;
}
-static 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;
+// replaces any unacceptable characters with '_', the
+// length of the resulting string is equal to the input string
+void sanitize_partition_name(std::string* string) {
+ const char* accept =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "_-.";
- pdev = find_platform_device(uevent->path);
- if (pdev) {
- device = pdev->name;
+ if (!string) return;
+
+ std::string::size_type pos = 0;
+ while ((pos = string->find_first_not_of(accept, pos)) != std::string::npos) {
+ (*string)[pos] = '_';
+ }
+}
+
+std::vector<std::string> get_block_device_symlinks(uevent* uevent) {
+ std::string device;
+ std::string type;
+
+ if (find_platform_device(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/";
+
+ if (android::base::StartsWith(device, devices_platform_prefix.c_str())) {
+ device = device.substr(devices_platform_prefix.length());
+ } else if (android::base::StartsWith(device, devices_prefix.c_str())) {
+ device = device.substr(devices_prefix.length());
+ }
+
type = "platform";
- } else if (!find_pci_device_prefix(uevent->path, buf, sizeof(buf))) {
- device = buf;
+ } else if (find_pci_device_prefix(uevent->path, &device)) {
type = "pci";
- } else if (!find_vbd_device_prefix(uevent->path, buf, sizeof(buf))) {
- device = buf;
+ } else if (find_vbd_device_prefix(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);
+ sanitize_partition_name(&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;
+ 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;
+static void make_link_init(const std::string& oldpath, const std::string& newpath) {
+ if (mkdir_recursive(dirname(newpath.c_str()), 0755)) {
+ PLOG(ERROR) << "Failed to create directory " << dirname(newpath.c_str());
+ }
- 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;
- }
+ if (symlink(oldpath.c_str(), newpath.c_str()) && 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 remove_link(const std::string& oldpath, const std::string& newpath) {
+ std::string path;
+ if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath.c_str());
}
-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]);
- }
+static void handle_device(const std::string& action, const std::string& devpath, int block,
+ int major, int minor, const std::vector<std::string>& links) {
+ if (action == "add") {
+ make_device(devpath, block, major, minor, links);
+ for (const auto& link : links) {
+ make_link_init(devpath, 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) {
+ remove_link(devpath, link);
}
- unlink(devpath);
- }
-
- if (links) {
- for (int i = 0; links[i]; i++) {
- free(links[i]);
- }
- free(links);
+ unlink(devpath.c_str());
}
}
-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;
+void handle_platform_device_event(uevent* uevent) {
+ if (uevent->action == "add") {
+ platform_devices.emplace_back(uevent->path);
+ } else if (uevent->action == "remove") {
+ auto it = std::find(platform_devices.begin(), platform_devices.end(), uevent->path);
+ if (it != platform_devices.end()) platform_devices.erase(it);
}
-
- return name;
}
-#define DEVPATH_LEN 96
-#define MAX_DEV_NAME 64
+static void handle_block_device_event(uevent* uevent) {
+ // if it's not a /dev device, nothing to do
+ if (uevent->major < 0 || uevent->minor < 0) return;
-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);
+ const char* base = "/dev/block/";
make_dir(base, 0755);
- if (!strncmp(uevent->path, "/devices/", 9))
+ std::string name = android::base::Basename(uevent->path);
+ std::string devpath = base + name;
+
+ std::vector<std::string> links;
+ if (android::base::StartsWith(uevent->path, "/devices")) {
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;
+
+ handle_device(uevent->action, devpath, 1, uevent->major, uevent->minor, links);
}
-static void mkdir_recursive_for_devpath(const char *devpath)
-{
- char dir[DEVPATH_LEN];
- char *slash;
+static void handle_generic_device_event(uevent* uevent) {
+ // if it's not a /dev device, nothing to do
+ if (uevent->major < 0 || uevent->minor < 0) return;
- strcpy(dir, devpath);
- slash = strrchr(dir, '/');
- *slash = '\0';
- mkdir_recursive(dir, 0755);
-}
+ std::string devpath;
-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";
+ if (android::base::StartsWith(uevent->subsystem, "usb")) {
+ 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 = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
+ }
+ } else {
+ // ignore other USB events
return;
}
+ } else if (auto subsystem = std::find(subsystems.begin(), subsystems.end(), uevent->subsystem);
+ subsystem != subsystems.end()) {
+ devpath = subsystem->ParseDevPath(uevent);
+ } else {
+ devpath = "/dev/" + android::base::Basename(uevent->path);
+ }
- 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);
+ mkdir_recursive(android::base::Dirname(devpath), 0755);
- if (!devpath[0])
- snprintf(devpath, sizeof(devpath), "%s%s", base, name);
+ auto links = get_character_device_symlinks(uevent);
- handle_device(uevent->action, devpath, uevent->path, 0,
- uevent->major, uevent->minor, links);
+ handle_device(uevent->action, devpath, 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 (uevent->action == "add" || uevent->action == "change" || uevent->action == "online") {
+ fixup_sys_permissions(uevent->path, uevent->subsystem);
+ }
- if (!strncmp(uevent->subsystem, "block", 5)) {
+ if (uevent->subsystem == "block") {
handle_block_device_event(uevent);
- } else if (!strncmp(uevent->subsystem, "platform", 8)) {
+ } else if (uevent->subsystem == "platform") {
handle_platform_device_event(uevent);
} else {
handle_generic_device_event(uevent);
@@ -858,7 +690,7 @@
LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
- std::string root = android::base::StringPrintf("/sys%s", uevent->path);
+ std::string root = "/sys" + uevent->path;
std::string loading = root + "/loading";
std::string data = root + "/data";
@@ -874,9 +706,12 @@
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 = android::base::StringPrintf("%s/%s", firmware_dirs[i], uevent->firmware);
+ std::string file = 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) {
@@ -900,8 +735,7 @@
}
static void handle_firmware_event(uevent* uevent) {
- if (strcmp(uevent->subsystem, "firmware")) return;
- if (strcmp(uevent->action, "add")) return;
+ if (uevent->subsystem != "firmware" || 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.)
@@ -935,7 +769,7 @@
msg[n] = '\0';
msg[n+1] = '\0';
- struct uevent uevent;
+ uevent uevent;
parse_event(msg, &uevent);
coldboot_action_t act = handle_uevent(&uevent);
if (should_stop_coldboot(act))
@@ -1079,7 +913,7 @@
}
void device_close() {
- destroy_platform_devices();
+ platform_devices.clear();
device_fd.reset();
selinux_status_close();
}
diff --git a/init/devices.h b/init/devices.h
index 26a064b..647b4c4 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,8 +17,14 @@
#ifndef _INIT_DEVICES_H
#define _INIT_DEVICES_H
-#include <functional>
#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "init_parser.h"
enum coldboot_action_t {
// coldboot continues without creating the device for the uevent
@@ -33,26 +39,103 @@
};
struct uevent {
- const char* action;
- const char* path;
- const char* subsystem;
- const char* firmware;
- const char* partition_name;
- const char* device_name;
+ 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;
};
+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_;
+};
+
+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_;
+};
+
+class Subsystem {
+ public:
+ friend class SubsystemParser;
+
+ Subsystem() {}
+
+ // 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(uevent* uevent) const;
+
+ bool operator==(const std::string& string_name) { 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 SubsystemParser : public SectionParser {
+ public:
+ SubsystemParser() {}
+ bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
+ std::string* err) override;
+ bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+ void EndSection() override;
+
+ private:
+ bool ParseDevName(std::vector<std::string>&& args, std::string* err);
+ bool ParseDirName(std::vector<std::string>&& args, std::string* err);
+
+ Subsystem subsystem_;
+};
+
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs);
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();
-
-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();
-#endif /* _INIT_DEVICES_H */
+// Exposed for testing
+extern std::vector<std::string> platform_devices;
+bool find_platform_device(const std::string& path, std::string* out_path);
+std::vector<std::string> get_character_device_symlinks(uevent* uevent);
+std::vector<std::string> get_block_device_symlinks(uevent* uevent);
+void sanitize_partition_name(std::string* string);
+void handle_platform_device_event(uevent* uevent);
+
+#endif /* _INIT_DEVICES_H */
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
new file mode 100644
index 0000000..66521db
--- /dev/null
+++ b/init/devices_test.cpp
@@ -0,0 +1,409 @@
+/*
+ * 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 <string>
+#include <vector>
+
+#include <android-base/scopeguard.h>
+#include <gtest/gtest.h>
+
+void add_platform_device(const std::string& path) {
+ uevent uevent = {
+ .action = "add", .subsystem = "platform", .path = path,
+ };
+ handle_platform_device_event(&uevent);
+}
+
+void remove_platform_device(const std::string& path) {
+ uevent uevent = {
+ .action = "remove", .subsystem = "platform", .path = path,
+ };
+ handle_platform_device_event(&uevent);
+}
+
+template <std::vector<std::string> (*Function)(uevent*)>
+void test_get_symlinks(const std::string& platform_device_name, uevent* uevent,
+ const std::vector<std::string> expected_links) {
+ add_platform_device(platform_device_name);
+ auto platform_device_remover = android::base::make_scope_guard(
+ [&platform_device_name]() { remove_platform_device(platform_device_name); });
+
+ std::vector<std::string> result = Function(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]);
+ }
+}
+
+TEST(devices, handle_platform_device_event) {
+ platform_devices.clear();
+ add_platform_device("/devices/platform/some_device_name");
+ ASSERT_EQ(1U, platform_devices.size());
+ remove_platform_device("/devices/platform/some_device_name");
+ ASSERT_EQ(0U, platform_devices.size());
+}
+
+TEST(devices, find_platform_device) {
+ platform_devices.clear();
+ add_platform_device("/devices/platform/some_device_name");
+ add_platform_device("/devices/platform/some_device_name/longer");
+ add_platform_device("/devices/platform/other_device_name");
+ EXPECT_EQ(3U, platform_devices.size());
+
+ std::string out_path;
+ EXPECT_FALSE(find_platform_device("/devices/platform/not_found", &out_path));
+ EXPECT_EQ("", out_path);
+
+ EXPECT_FALSE(
+ find_platform_device("/devices/platform/some_device_name_with_same_prefix", &out_path));
+
+ EXPECT_TRUE(
+ find_platform_device("/devices/platform/some_device_name/longer/longer_child", &out_path));
+ EXPECT_EQ("/devices/platform/some_device_name/longer", out_path);
+
+ EXPECT_TRUE(find_platform_device("/devices/platform/some_device_name/other_child", &out_path));
+ EXPECT_EQ("/devices/platform/some_device_name", out_path);
+}
+
+TEST(devices, get_character_device_symlinks_success) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
+ .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result{"/dev/usb/ttyname"};
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_pdev_match) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/device/name/tty2-1:1.0", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_nothing_after_platform_device) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_usb_found) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_roothub) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_usb_device) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_final_slash) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_final_name) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, 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"};
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, 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",
+ };
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, 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",
+ };
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, 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",
+ };
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, 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"};
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, 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{};
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, 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"};
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, 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{};
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, 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;
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, sanitize_null) {
+ sanitize_partition_name(nullptr);
+}
+
+TEST(devices, sanitize_empty) {
+ std::string empty;
+ sanitize_partition_name(&empty);
+ EXPECT_EQ(0u, empty.size());
+}
+
+TEST(devices, sanitize_allgood) {
+ std::string good =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "_-.";
+ std::string good_copy = good;
+ sanitize_partition_name(&good);
+ EXPECT_EQ(good_copy, good);
+}
+
+TEST(devices, sanitize_somebad) {
+ std::string string = "abc!@#$%^&*()";
+ sanitize_partition_name(&string);
+ EXPECT_EQ("abc__________", string);
+}
+
+TEST(devices, sanitize_allbad) {
+ std::string string = "!@#$%^&*()";
+ sanitize_partition_name(&string);
+ EXPECT_EQ("__________", string);
+}
+
+TEST(devices, sanitize_onebad) {
+ std::string string = ")";
+ sanitize_partition_name(&string);
+ EXPECT_EQ("_", string);
+}
+
+TEST(devices, 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(devices, 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(devices, 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(devices, 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(devices, 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(devices, 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(devices, 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());
+}
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index d52247b..99275e5 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -16,16 +16,12 @@
#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) {
+bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) {
if (args.size() != 2) {
*err = "single argument needed for import\n";
return false;
@@ -39,16 +35,18 @@
}
LOG(INFO) << "Added '" << conf_file << "' to import list";
- imports_.emplace_back(std::move(conf_file));
+ if (filename_.empty()) filename_ = filename;
+ imports_.emplace_back(std::move(conf_file), line);
return true;
}
-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
+ << "'";
}
}
}
diff --git a/init/import_parser.h b/init/import_parser.h
index 0e91025..45cbfad 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -23,21 +23,18 @@
#include <vector>
class ImportParser : public SectionParser {
-public:
- ImportParser() {
- }
- bool ParseSection(const std::vector<std::string>& args,
+ public:
+ ImportParser(Parser* parser) : parser_(parser) {}
+ bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
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_;
+ 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_;
};
#endif
diff --git a/init/init.cpp b/init/init.cpp
index 9cdbd19..731269f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "init.h"
+
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
@@ -40,7 +42,9 @@
#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>
@@ -50,16 +54,13 @@
#include <fstream>
#include <memory>
-#include <set>
#include <vector>
#include "action.h"
#include "bootchart.h"
#include "devices.h"
-#include "fs_mgr.h"
-#include "fs_mgr_avb.h"
#include "import_parser.h"
-#include "init.h"
+#include "init_first_stage.h"
#include "init_parser.h"
#include "keychords.h"
#include "log.h"
@@ -71,6 +72,7 @@
#include "util.h"
#include "watchdogd.h"
+using android::base::boot_clock;
using android::base::GetProperty;
using android::base::StringPrintf;
@@ -92,6 +94,11 @@
static std::string wait_prop_name;
static std::string wait_prop_value;
+void DumpState() {
+ ServiceManager::GetInstance().DumpState();
+ ActionManager::GetInstance().DumpState();
+}
+
void register_epoll_handler(int fd, void (*fn)()) {
epoll_event ev;
ev.events = EPOLLIN;
@@ -157,8 +164,8 @@
// waiting on a property.
if (name == "sys.powerctl") HandlePowerctlMessage(value);
- 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();
@@ -485,71 +492,10 @@
}
}
-/* Reads the content of device tree file into dt_value.
- * Returns true if the read is success, false otherwise.
- */
-static bool read_dt_file(const std::string& file_name, std::string* dt_value) {
- if (android::base::ReadFileToString(file_name, dt_value)) {
- if (!dt_value->empty()) {
- dt_value->pop_back(); // Trim the trailing '\0' out.
- return true;
- }
- }
- return false;
-}
-
-static const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
-
-static bool is_dt_value_expected(const std::string& dt_file_suffix,
- const std::string& expected_value) {
- std::string dt_value;
- std::string file_name = kAndroidDtDir + dt_file_suffix;
-
- if (read_dt_file(file_name, &dt_value)) {
- if (dt_value == expected_value) {
- return true;
- }
- }
- return false;
-}
-
-static inline bool is_dt_compatible() {
- return is_dt_value_expected("compatible", "android,firmware");
-}
-
-static inline bool is_dt_fstab_compatible() {
- return is_dt_value_expected("fstab/compatible", "android,fstab");
-}
-
-static inline bool is_dt_vbmeta_compatible() {
- return is_dt_value_expected("vbmeta/compatible", "android,vbmeta");
-}
-
-// Gets the vbmeta config from device tree. Specifically, the 'parts' and 'by_name_prefix'.
-// /{
-// firmware {
-// android {
-// vbmeta {
-// compatible = "android,vbmeta";
-// parts = "vbmeta,boot,system,vendor"
-// by_name_prefix="/dev/block/platform/soc.0/f9824900.sdhci/by-name/"
-// };
-// };
-// };
-// }
-static bool get_vbmeta_config_from_dt(std::string* vbmeta_partitions,
- std::string* device_file_by_name_prefix) {
- std::string file_name = kAndroidDtDir + "vbmeta/parts";
- if (!read_dt_file(file_name, vbmeta_partitions)) return false;
-
- file_name = kAndroidDtDir + "vbmeta/by_name_prefix";
- if (!read_dt_file(file_name, device_file_by_name_prefix)) return false;
-
- return true;
-}
-
static void process_kernel_dt() {
- if (!is_dt_compatible()) return;
+ if (!is_android_dt_value_expected("compatible", "android,firmware")) {
+ return;
+ }
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(kAndroidDtDir.c_str()), closedir);
if (!dir) return;
@@ -589,7 +535,7 @@
static int queue_property_triggers_action(const std::vector<std::string>& args)
{
ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
- ActionManager::GetInstance().QueueAllPropertyTriggers();
+ ActionManager::GetInstance().QueueAllPropertyActions();
return 0;
}
@@ -942,6 +888,9 @@
LOG(INFO) << "Running restorecon...";
restorecon("/dev");
restorecon("/dev/kmsg");
+ if constexpr (WORLD_WRITABLE_KMSG) {
+ restorecon("/dev/kmsg_debug");
+ }
restorecon("/dev/socket");
restorecon("/dev/random");
restorecon("/dev/urandom");
@@ -982,289 +931,6 @@
}
}
-// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-static void device_init_dm_device(const std::string& dm_device) {
- const std::string device_name(basename(dm_device.c_str()));
- const std::string syspath = "/sys/block/" + device_name;
-
- device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
- if (uevent->device_name && device_name == uevent->device_name) {
- LOG(VERBOSE) << "early_mount: creating dm-verity device : " << dm_device;
- return COLDBOOT_STOP;
- }
- return COLDBOOT_CONTINUE;
- });
- device_close();
-}
-
-static bool vboot_1_0_mount_partitions(const std::vector<fstab_rec*>& fstab_recs) {
- if (fstab_recs.empty()) return false;
-
- for (auto rec : fstab_recs) {
- bool need_create_dm_device = false;
- if (fs_mgr_is_verified(rec)) {
- // setup verity and create the dm-XX block device
- // needed to mount this partition
- int ret = fs_mgr_setup_verity(rec, false /* wait_for_verity_dev */);
- if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
- LOG(INFO) << "verity disabled for '" << rec->mount_point << "'";
- } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
- need_create_dm_device = true;
- } else {
- PLOG(ERROR) << "early_mount: failed to setup verity for '" << rec->mount_point
- << "'";
- return false;
- }
- }
- if (need_create_dm_device) {
- // The exact block device name (rec->blk_device) is changed to "/dev/block/dm-XX".
- // Need to create it because ueventd isn't started during early mount.
- device_init_dm_device(rec->blk_device);
- }
- if (fs_mgr_do_mount_one(rec)) {
- PLOG(ERROR) << "early_mount: failed to mount '" << rec->mount_point << "'";
- return false;
- }
- }
-
- return true;
-}
-
-static bool vboot_2_0_mount_partitions(const std::vector<fstab_rec*>& fstab_recs,
- const std::string& device_file_by_name_prefix) {
- if (fstab_recs.empty()) return false;
-
- FsManagerAvbUniquePtr avb_handle = FsManagerAvbHandle::Open(device_file_by_name_prefix);
- if (!avb_handle) {
- LOG(INFO) << "Failed to Open FsManagerAvbHandle";
- return false;
- }
-
- setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
- for (auto rec : fstab_recs) {
- bool need_create_dm_device = false;
- if (fs_mgr_is_avb(rec)) {
- if (avb_handle->hashtree_disabled()) {
- LOG(INFO) << "avb hashtree disabled for '" << rec->mount_point << "'";
- } else if (avb_handle->SetUpAvb(rec, false /* wait_for_verity_dev */)) {
- need_create_dm_device = true;
- } else {
- PLOG(ERROR) << "early_mount: failed to set up AVB on partition: '"
- << rec->mount_point << "'";
- return false;
- }
- }
- if (need_create_dm_device) {
- // The exact block device name (rec->blk_device) is changed to "/dev/block/dm-XX".
- // Need to create it because ueventd isn't started during early mount.
- device_init_dm_device(rec->blk_device);
- }
- if (fs_mgr_do_mount_one(rec)) {
- PLOG(ERROR) << "early_mount: failed to mount '" << rec->mount_point << "'";
- return false;
- }
- }
-
- return true;
-}
-
-static bool mount_early_partitions(const std::vector<fstab_rec*>& fstab_recs,
- const std::string& device_file_by_name_prefix) {
- if (is_dt_vbmeta_compatible()) { // AVB (external/avb) is used to setup dm-verity.
- return vboot_2_0_mount_partitions(fstab_recs, device_file_by_name_prefix);
- } else {
- return vboot_1_0_mount_partitions(fstab_recs);
- }
-}
-
-// Creates devices with uevent->partition_name matching one in the in/out
-// partition_names. Note that the partition_names MUST have A/B suffix
-// when A/B is used. Found partitions will then be removed from the
-// partition_names for caller to check which devices are NOT created.
-static void early_device_init(std::set<std::string>* partition_names) {
- if (partition_names->empty()) {
- return;
- }
- device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
- if (!strncmp(uevent->subsystem, "firmware", 8)) {
- return COLDBOOT_CONTINUE;
- }
-
- // we need platform devices to create symlinks
- if (!strncmp(uevent->subsystem, "platform", 8)) {
- return COLDBOOT_CREATE;
- }
-
- // Ignore everything that is not a block device
- if (strncmp(uevent->subsystem, "block", 5)) {
- return COLDBOOT_CONTINUE;
- }
-
- if (uevent->partition_name) {
- // match partition names to create device nodes for partitions
- // both partition_names and uevent->partition_name have A/B suffix when A/B is used
- auto iter = partition_names->find(uevent->partition_name);
- if (iter != partition_names->end()) {
- LOG(VERBOSE) << "early_mount: found partition: " << *iter;
- partition_names->erase(iter);
- if (partition_names->empty()) {
- return COLDBOOT_STOP; // found all partitions, stop coldboot
- } else {
- return COLDBOOT_CREATE; // create this device and continue to find others
- }
- }
- }
- // Not found a partition or find an unneeded partition, continue to find others
- return COLDBOOT_CONTINUE;
- });
-}
-
-static bool vboot_1_0_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
- std::set<std::string>* out_partitions,
- bool* out_need_verity) {
- std::string meta_partition;
- for (auto fstab_rec : early_fstab_recs) {
- // don't allow verifyatboot for early mounted partitions
- if (fs_mgr_is_verifyatboot(fstab_rec)) {
- LOG(ERROR) << "early_mount: partitions can't be verified at boot";
- return false;
- }
- // check for verified partitions
- if (fs_mgr_is_verified(fstab_rec)) {
- *out_need_verity = true;
- }
- // check if verity metadata is on a separate partition and get partition
- // name from the end of the ->verity_loc path. verity state is not partition
- // specific, so there must be only 1 additional partition that carries
- // verity state.
- if (fstab_rec->verity_loc) {
- if (!meta_partition.empty()) {
- LOG(ERROR) << "early_mount: more than one meta partition found: " << meta_partition
- << ", " << basename(fstab_rec->verity_loc);
- return false;
- } else {
- meta_partition = basename(fstab_rec->verity_loc);
- }
- }
- }
-
- // includes those early mount partitions and meta_partition (if any)
- // note that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used
- for (auto fstab_rec : early_fstab_recs) {
- out_partitions->emplace(basename(fstab_rec->blk_device));
- }
-
- if (!meta_partition.empty()) {
- out_partitions->emplace(std::move(meta_partition));
- }
-
- return true;
-}
-
-// a.k.a. AVB (external/avb)
-static bool vboot_2_0_early_partitions(std::set<std::string>* out_partitions, bool* out_need_verity,
- std::string* out_device_file_by_name_prefix) {
- std::string vbmeta_partitions;
- if (!get_vbmeta_config_from_dt(&vbmeta_partitions, out_device_file_by_name_prefix)) {
- return false;
- }
- // libavb verifies AVB metadata on all verified partitions at once.
- // e.g., The vbmeta_partitions will be "vbmeta,boot,system,vendor"
- // for libavb to verify metadata, even if we only need to early mount /vendor.
- std::vector<std::string> partitions = android::base::Split(vbmeta_partitions, ",");
- std::string ab_suffix = fs_mgr_get_slot_suffix();
- for (const auto& partition : partitions) {
- out_partitions->emplace(partition + ab_suffix);
- }
- *out_need_verity = true;
- return true;
-}
-
-static bool get_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
- std::set<std::string>* out_partitions, bool* out_need_verity,
- std::string* out_device_file_by_name_prefix) {
- *out_need_verity = false;
- out_partitions->clear();
- out_device_file_by_name_prefix->clear();
-
- if (is_dt_vbmeta_compatible()) { // AVB (external/avb) is used to setup dm-verity.
- return vboot_2_0_early_partitions(out_partitions, out_need_verity,
- out_device_file_by_name_prefix);
- } else {
- return vboot_1_0_early_partitions(early_fstab_recs, out_partitions, out_need_verity);
- }
-}
-
-/* Early mount vendor and ODM partitions. The fstab is read from device-tree. */
-static bool early_mount() {
- // skip early mount if we're in recovery mode
- if (access("/sbin/recovery", F_OK) == 0) {
- LOG(INFO) << "Early mount skipped (recovery mode)";
- return true;
- }
-
- // first check if device tree fstab entries are compatible
- if (!is_dt_fstab_compatible()) {
- LOG(INFO) << "Early mount skipped (missing/incompatible fstab in device tree)";
- return true;
- }
-
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> tab(
- fs_mgr_read_fstab_dt(), fs_mgr_free_fstab);
- if (!tab) {
- LOG(ERROR) << "Early mount failed to read fstab from device tree";
- return false;
- }
-
- // find out fstab records for odm, system and vendor
- std::vector<fstab_rec*> early_fstab_recs;
- for (auto mount_point : {"/odm", "/system", "/vendor"}) {
- fstab_rec* fstab_rec = fs_mgr_get_entry_for_mount_point(tab.get(), mount_point);
- if (fstab_rec != nullptr) {
- early_fstab_recs.push_back(fstab_rec);
- }
- }
-
- // nothing to early mount
- if (early_fstab_recs.empty()) return true;
-
- bool need_verity;
- std::string device_file_by_name_prefix;
- std::set<std::string> partition_names;
- // partition_names MUST have A/B suffix when A/B is used
- if (!get_early_partitions(early_fstab_recs, &partition_names, &need_verity,
- &device_file_by_name_prefix)) {
- return false;
- }
-
- bool success = false;
- // create the devices we need..
- early_device_init(&partition_names);
-
- // early_device_init will remove found partitions from partition_names
- // So if the partition_names is not empty here, means some partitions
- // are not found
- if (!partition_names.empty()) {
- LOG(ERROR) << "early_mount: partition(s) not found: "
- << android::base::Join(partition_names, ", ");
- goto done;
- }
-
- if (need_verity) {
- // create /dev/device mapper
- device_init("/sys/devices/virtual/misc/device-mapper",
- [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
- }
-
- if (mount_early_partitions(early_fstab_recs, device_file_by_name_prefix)) {
- success = true;
- }
-
-done:
- device_close();
- return success;
-}
-
static void install_reboot_signal_handlers() {
// 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
@@ -1327,7 +993,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));
@@ -1337,11 +1009,13 @@
LOG(INFO) << "init first stage started!";
- if (!early_mount()) {
+ if (!DoFirstStageMount()) {
LOG(ERROR) << "Failed to mount required partitions early ...";
panic();
}
+ SetInitAvbVersionInRecovery();
+
// Set up SELinux, loading the SELinux policy.
selinux_initialize(true);
@@ -1420,10 +1094,13 @@
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
+ ActionManager& am = ActionManager::GetInstance();
+ ServiceManager& sm = ServiceManager::GetInstance();
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>());
+
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+ parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
@@ -1441,9 +1118,7 @@
// 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");
@@ -1478,10 +1153,10 @@
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
- if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+ if (!(waiting_for_prop || sm.IsWaitingForExec())) {
am.ExecuteOneCommand();
}
- if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+ if (!(waiting_for_prop || sm.IsWaitingForExec())) {
restart_processes();
// If there's a process that needs restarting, wake up in time for that.
diff --git a/init/init.h b/init/init.h
index 1da3350..6add75f 100644
--- a/init/init.h
+++ b/init/init.h
@@ -34,4 +34,6 @@
bool start_waiting_for_property(const char *name, const char *value);
+void DumpState();
+
#endif /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
new file mode 100644
index 0000000..43f1c15
--- /dev/null
+++ b/init/init_first_stage.cpp
@@ -0,0 +1,433 @@
+/*
+ * 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 "init_first_stage.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "devices.h"
+#include "fs_mgr.h"
+#include "fs_mgr_avb.h"
+#include "util.h"
+
+// Class Declarations
+// ------------------
+class FirstStageMount {
+ public:
+ FirstStageMount();
+ virtual ~FirstStageMount() = default;
+
+ // The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2
+ // based on device tree configurations.
+ static std::unique_ptr<FirstStageMount> Create();
+ bool DoFirstStageMount(); // Mounts fstab entries read from device tree.
+ bool InitDevices();
+
+ protected:
+ void InitRequiredDevices(std::set<std::string>* devices_partition_names);
+ void InitVerityDevice(const std::string& verity_device);
+ bool MountPartitions();
+
+ virtual bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) = 0;
+ virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
+
+ // 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_;
+};
+
+class FirstStageMountVBootV1 : public FirstStageMount {
+ public:
+ FirstStageMountVBootV1() = default;
+ ~FirstStageMountVBootV1() override = default;
+
+ protected:
+ bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) override;
+ bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+};
+
+class FirstStageMountVBootV2 : public FirstStageMount {
+ public:
+ FirstStageMountVBootV2();
+ ~FirstStageMountVBootV2() override = default;
+
+ const std::string& by_name_prefix() const { return device_tree_by_name_prefix_; }
+
+ protected:
+ bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) override;
+ bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+ bool InitAvbHandle();
+
+ std::string device_tree_vbmeta_parts_;
+ std::string device_tree_by_name_prefix_;
+ FsManagerAvbUniquePtr avb_handle_;
+};
+
+// Static Functions
+// ----------------
+static inline bool IsDtVbmetaCompatible() {
+ return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
+}
+
+static bool inline IsRecoveryMode() {
+ return access("/sbin/recovery", F_OK) == 0;
+}
+
+// Class Definitions
+// -----------------
+FirstStageMount::FirstStageMount() : device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
+ if (!device_tree_fstab_) {
+ 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);
+ }
+ }
+}
+
+std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
+ if (IsDtVbmetaCompatible()) {
+ return std::make_unique<FirstStageMountVBootV2>();
+ } else {
+ return std::make_unique<FirstStageMountVBootV1>();
+ }
+}
+
+bool FirstStageMount::DoFirstStageMount() {
+ // Nothing to mount.
+ if (mount_fstab_recs_.empty()) return true;
+
+ if (!InitDevices()) return false;
+
+ if (!MountPartitions()) return false;
+
+ return true;
+}
+
+bool FirstStageMount::InitDevices() {
+ bool need_dm_verity;
+ std::set<std::string> devices_partition_names;
+
+ // The partition name in devices_partition_names MUST have A/B suffix when A/B is used.
+ if (!GetRequiredDevices(&devices_partition_names, &need_dm_verity)) return false;
+
+ if (need_dm_verity) {
+ device_init("/sys/devices/virtual/misc/device-mapper",
+ [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
+ }
+
+ bool success = false;
+ InitRequiredDevices(&devices_partition_names);
+
+ // InitRequiredDevices() will remove found partitions from devices_partition_names.
+ // So if it isn't empty here, it means some partitions are not found.
+ if (!devices_partition_names.empty()) {
+ LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
+ << android::base::Join(devices_partition_names, ", ");
+ } else {
+ success = true;
+ }
+
+ device_close();
+ return success;
+}
+
+// Creates devices with uevent->partition_name matching one in the in/out
+// devices_partition_names. Found partitions will then be removed from the
+// devices_partition_names for the caller to check which devices are NOT created.
+void FirstStageMount::InitRequiredDevices(std::set<std::string>* devices_partition_names) {
+ if (devices_partition_names->empty()) {
+ return;
+ }
+ device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
+ // We need platform devices to create symlinks.
+ if (uevent->subsystem == "platform") {
+ return COLDBOOT_CREATE;
+ }
+
+ // Ignores everything that is not a block device.
+ if (uevent->subsystem != "block") {
+ return COLDBOOT_CONTINUE;
+ }
+
+ if (!uevent->partition_name.empty()) {
+ // Matches partition name to create device nodes.
+ // Both devices_partition_names and uevent->partition_name have A/B
+ // suffix when A/B is used.
+ auto iter = devices_partition_names->find(uevent->partition_name);
+ if (iter != devices_partition_names->end()) {
+ LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
+ devices_partition_names->erase(iter);
+ if (devices_partition_names->empty()) {
+ return COLDBOOT_STOP; // Found all partitions, stop coldboot.
+ } else {
+ return COLDBOOT_CREATE; // Creates this device and continue to find others.
+ }
+ }
+ }
+ // Not found a partition or find an unneeded partition, continue to find others.
+ return COLDBOOT_CONTINUE;
+ });
+}
+
+// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
+void 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;
+
+ device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
+ if (uevent->device_name == device_name) {
+ LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
+ return COLDBOOT_STOP;
+ }
+ return COLDBOOT_CONTINUE;
+ });
+ device_close();
+}
+
+bool FirstStageMount::MountPartitions() {
+ for (auto fstab_rec : mount_fstab_recs_) {
+ if (!SetUpDmVerity(fstab_rec)) {
+ PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
+ return false;
+ }
+ if (fs_mgr_do_mount_one(fstab_rec)) {
+ PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool FirstStageMountVBootV1::GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) {
+ std::string verity_loc_device;
+ *out_need_dm_verity = false;
+
+ for (auto fstab_rec : mount_fstab_recs_) {
+ // Don't allow verifyatboot in the first stage.
+ if (fs_mgr_is_verifyatboot(fstab_rec)) {
+ LOG(ERROR) << "Partitions can't be verified at boot";
+ return false;
+ }
+ // Checks for verified partitions.
+ if (fs_mgr_is_verified(fstab_rec)) {
+ *out_need_dm_verity = true;
+ }
+ // Checks if verity metadata is on a separate partition. Note that it is
+ // not partition specific, so there must be only one additional partition
+ // that carries verity state.
+ if (fstab_rec->verity_loc) {
+ if (verity_loc_device.empty()) {
+ verity_loc_device = fstab_rec->verity_loc;
+ } else if (verity_loc_device != fstab_rec->verity_loc) {
+ LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", "
+ << fstab_rec->verity_loc;
+ return false;
+ }
+ }
+ }
+
+ // Includes the partition names of fstab records and verity_loc_device (if any).
+ // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
+ for (auto fstab_rec : mount_fstab_recs_) {
+ out_devices_partition_names->emplace(basename(fstab_rec->blk_device));
+ }
+
+ if (!verity_loc_device.empty()) {
+ out_devices_partition_names->emplace(basename(verity_loc_device.c_str()));
+ }
+
+ return true;
+}
+
+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;
+ }
+ }
+ return true; // Returns true to mount the partition.
+}
+
+// FirstStageMountVBootV2 constructor.
+// Gets the vbmeta configurations from device tree.
+// Specifically, the 'parts' and 'by_name_prefix' below.
+// /{
+// firmware {
+// android {
+// vbmeta {
+// compatible = "android,vbmeta";
+// parts = "vbmeta,boot,system,vendor"
+// by_name_prefix = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/"
+// };
+// };
+// };
+// }
+FirstStageMountVBootV2::FirstStageMountVBootV2() : avb_handle_(nullptr) {
+ if (!read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts_)) {
+ PLOG(ERROR) << "Failed to read vbmeta/parts from device tree";
+ return;
+ }
+
+ // TODO: removes by_name_prefix to allow partitions on different block devices.
+ if (!read_android_dt_file("vbmeta/by_name_prefix", &device_tree_by_name_prefix_)) {
+ PLOG(ERROR) << "Failed to read vbmeta/by_name_prefix from dt";
+ return;
+ }
+}
+
+bool FirstStageMountVBootV2::GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) {
+ *out_need_dm_verity = false;
+
+ // fstab_rec->blk_device has A/B suffix.
+ for (auto fstab_rec : mount_fstab_recs_) {
+ if (fs_mgr_is_avb(fstab_rec)) {
+ *out_need_dm_verity = true;
+ }
+ out_devices_partition_names->emplace(basename(fstab_rec->blk_device));
+ }
+
+ // libavb verifies AVB metadata on all verified partitions at once.
+ // e.g., The device_tree_vbmeta_parts_ will be "vbmeta,boot,system,vendor"
+ // for libavb to verify metadata, even if there is only /vendor in the
+ // above mount_fstab_recs_.
+ if (*out_need_dm_verity) {
+ if (device_tree_vbmeta_parts_.empty()) {
+ LOG(ERROR) << "Missing vbmeta parts in device tree";
+ return false;
+ }
+ std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ",");
+ std::string ab_suffix = fs_mgr_get_slot_suffix();
+ for (const auto& partition : partitions) {
+ // out_devices_partition_names is of type std::set so it's not an issue to emplace
+ // a partition twice. e.g., /vendor might be in both places:
+ // - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor"
+ // - mount_fstab_recs_: /vendor_a
+ out_devices_partition_names->emplace(partition + ab_suffix);
+ }
+ }
+ return true;
+}
+
+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;
+ }
+ }
+ return true; // Returns true to mount the partition.
+}
+
+bool FirstStageMountVBootV2::InitAvbHandle() {
+ if (avb_handle_) return true; // Returns true if the handle is already initialized.
+
+ avb_handle_ = FsManagerAvbHandle::Open(device_tree_by_name_prefix_);
+ if (!avb_handle_) {
+ PLOG(ERROR) << "Failed to open FsManagerAvbHandle";
+ return false;
+ }
+ // Sets INIT_AVB_VERSION here for init to set ro.boot.avb_version in the second stage.
+ setenv("INIT_AVB_VERSION", avb_handle_->avb_version().c_str(), 1);
+ return true;
+}
+
+// Public functions
+// ----------------
+// Mounts /system, /vendor, and/or /odm if they are present in the fstab provided by device tree.
+bool DoFirstStageMount() {
+ // Skips first stage mount if we're in recovery mode.
+ if (IsRecoveryMode()) {
+ LOG(INFO) << "First stage mount skipped (recovery mode)";
+ return true;
+ }
+
+ // Firstly checks if device tree fstab entries are compatible.
+ if (!is_android_dt_value_expected("fstab/compatible", "android,fstab")) {
+ LOG(INFO) << "First stage mount skipped (missing/incompatible fstab in device tree)";
+ return true;
+ }
+
+ std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create();
+ if (!handle) {
+ LOG(ERROR) << "Failed to create FirstStageMount";
+ return false;
+ }
+ return handle->DoFirstStageMount();
+}
+
+void SetInitAvbVersionInRecovery() {
+ if (!IsRecoveryMode()) {
+ LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not in recovery mode)";
+ return;
+ }
+
+ if (!IsDtVbmetaCompatible()) {
+ LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)";
+ return;
+ }
+
+ // Initializes required devices for the subsequent FsManagerAvbHandle::Open()
+ // to verify AVB metadata on all partitions in the verified chain.
+ // We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the
+ // Open() function returns a valid handle.
+ // We don't need to mount partitions here in recovery mode.
+ FirstStageMountVBootV2 avb_first_mount;
+ if (!avb_first_mount.InitDevices()) {
+ LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION";
+ return;
+ }
+
+ FsManagerAvbUniquePtr avb_handle = FsManagerAvbHandle::Open(avb_first_mount.by_name_prefix());
+ if (!avb_handle) {
+ PLOG(ERROR) << "Failed to open FsManagerAvbHandle for INIT_AVB_VERSION";
+ return;
+ }
+ setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
+}
diff --git a/bootstat/uptime_parser.h b/init/init_first_stage.h
similarity index 65%
rename from bootstat/uptime_parser.h
rename to init/init_first_stage.h
index 756ae9b..170a24c 100644
--- a/bootstat/uptime_parser.h
+++ b/init/init_first_stage.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,16 +14,10 @@
* limitations under the License.
*/
-#ifndef UPTIME_PARSER_H_
-#define UPTIME_PARSER_H_
+#ifndef _INIT_FIRST_STAGE_H
+#define _INIT_FIRST_STAGE_H
-#include <time.h>
+bool DoFirstStageMount();
+void SetInitAvbVersionInRecovery();
-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
+#endif
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index a192862..620367a 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -14,18 +14,16 @@
* 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 <dirent.h>
+
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "parser.h"
+#include "util.h"
Parser::Parser() {
}
@@ -40,13 +38,16 @@
section_parsers_[name] = std::move(parser);
}
+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.filename = filename.c_str();
state.line = 0;
state.ptr = &data_copy[0];
state.nexttoken = 0;
@@ -66,21 +67,34 @@
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();
+
+ std::string ret_err;
+ if (!callback(std::move(args), &ret_err)) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
+ }
+ section_parser = nullptr;
+ 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());
+ if (!section_parser->ParseSection(std::move(args), filename, state.line, &ret_err)) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
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());
+ if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
}
}
args.clear();
@@ -102,8 +116,8 @@
data.push_back('\n'); // TODO: fix parse_config.
ParseData(path, data);
- for (const auto& sp : section_parsers_) {
- sp.second->EndFile(path);
+ for (const auto& [section_name, section_parser] : section_parsers_) {
+ section_parser->EndFile();
}
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
@@ -143,8 +157,3 @@
}
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
index f66ba52..bd8a178 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -18,50 +18,76 @@
#define _INIT_INIT_PARSER_H_
#include <map>
+#include <memory>
#include <string>
#include <vector>
+// 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.
+
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;
+ public:
+ virtual ~SectionParser() {}
+ virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) = 0;
+ virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
+ virtual void EndSection(){};
+ virtual void EndFile(){};
};
class Parser {
-public:
+ 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<bool(std::vector<std::string>&&, std::string*)>;
+
static Parser& GetInstance();
- void DumpState() const;
+
+ // Exposed for testing
+ Parser();
+
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;
- }
+ void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
+ void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+ 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();
-
+ 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_;
bool is_system_etc_init_loaded_ = false;
bool is_vendor_etc_init_loaded_ = false;
bool is_odm_etc_init_loaded_ = false;
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
index 52aaa37..d8fd2ba 100644
--- a/init/init_parser_test.cpp
+++ b/init/init_parser_test.cpp
@@ -18,9 +18,7 @@
#include "init.h"
#include "service.h"
-#include "util.h"
-#include <errno.h>
#include <gtest/gtest.h>
#include <string>
diff --git a/init/init_test.cpp b/init/init_test.cpp
new file mode 100644
index 0000000..3da14b5
--- /dev/null
+++ b/init/init_test.cpp
@@ -0,0 +1,186 @@
+/*
+ * 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 "init_parser.h"
+#include "keyword_map.h"
+#include "util.h"
+
+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 0;
+ });
+ }
+
+ 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
+ // write_file() ensures the right mode is set
+ ASSERT_TRUE(write_file(std::string(dir.path) + "/a.rc", dir_a_script));
+
+ ASSERT_TRUE(write_file(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 0;
+ };
+
+ 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);
+}
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 5801ea8..c572cee 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -14,19 +14,17 @@
* limitations under the License.
*/
-#include <errno.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"
static struct input_keychord *keychords = 0;
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 693d82a..88bad01 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -24,18 +24,23 @@
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 Function FindFunction(const std::vector<std::string>& args, std::string* err) const {
using android::base::StringPrintf;
+ if (args.empty()) {
+ *err = "keyword needed, but not provided";
+ return nullptr;
+ }
+ 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());
@@ -68,10 +73,10 @@
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;
};
#endif
diff --git a/init/log.h b/init/log.h
index 8fa6d74..29a27af 100644
--- a/init/log.h
+++ b/init/log.h
@@ -17,7 +17,7 @@
#ifndef _INIT_LOG_H_
#define _INIT_LOG_H_
-#include <android-base/logging.h>
+#include <sys/cdefs.h>
void InitKernelLogging(char* argv[]);
diff --git a/init/parser.cpp b/init/parser.cpp
index 45862b7..0d13cfe 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -1,28 +1,5 @@
#include "parser.h"
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "log.h"
-
-void parse_error(struct parse_state *state, const char *fmt, ...)
-{
- va_list ap;
- char buf[128];
- int off;
-
- snprintf(buf, sizeof(buf), "%s: %d: ", state->filename, state->line);
- buf[127] = 0;
- off = strlen(buf);
-
- va_start(ap, fmt);
- vsnprintf(buf + off, 128 - off, fmt, ap);
- va_end(ap);
- buf[127] = 0;
- LOG(ERROR) << buf;
-}
-
int next_token(struct parse_state *state)
{
char *x = state->ptr;
diff --git a/init/parser.h b/init/parser.h
index 95e1164..3dcc566 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -27,14 +27,8 @@
char *text;
int line;
int nexttoken;
- void *context;
- void (*parse_line)(struct parse_state *state, int nargs, char **args);
- const char *filename;
- void *priv;
};
-void dump_parser_state(void);
int next_token(struct parse_state *state);
-void parse_error(struct parse_state *state, const char *fmt, ...);
#endif /* PARSER_H_ */
diff --git a/init/property_service.cpp b/init/property_service.cpp
index bbe353f..aa47976 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -14,47 +14,46 @@
* limitations under the License.
*/
+#include "property_service.h"
+
+#include <ctype.h>
+#include <dirent.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 <selinux/android.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
+#include <memory>
+#include <vector>
#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 "util.h"
-#include "log.h"
using android::base::StringPrintf;
diff --git a/init/property_service.h b/init/property_service.h
index 994da63..9a5b6f6 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -17,9 +17,8 @@
#ifndef _INIT_PROPERTY_H
#define _INIT_PROPERTY_H
-#include <stddef.h>
#include <sys/socket.h>
-#include <sys/system_properties.h>
+
#include <string>
struct property_audit_data {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index c997519..838406d 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -13,6 +13,9 @@
* 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>
@@ -29,11 +32,11 @@
#include <memory>
#include <set>
-#include <string>
#include <thread>
#include <vector>
#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>
@@ -45,11 +48,8 @@
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
-#include "log.h"
#include "property_service.h"
-#include "reboot.h"
#include "service.h"
-#include "util.h"
using android::base::StringPrintf;
diff --git a/init/reboot.h b/init/reboot.h
index cd6be39..b304b3c 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -17,6 +17,8 @@
#ifndef _INIT_REBOOT_H
#define _INIT_REBOOT_H
+#include <string>
+
/* Reboot / shutdown the system.
* cmd ANDROID_RB_* as defined in android_reboot.h
* reason Reason string like "reboot", "userrequested"
diff --git a/init/service.cpp b/init/service.cpp
index c20e644..39f6709 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -24,30 +24,27 @@
#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/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 "util.h"
+using android::base::boot_clock;
using android::base::ParseInt;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
@@ -160,6 +157,7 @@
gid_(0),
namespace_flags_(0),
seclabel_(""),
+ onrestart_(false, "<Service '" + name + "' onrestart>", 0),
ioprio_class_(IoSchedClass_NONE),
ioprio_pri_(0),
priority_(0),
@@ -183,6 +181,7 @@
capabilities_(capabilities),
namespace_flags_(namespace_flags),
seclabel_(seclabel),
+ onrestart_(false, "<Service '" + name + "' onrestart>", 0),
ioprio_class_(IoSchedClass_NONE),
ioprio_pri_(0),
priority_(0),
@@ -218,7 +217,7 @@
r = killProcessGroup(uid_, pid_, signal);
}
if (r == -1) {
- PLOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
+ LOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
}
if (kill(-pid_, signal) == -1) {
PLOG(ERROR) << "kill(" << pid_ << ", " << signal << ") failed";
@@ -437,7 +436,8 @@
bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
std::vector<std::string> str_args(args.begin() + 1, args.end());
- onrestart_.AddCommand(str_args, "", 0, err);
+ int line = onrestart_.NumCommands() + 1;
+ onrestart_.AddCommand(str_args, line, err);
return true;
}
@@ -530,14 +530,14 @@
}
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 = {
@@ -568,13 +568,8 @@
}
bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
- if (args.empty()) {
- *err = "option needed, but not provided";
- return false;
- }
-
static const OptionParserMap parser_map;
- auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);
+ auto parser = parser_map.FindFunction(args, err);
if (!parser) {
return false;
@@ -635,7 +630,6 @@
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
- LOG(INFO) << "computing context for service '" << name_ << "'";
scon = ComputeContextFromExecutable(name_, args_[0]);
if (scon == "") {
return false;
@@ -873,11 +867,6 @@
}
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;
- }
services_.emplace_back(std::move(service));
}
@@ -932,7 +921,9 @@
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());
+ std::string name =
+ "exec " + std::to_string(exec_count_) + " (" + android::base::Join(str_args, " ") + ")";
+
unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY;
CapSet no_capabilities;
unsigned namespace_flags = 0;
@@ -1095,8 +1086,8 @@
}
}
-bool ServiceParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) {
if (args.size() < 3) {
*err = "services must have a name and a program";
return false;
@@ -1108,20 +1099,24 @@
return false;
}
+ Service* old_service = service_manager_->FindServiceByName(name);
+ if (old_service) {
+ *err = "ignored duplicate definition of service '" + name + "'";
+ return false;
+ }
+
std::vector<std::string> str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name, str_args);
return true;
}
-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;
+bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+ return service_ ? service_->ParseLine(std::move(args), err) : false;
}
void ServiceParser::EndSection() {
if (service_) {
- ServiceManager::GetInstance().AddService(std::move(service_));
+ service_manager_->AddService(std::move(service_));
}
}
diff --git a/init/service.h b/init/service.h
index f08a03f..634fe4e 100644
--- a/init/service.h
+++ b/init/service.h
@@ -19,13 +19,14 @@
#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"
@@ -144,8 +145,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_;
@@ -211,20 +212,18 @@
};
class ServiceParser : public SectionParser {
-public:
- ServiceParser() : service_(nullptr) {
- }
- bool ParseSection(const std::vector<std::string>& args,
+ public:
+ ServiceParser(ServiceManager* service_manager)
+ : service_manager_(service_manager), service_(nullptr) {}
+ bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override;
+ bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
void EndSection() override;
- void EndFile(const std::string&) override {
- }
-private:
+
+ private:
bool IsValidName(const std::string& name) const;
+ ServiceManager* service_manager_;
std::unique_ptr<Service> service_;
};
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 5e3acac..4d56d84 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -14,22 +14,17 @@
* limitations under the License.
*/
-#include <errno.h>
-#include <fcntl.h>
#include <signal.h>
-#include <stdio.h>
+#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
-#include <sys/wait.h>
#include <unistd.h>
+#include <android-base/logging.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;
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index f27be64..963cc4d 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -14,27 +14,27 @@
* 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/types.h>
-
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <selinux/selinux.h>
-#include "ueventd.h"
+#include "devices.h"
#include "log.h"
#include "util.h"
-#include "devices.h"
-#include "ueventd_parser.h"
+
+template <bool sysfs>
+static bool ParseSingleLine(std::vector<std::string>&& line, std::string* err);
int ueventd_main(int argc, char **argv)
{
@@ -60,9 +60,14 @@
cb.func_log = selinux_klog_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
- ueventd_parse_config_file("/ueventd.rc");
- ueventd_parse_config_file("/vendor/ueventd.rc");
- ueventd_parse_config_file("/odm/ueventd.rc");
+ Parser& parser = Parser::GetInstance();
+ parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>());
+ using namespace std::placeholders;
+ parser.AddSingleLineParser("/sys/", std::bind(ParsePermissionsLine, _1, _2, true));
+ parser.AddSingleLineParser("/dev/", std::bind(ParsePermissionsLine, _1, _2, false));
+ parser.ParseConfig("/ueventd.rc");
+ parser.ParseConfig("/vendor/ueventd.rc");
+ parser.ParseConfig("/odm/ueventd.rc");
/*
* keep the current product name base configuration so
@@ -72,7 +77,7 @@
* 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();
@@ -93,74 +98,3 @@
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;
- }
-}
diff --git a/init/ueventd.h b/init/ueventd.h
index d12d7fe..1f424d3 100644
--- a/init/ueventd.h
+++ b/init/ueventd.h
@@ -17,23 +17,6 @@
#ifndef _INIT_UEVENTD_H_
#define _INIT_UEVENTD_H_
-#include <cutils/list.h>
-#include <sys/types.h>
-
-enum devname_src_t {
- DEVNAME_UNKNOWN = 0,
- DEVNAME_UEVENT_DEVNAME,
- DEVNAME_UEVENT_DEVPATH,
-};
-
-struct ueventd_subsystem {
- struct listnode slist;
-
- const char *name;
- const char *dirname;
- devname_src_t devname_src;
-};
-
-int ueventd_main(int argc, char **argv);
+int ueventd_main(int argc, char** argv);
#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
deleted file mode 100644
index baff58c..0000000
--- a/init/ueventd_parser.cpp
+++ /dev/null
@@ -1,241 +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 <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);
-
-static void parse_line_device(struct parse_state *state, int nargs, char **args);
-
-#define SECTION 0x01
-#define OPTION 0x02
-
-#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;
- }
-
- 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;
- }
-
- 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;
-}
-
-static void parse_line_subsystem(struct parse_state *state, int nargs,
- char **args)
-{
- struct ueventd_subsystem *s = (ueventd_subsystem*) state->context;
- int kw;
-
- if (nargs == 0) {
- return;
- }
-
- 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]);
- }
-}
-
-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;
- }
- state->parse_line = parse_line_no_op;
-}
-
-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);
- } else {
- parse_line_device(state, nargs, args);
- }
-}
-
-static void parse_config(const char *fn, const std::string& data)
-{
- char *args[UEVENTD_PARSER_MAXARGS];
-
- 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;
- }
- }
-}
-
-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;
-}
-
-static void parse_line_device(parse_state*, int nargs, char** args) {
- set_device_permission(nargs, args);
-}
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
deleted file mode 100644
index 907cc49..0000000
--- a/init/ueventd_parser.h
+++ /dev/null
@@ -1,28 +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_UEVENTD_PARSER_H_
-#define _INIT_UEVENTD_PARSER_H_
-
-#include "ueventd.h"
-
-#define UEVENTD_PARSER_MAXARGS 5
-
-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);
-
-#endif
diff --git a/init/util.cpp b/init/util.cpp
index 895e78a..a101ce5 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,15 +37,15 @@
#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 <selinux/label.h>
#include "init.h"
-#include "log.h"
#include "reboot.h"
-#include "util.h"
+
+using android::base::boot_clock;
static unsigned int do_decode_uid(const char *s)
{
@@ -199,68 +194,21 @@
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;
+int 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.c_str(), mode);
+ if (ret && errno != EEXIST) return ret;
}
}
- ret = make_dir(pathname, mode);
- if (ret && errno != EEXIST)
- return ret;
+ auto ret = make_dir(path.c_str(), 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++ = '_';
- }
-}
-
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) {
@@ -421,3 +369,26 @@
os << t.duration_s() << " seconds";
return os;
}
+
+// Reads the content of device tree file under kAndroidDtDir 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;
+ if (android::base::ReadFileToString(file_name, dt_content)) {
+ if (!dt_content->empty()) {
+ dt_content->pop_back(); // Trims the trailing '\0' out.
+ return true;
+ }
+ }
+ return false;
+}
+
+bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content) {
+ std::string dt_content;
+ if (read_android_dt_file(sub_path, &dt_content)) {
+ if (dt_content == expected_content) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/init/util.h b/init/util.h
index 90ec6bb..92b3a1d 100644
--- a/init/util.h
+++ b/init/util.h
@@ -25,8 +25,13 @@
#include <ostream>
#include <string>
+#include <android-base/chrono_utils.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,
@@ -35,40 +40,29 @@
bool read_file(const std::string& path, std::string* content);
bool write_file(const std::string& path, const std::string& content);
-// 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;
-
- static time_point now();
-};
-
class Timer {
- public:
- Timer() : start_(boot_clock::now()) {
- }
+ 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();
- }
+ 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();
- }
+ int64_t duration_ms() const {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_)
+ .count();
+ }
- private:
- boot_clock::time_point start_;
+ private:
+ android::base::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);
+int 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)>&);
@@ -80,4 +74,8 @@
void panic() __attribute__((__noreturn__));
+// Reads or compares the content of device tree file under kAndroidDtDir 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);
+
#endif
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 0c0350a..b8b409a 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -120,3 +120,39 @@
EXPECT_EQ(UINT_MAX, decode_uid("toot"));
EXPECT_EQ(123U, decode_uid("123"));
}
+
+TEST(util, is_dir) {
+ TemporaryDir test_dir;
+ EXPECT_TRUE(is_dir(test_dir.path));
+ TemporaryFile tf;
+ EXPECT_FALSE(is_dir(tf.path));
+}
+
+// sehandle is needed for make_dir()
+// TODO: Remove once sehandle is encapsulated
+#include <selinux/label.h>
+selabel_handle* sehandle;
+
+TEST(util, mkdir_recursive) {
+ TemporaryDir test_dir;
+ std::string path = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
+ EXPECT_EQ(0, 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_EQ(0, 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()));
+}
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index b196147..21c1e5b 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -16,11 +16,12 @@
#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"
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index 0d57814..b0ac5c4 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -25,6 +25,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/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/libcutils/Android.bp b/libcutils/Android.bp
index 513736d..58170ec 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",
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.c
index e0e6a34..96ca566 100644
--- a/libcutils/canned_fs_config.c
+++ b/libcutils/canned_fs_config.c
@@ -17,6 +17,7 @@
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -41,7 +42,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 +51,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/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/sched_policy.cpp b/libcutils/sched_policy.cpp
index 3837ca7..217733a 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -263,24 +263,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;
}
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/tests/Android.bp b/libcutils/tests/Android.bp
index 718d76b..a0b1d7b 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -62,6 +62,7 @@
cc_test {
name: "libcutils_test",
+ test_suites: ["device-tests"],
defaults: ["libcutils_test_default"],
host_supported: true,
shared_libs: test_libraries,
@@ -69,6 +70,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/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..5fc7e35 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;
@@ -312,9 +319,9 @@
while (*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/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index 55953fc..057be5d 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;
}
@@ -209,14 +211,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_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/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..b62f8b4 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -326,6 +326,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 +638,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 +653,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 +692,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 +924,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/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/libnativebridge/.clang-format b/libnativebridge/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libnativebridge/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 7976f67..050373a 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -575,15 +575,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,
@@ -610,15 +610,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;
}
void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) {
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/libnativeloader/.clang-format b/libnativeloader/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libnativeloader/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index ebd6d4b..f3391d1 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -184,8 +184,8 @@
}
if (!NativeBridgeLinkNamespaces(ns, nullptr, public_libraries_.c_str())) {
- *error_msg = NativeBridgeGetError();
- return false;
+ *error_msg = NativeBridgeGetError();
+ return false;
}
native_loader_ns = NativeLoaderNamespace(ns);
@@ -329,10 +329,10 @@
// and now initialize native bridge namespaces if necessary.
if (NativeBridgeInitialized()) {
- initialized_ = NativeBridgeInitAnonymousNamespace(
- public_libraries_.c_str(), is_native_bridge ? library_path : nullptr);
- if (!initialized_) {
- *error_msg = NativeBridgeGetError();
+ initialized_ = NativeBridgeInitAnonymousNamespace(public_libraries_.c_str(),
+ is_native_bridge ? library_path : nullptr);
+ if (!initialized_) {
+ *error_msg = NativeBridgeGetError();
}
}
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
new file mode 100644
index 0000000..f710ba2
--- /dev/null
+++ b/libnetutils/Android.bp
@@ -0,0 +1,19 @@
+cc_library_shared {
+ name: "libnetutils",
+
+ srcs: [
+ "dhcpclient.c",
+ "dhcpmsg.c",
+ "ifc_utils.c",
+ "packet.c",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ ],
+
+ cflags: ["-Werror"],
+
+ export_include_dirs: ["include"],
+}
diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk
deleted file mode 100644
index 2150279..0000000
--- a/libnetutils/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- dhcpclient.c \
- dhcpmsg.c \
- ifc_utils.c \
- packet.c
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- liblog
-
-LOCAL_MODULE := libnetutils
-
-LOCAL_CFLAGS := -Werror
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
new file mode 100644
index 0000000..37e4dbf
--- /dev/null
+++ b/libprocessgroup/Android.bp
@@ -0,0 +1,10 @@
+cc_library {
+ srcs: ["processgroup.cpp"],
+ name: "libprocessgroup",
+ 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/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/libsysutils/Android.bp b/libsysutils/Android.bp
new file mode 100644
index 0000000..296bd26
--- /dev/null
+++ b/libsysutils/Android.bp
@@ -0,0 +1,25 @@
+cc_library_shared {
+ name: "libsysutils",
+ srcs: [
+ "src/SocketListener.cpp",
+ "src/FrameworkListener.cpp",
+ "src/NetlinkListener.cpp",
+ "src/NetlinkEvent.cpp",
+ "src/FrameworkCommand.cpp",
+ "src/SocketClient.cpp",
+ "src/ServiceManager.cpp",
+ ],
+
+ logtags: ["EventLogTags.logtags"],
+
+ cflags: ["-Werror"],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libnl",
+ ],
+
+ export_include_dirs: ["include"],
+}
diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk
deleted file mode 100644
index 584e5a2..0000000
--- a/libsysutils/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- src/SocketListener.cpp \
- src/FrameworkListener.cpp \
- src/NetlinkListener.cpp \
- src/NetlinkEvent.cpp \
- src/FrameworkCommand.cpp \
- src/SocketClient.cpp \
- src/ServiceManager.cpp \
- EventLogTags.logtags
-
-LOCAL_MODULE:= libsysutils
-
-LOCAL_CFLAGS := -Werror
-
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- libcutils \
- liblog \
- libnl
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := system/core/libsysutils/include
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ece623b..32ed6c3 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -47,12 +47,18 @@
srcs: [
"ArmExidx.cpp",
+ "DwarfCfa.cpp",
+ "DwarfMemory.cpp",
+ "DwarfOp.cpp",
"Elf.cpp",
"ElfInterface.cpp",
"ElfInterfaceArm.cpp",
"Log.cpp",
"Regs.cpp",
+ "MapInfo.cpp",
+ "Maps.cpp",
"Memory.cpp",
+ "Symbols.cpp",
],
shared_libs: [
@@ -87,16 +93,24 @@
srcs: [
"tests/ArmExidxDecodeTest.cpp",
"tests/ArmExidxExtractTest.cpp",
+ "tests/DwarfCfaLogTest.cpp",
+ "tests/DwarfCfaTest.cpp",
+ "tests/DwarfMemoryTest.cpp",
+ "tests/DwarfOpLogTest.cpp",
+ "tests/DwarfOpTest.cpp",
"tests/ElfInterfaceArmTest.cpp",
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
"tests/LogFake.cpp",
+ "tests/MapInfoTest.cpp",
+ "tests/MapsTest.cpp",
"tests/MemoryFake.cpp",
"tests/MemoryFileTest.cpp",
"tests/MemoryLocalTest.cpp",
"tests/MemoryRangeTest.cpp",
"tests/MemoryRemoteTest.cpp",
"tests/RegsTest.cpp",
+ "tests/SymbolsTest.cpp",
],
cflags: [
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
new file mode 100644
index 0000000..006f039
--- /dev/null
+++ b/libunwindstack/DwarfCfa.cpp
@@ -0,0 +1,713 @@
+/*
+ * 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 "DwarfCfa.h"
+#include "DwarfEncoding.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+
+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>;
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
new file mode 100644
index 0000000..ce7da4a
--- /dev/null
+++ b/libunwindstack/DwarfCfa.h
@@ -0,0 +1,255 @@
+/*
+ * 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 "DwarfError.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+
+// 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,
+ };
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_CFA_H
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h
new file mode 100644
index 0000000..0ff3b8c
--- /dev/null
+++ b/libunwindstack/DwarfEncoding.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 _LIBUNWINDSTACK_DWARF_ENCODING_H
+#define _LIBUNWINDSTACK_DWARF_ENCODING_H
+
+#include <stdint.h>
+
+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,
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/bootstat/uptime_parser.h b/libunwindstack/DwarfError.h
similarity index 61%
copy from bootstat/uptime_parser.h
copy to libunwindstack/DwarfError.h
index 756ae9b..824c307 100644
--- a/bootstat/uptime_parser.h
+++ b/libunwindstack/DwarfError.h
@@ -14,16 +14,19 @@
* limitations under the License.
*/
-#ifndef UPTIME_PARSER_H_
-#define UPTIME_PARSER_H_
+#ifndef _LIBUNWINDSTACK_DWARF_ERROR_H
+#define _LIBUNWINDSTACK_DWARF_ERROR_H
-#include <time.h>
+#include <stdint.h>
-namespace bootstat {
+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,
+};
-// 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
+#endif // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/DwarfLocation.h b/libunwindstack/DwarfLocation.h
new file mode 100644
index 0000000..062d125
--- /dev/null
+++ b/libunwindstack/DwarfLocation.h
@@ -0,0 +1,41 @@
+/*
+ * 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>
+
+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;
+
+#endif // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
new file mode 100644
index 0000000..11806ea
--- /dev/null
+++ b/libunwindstack/DwarfMemory.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 <assert.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "DwarfEncoding.h"
+#include "DwarfMemory.h"
+#include "Memory.h"
+
+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) {
+ assert((encoding & 0x0f) == 0);
+ assert(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 & 0xf0, 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*);
diff --git a/libunwindstack/DwarfMemory.h b/libunwindstack/DwarfMemory.h
new file mode 100644
index 0000000..a304dd9
--- /dev/null
+++ b/libunwindstack/DwarfMemory.h
@@ -0,0 +1,72 @@
+/*
+ * 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>
+
+// 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);
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
new file mode 100644
index 0000000..507ca08
--- /dev/null
+++ b/libunwindstack/DwarfOp.cpp
@@ -0,0 +1,462 @@
+/*
+ * 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 "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Memory.h"
+#include "Regs.h"
+
+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>;
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
new file mode 100644
index 0000000..ed6537a
--- /dev/null
+++ b/libunwindstack/DwarfOp.h
@@ -0,0 +1,1636 @@
+/*
+ * 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"
+
+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 RegsTmpl;
+
+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(RegsTmpl<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_;
+
+ RegsTmpl<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
+ };
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/DwarfStructs.h b/libunwindstack/DwarfStructs.h
new file mode 100644
index 0000000..57aac88
--- /dev/null
+++ b/libunwindstack/DwarfStructs.h
@@ -0,0 +1,52 @@
+/*
+ * 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>
+
+#include "DwarfEncoding.h"
+
+struct DwarfCIE {
+ uint8_t version = 0;
+ uint8_t fde_address_encoding = DW_EH_PE_absptr;
+ uint8_t lsda_encoding = DW_EH_PE_omit;
+ 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);
+
+#endif // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index d59e9d8..087457c 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -42,7 +42,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 +54,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 +79,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,8 +116,8 @@
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;
}
@@ -125,27 +125,27 @@
// Skip the first header, it's always going to be NULL.
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) {
// 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)) {
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))) {
+ if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+ memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
debug_frame_offset_ = shdr.sh_offset;
debug_frame_size_ = shdr.sh_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))) {
+ if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+ memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
gnu_debugdata_offset_ = shdr.sh_offset;
gnu_debugdata_size_ = shdr.sh_size;
}
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index e157320..bab84cc 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -85,10 +85,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 +98,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;
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
new file mode 100644
index 0000000..051f700
--- /dev/null
+++ b/libunwindstack/MapInfo.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include "Elf.h"
+#include "MapInfo.h"
+#include "Maps.h"
+#include "Memory.h"
+
+Memory* MapInfo::CreateMemory(pid_t pid) {
+ if (end <= start) {
+ return nullptr;
+ }
+
+ elf_offset = 0;
+
+ // First try and use the file associated with the info.
+ if (!name.empty()) {
+ // Fail on device maps.
+ if (flags & MAPS_FLAGS_DEVICE_MAP) {
+ return nullptr;
+ }
+
+ std::unique_ptr<MemoryFileAtOffset> file_memory(new MemoryFileAtOffset);
+ uint64_t map_size;
+ if (offset != 0) {
+ // Only map in a piece of the file.
+ map_size = end - start;
+ } else {
+ map_size = UINT64_MAX;
+ }
+ if (file_memory->Init(name, offset, map_size)) {
+ // It's possible that a non-zero offset might not be pointing to
+ // valid elf data. Check if this is a valid elf, and if not assume
+ // that this was meant to incorporate the entire file.
+ if (offset != 0 && !Elf::IsValidElf(file_memory.get())) {
+ // Don't bother checking the validity that will happen on the elf init.
+ if (file_memory->Init(name, 0)) {
+ elf_offset = offset;
+ return file_memory.release();
+ }
+ // Fall through if the init fails.
+ } else {
+ return file_memory.release();
+ }
+ }
+ }
+
+ Memory* memory = nullptr;
+ if (pid == getpid()) {
+ memory = new MemoryLocal();
+ } else {
+ memory = new MemoryRemote(pid);
+ }
+ return new MemoryRange(memory, start, end);
+}
+
+Elf* MapInfo::GetElf(pid_t pid, bool) {
+ if (elf) {
+ return elf;
+ }
+
+ elf = new Elf(CreateMemory(pid));
+ elf->Init();
+ // If the init fails, keep the elf around as an invalid object so we
+ // don't try to reinit the object.
+ return elf;
+}
diff --git a/libunwindstack/MapInfo.h b/libunwindstack/MapInfo.h
index 8342904..79a2ada 100644
--- a/libunwindstack/MapInfo.h
+++ b/libunwindstack/MapInfo.h
@@ -39,6 +39,7 @@
uint64_t elf_offset;
Memory* CreateMemory(pid_t pid);
+ Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
};
#endif // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
new file mode 100644
index 0000000..b369c43
--- /dev/null
+++ b/libunwindstack/Maps.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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 <memory>
+#include <string>
+#include <vector>
+
+#include "Maps.h"
+
+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;
+}
+
+bool Maps::ParseLine(const char* line, MapInfo* map_info) {
+ char permissions[5];
+ int name_pos;
+ // Linux /proc/<pid>/maps lines:
+ // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
+ if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %" SCNx64 " %*x:%*x %*d %n", &map_info->start,
+ &map_info->end, permissions, &map_info->offset, &name_pos) != 4) {
+ return false;
+ }
+ map_info->flags = PROT_NONE;
+ if (permissions[0] == 'r') {
+ map_info->flags |= PROT_READ;
+ }
+ if (permissions[1] == 'w') {
+ map_info->flags |= PROT_WRITE;
+ }
+ if (permissions[2] == 'x') {
+ map_info->flags |= PROT_EXEC;
+ }
+
+ map_info->name = &line[name_pos];
+ size_t length = map_info->name.length() - 1;
+ if (map_info->name[length] == '\n') {
+ map_info->name.erase(length);
+ }
+ // Mark a device map in /dev/and not in /dev/ashmem/ specially.
+ if (!map_info->name.empty() && 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() {
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(GetMapsFile().c_str(), "re"), fclose);
+ if (!fp) {
+ return false;
+ }
+
+ bool valid = true;
+ char* line = nullptr;
+ size_t line_len;
+ while (getline(&line, &line_len, fp.get()) > 0) {
+ MapInfo map_info;
+ if (!ParseLine(line, &map_info)) {
+ valid = false;
+ break;
+ }
+
+ maps_.push_back(map_info);
+ }
+ free(line);
+
+ return valid;
+}
+
+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 {
+ end_of_line++;
+ line = std::string(start_of_line, end_of_line - start_of_line);
+ }
+
+ MapInfo map_info;
+ if (!ParseLine(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;
+}
diff --git a/libunwindstack/Maps.h b/libunwindstack/Maps.h
new file mode 100644
index 0000000..239b64a
--- /dev/null
+++ b/libunwindstack/Maps.h
@@ -0,0 +1,107 @@
+/*
+ * 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 "Elf.h"
+#include "MapInfo.h"
+
+// 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);
+
+ bool ParseLine(const char* line, MapInfo* map_info);
+
+ 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(); }
+
+ 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;
+};
+
+#endif // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 1fcf842..9e46509 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -96,10 +96,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 +119,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 +137,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 +145,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 +167,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 +178,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 +188,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 +227,29 @@
}
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;
}
+
+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);
+}
diff --git a/libunwindstack/Memory.h b/libunwindstack/Memory.h
index c5316a1..f9f6d56 100644
--- a/libunwindstack/Memory.h
+++ b/libunwindstack/Memory.h
@@ -17,6 +17,7 @@
#ifndef _LIBUNWINDSTACK_MEMORY_H
#define _LIBUNWINDSTACK_MEMORY_H
+#include <assert.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
@@ -33,9 +34,16 @@
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) {
@@ -103,6 +111,9 @@
pid_t pid() { return pid_; }
+ protected:
+ virtual bool PtraceRead(uint64_t addr, long* value);
+
private:
pid_t pid_;
};
@@ -118,15 +129,12 @@
class MemoryRange : public Memory {
public:
MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
- : memory_(memory), begin_(begin), length_(end - begin_) {}
+ : memory_(memory), begin_(begin), length_(end - begin) {
+ assert(end > begin);
+ }
virtual ~MemoryRange() { delete memory_; }
- 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_;
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
new file mode 100644
index 0000000..86c1233
--- /dev/null
+++ b/libunwindstack/Symbols.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 <assert.h>
+#include <elf.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "Memory.h"
+#include "Symbols.h"
+
+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) {
+ assert(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*);
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
new file mode 100644
index 0000000..3c0d033
--- /dev/null
+++ b/libunwindstack/Symbols.h
@@ -0,0 +1,64 @@
+/*
+ * 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>
+
+// 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_;
+};
+
+#endif // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
new file mode 100644
index 0000000..3185bc3
--- /dev/null
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -0,0 +1,814 @@
+/*
+ * 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 "DwarfCfa.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+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);
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
new file mode 100644
index 0000000..6cf028a
--- /dev/null
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -0,0 +1,959 @@
+/*
+ * 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 "DwarfCfa.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+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);
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
new file mode 100644
index 0000000..4877f36
--- /dev/null
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -0,0 +1,472 @@
+/*
+ * 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 "DwarfMemory.h"
+
+#include "MemoryFake.h"
+
+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();
+
+ 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>();
+}
+
+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);
+}
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
new file mode 100644
index 0000000..d18aad0
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -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.
+ */
+
+#include <stdint.h>
+
+#include <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Regs.h"
+
+#include "MemoryFake.h"
+
+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);
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
new file mode 100644
index 0000000..520c545
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -0,0 +1,1593 @@
+/*
+ * 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 "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Regs.h"
+
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class RegsFake : public RegsTmpl<TypeParam> {
+ public:
+ RegsFake(uint16_t total_regs, uint16_t sp_reg)
+ : RegsTmpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+ virtual ~RegsFake() = default;
+
+ uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
+ uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+ bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
+};
+
+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);
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
new file mode 100644
index 0000000..c846ad7
--- /dev/null
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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 "Elf.h"
+#include "MapInfo.h"
+#include "Memory.h"
+
+class MapInfoTest : public ::testing::Test {
+ protected:
+ static void SetUpTestCase() {
+ std::vector<uint8_t> buffer(1024);
+ memcpy(buffer.data(), ELFMAG, SELFMAG);
+ for (size_t i = SELFMAG; i < buffer.size(); i++) {
+ buffer[i] = i / 256 + 1;
+ }
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ for (size_t i = 0; i < 0x100; i++) {
+ buffer[i] = i / 256 + 1;
+ }
+ memcpy(&buffer[0x100], ELFMAG, SELFMAG);
+ for (size_t i = 0x100 + SELFMAG; i < buffer.size(); i++) {
+ buffer[i] = i / 256 + 1;
+ }
+ ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
+ }
+
+ static TemporaryFile elf_;
+
+ static TemporaryFile elf_at_100_;
+};
+TemporaryFile MapInfoTest::elf_;
+TemporaryFile MapInfoTest::elf_at_100_;
+
+TEST_F(MapInfoTest, end_le_start) {
+ MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
+
+ std::unique_ptr<Memory> memory;
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() == nullptr);
+
+ info.end = 0xff;
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() == nullptr);
+
+ // Make sure this test is valid.
+ info.end = 0x101;
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_FALSE(info.CreateMemory(getpid()) == 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(MapInfoTest, create_memory_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(getpid()));
+ 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);
+ for (size_t i = SELFMAG; i < buffer.size(); i++) {
+ ASSERT_EQ(i / 256 + 1, 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(MapInfoTest, create_memory_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(getpid()));
+ 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);
+ for (size_t i = SELFMAG; i < buffer.size(); i++) {
+ ASSERT_EQ(2, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1));
+}
+
+// Verify that device file names will never result in Memory object creation.
+TEST_F(MapInfoTest, create_memory_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;
+ std::unique_ptr<Memory> memory;
+
+ info.flags = 0x8000;
+ info.name = "/dev/something";
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() == nullptr);
+}
+
+TEST_F(MapInfoTest, create_memory_local_memory) {
+ // Set up some memory for a valid local memory object.
+ std::vector<uint8_t> buffer(1024);
+ for (size_t i = 0; i < buffer.size(); i++) {
+ buffer[i] = i % 256;
+ }
+
+ MapInfo info;
+ info.start = reinterpret_cast<uint64_t>(buffer.data());
+ info.end = info.start + buffer.size();
+ info.offset = 0;
+
+ std::unique_ptr<Memory> memory;
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() != nullptr);
+
+ std::vector<uint8_t> read_buffer(1024);
+ ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
+ for (size_t i = 0; i < read_buffer.size(); i++) {
+ ASSERT_EQ(i % 256, read_buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1));
+}
+
+TEST_F(MapInfoTest, create_memory_remote_memory) {
+ std::vector<uint8_t> buffer(1024);
+ memset(buffer.data(), 0xa, buffer.size());
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true)
+ ;
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) != -1);
+ uint64_t iterations = 0;
+ siginfo_t si;
+ while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
+ usleep(30);
+ iterations++;
+ ASSERT_LT(iterations, 500000000ULL);
+ }
+
+ MapInfo info;
+ info.start = reinterpret_cast<uint64_t>(buffer.data());
+ info.end = info.start + buffer.size();
+ info.offset = 0;
+
+ std::unique_ptr<Memory> memory;
+ memory.reset(info.CreateMemory(pid));
+ ASSERT_TRUE(memory.get() != nullptr);
+ // Set the local memory to a different value to guarantee we are reading
+ // from the remote process.
+ memset(buffer.data(), 0x1, buffer.size());
+ std::vector<uint8_t> read_buffer(1024);
+ ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
+ for (size_t i = 0; i < read_buffer.size(); i++) {
+ ASSERT_EQ(0xaU, read_buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+}
+
+TEST_F(MapInfoTest, get_elf) {
+ // Create a map to use as initialization data.
+ void* map = mmap(nullptr, 1024, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(MAP_FAILED, map);
+
+ uint64_t start = reinterpret_cast<uint64_t>(map);
+ MapInfo info{.start = start, .end = start + 1024, .offset = 0, .name = ""};
+
+ // The map contains garbage, but this should still produce an elf object.
+ Elf* elf = info.GetElf(getpid(), false);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_FALSE(elf->valid());
+
+ ASSERT_EQ(0, munmap(map, 1024));
+}
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
new file mode 100644
index 0000000..7eb9bae
--- /dev/null
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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 <sys/mman.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Maps.h"
+
+TEST(MapsTest, parse_permissions) {
+ BufferMaps maps(
+ "1000-2000 ---- 00000000 00:00 0\n"
+ "2000-3000 r--- 00000000 00:00 0\n"
+ "3000-4000 -w-- 00000000 00:00 0\n"
+ "4000-5000 --x- 00000000 00:00 0\n"
+ "5000-6000 rwx- 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(
+ "720b29b000-720b29e000 rw-p 00000000 00:00 0\n"
+ "720b29e000-720b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+ "720b29f000-720b2a0000 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(0x720b29b000U, it->start);
+ ASSERT_EQ(0x720b29e000U, 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(0x720b29e000U, it->start);
+ ASSERT_EQ(0x720b29f000U, it->end);
+ ASSERT_EQ(0U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
+ ++it;
+ ASSERT_EQ("", it->name);
+ ASSERT_EQ(0x720b29f000U, it->start);
+ ASSERT_EQ(0x720b2a0000U, 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("720b29b000-720b29e000 r-xp a0000000 00:00 0 /fake.so\n"
+ "720b2b0000-720b2e0000 r-xp b0000000 00:00 0 /fake2.so\n"
+ "720b2e0000-720b2f0000 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(0x720b29b000U, it->start);
+ ASSERT_EQ(0x720b29e000U, it->end);
+ ASSERT_EQ(0xa0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("/fake.so", it->name);
+ ++it;
+ ASSERT_EQ(0x720b2b0000U, it->start);
+ ASSERT_EQ(0x720b2e0000U, it->end);
+ ASSERT_EQ(0xb0000000U, it->offset);
+ ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+ ASSERT_EQ("/fake2.so", it->name);
+ ++it;
+ ASSERT_EQ(0x720b2e0000U, it->start);
+ ASSERT_EQ(0x720b2f0000U, 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, 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);
+}
diff --git a/libunwindstack/tests/MemoryBuffer.cpp b/libunwindstack/tests/MemoryBuffer.cpp
new file mode 100644
index 0000000..af3d6b9
--- /dev/null
+++ b/libunwindstack/tests/MemoryBuffer.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 "Memory.h"
+
+#include "LogFake.h"
+
+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));
+}
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index e05736b..70ef30a 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -75,4 +75,16 @@
}
};
+class MemoryFakeRemote : public MemoryRemote {
+ public:
+ MemoryFakeRemote() : MemoryRemote(0) {}
+ virtual ~MemoryFakeRemote() = default;
+
+ protected:
+ bool PtraceRead(uint64_t, long* value) override {
+ *value = 0;
+ return true;
+ }
+};
+
#endif // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index 870ca19..aa7a23a 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#include <string>
+#include <vector>
+
#include <android-base/test_utils.h>
#include <android-base/file.h>
#include <gtest/gtest.h>
@@ -39,7 +42,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 +52,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 +62,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 +78,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 +99,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 +120,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 +157,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) {
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index 0ba5f1c..ab999da 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -47,25 +47,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 +54,13 @@
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));
+}
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index d636ec4..ee5ba01 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -17,6 +17,7 @@
#include <stdint.h>
#include <string.h>
+#include <memory>
#include <vector>
#include <gtest/gtest.h>
@@ -65,35 +66,14 @@
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_F(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));
-}
-
-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));
+ std::unique_ptr<MemoryRange> overflow(new MemoryRange(new MemoryFakeAlwaysReadZero, 100, 200));
+ ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100));
}
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index 7664c3e..e48edf7 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -33,6 +33,8 @@
#include "Memory.h"
+#include "MemoryFake.h"
+
class MemoryRemoteTest : public ::testing::Test {
protected:
static uint64_t NanoTime() {
@@ -121,6 +123,9 @@
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));
@@ -128,6 +133,14 @@
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) {
pid_t pid;
if ((pid = fork()) == 0) {
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
new file mode 100644
index 0000000..51b5d7d
--- /dev/null
+++ b/libunwindstack/tests/MemoryTest.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 <stdint.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "MemoryFake.h"
+
+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);
+}
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
new file mode 100644
index 0000000..a0a21e6
--- /dev/null
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -0,0 +1,335 @@
+/*
+ * 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 "Memory.h"
+#include "MemoryFake.h"
+#include "Symbols.h"
+
+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);
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
new file mode 100644
index 0000000..a0d6b9b
--- /dev/null
+++ b/libusbhost/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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",
+ 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 696db3b..83952ab 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -17,8 +17,14 @@
vendor_available: true,
host_supported: true,
- header_libs: ["libsystem_headers",],
- export_header_lib_headers: ["libsystem_headers",],
+ header_libs: [
+ "libsystem_headers",
+ "libcutils_headers"
+ ],
+ export_header_lib_headers: [
+ "libsystem_headers",
+ "libcutils_headers"
+ ],
export_include_dirs: ["include"],
target: {
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/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 21868f2..9c07472 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>
@@ -31,6 +32,7 @@
#include <android-base/file.h>
#include <gtest/gtest.h>
+#include <log/event_tag_map.h>
#include <log/log.h>
#include <log/log_event_list.h>
@@ -47,6 +49,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
@@ -70,7 +82,7 @@
#undef LOG_TAG
#define LOG_TAG "inject"
RLOGE(logcat_executable ".buckets");
- sleep(1);
+ rest();
ASSERT_TRUE(NULL !=
(fp = logcat_popen(
@@ -1204,7 +1216,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;
@@ -1412,7 +1429,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 +1467,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 +1492,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 +1538,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 +1551,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_LE(0, ret);
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_LE(0, ret);
EXPECT_TRUE(End_to_End(sync.tagStr,
"[id=%s,event=42,source=-1,account=0]", id));
}
@@ -1563,7 +1581,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_LE(0, ret);
EXPECT_TRUE(End_to_End(sync.tagStr, "[id=%s,event=43,-1,0]", id));
}
@@ -1571,7 +1590,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_LE(0, ret);
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 +1603,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_LE(0, ret);
EXPECT_TRUE(
End_to_End(sync.tagStr, "[aggregation time=30ms,count=2]"));
}
@@ -1591,7 +1612,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_LE(0, ret);
EXPECT_TRUE(
End_to_End(sync.tagStr, "[aggregation time=31.57s,count=911]"));
}
@@ -1602,42 +1624,48 @@
{
android_log_event_list ctx(sync.tagNo);
ctx << (uint32_t)512;
- ctx.write();
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_LE(0, ret);
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_LE(0, ret);
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_LE(0, ret);
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_LE(0, ret);
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_LE(0, ret);
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_LE(0, ret);
EXPECT_TRUE(End_to_End(sync.tagStr, "current=-1GB"));
}
}
@@ -1645,9 +1673,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_LE(0, ret);
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_LE(0, ret);
+ 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_LE(0, ret);
+ 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_LE(0, ret);
+ 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_LE(0, ret);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "new=1d 2:03:58"));
+ }
+ }
}
static bool reportedSecurity(const char* command) {
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..ee38d1c 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -277,7 +277,7 @@
++cp;
}
tid = pid;
- logbuf->lock();
+ logbuf->wrlock();
uid = logbuf->pidToUid(pid);
logbuf->unlock();
memmove(pidptr, cp, strlen(cp) + 1);
@@ -322,7 +322,7 @@
pid = tid;
comm = "auditd";
} else {
- logbuf->lock();
+ logbuf->wrlock();
comm = commfree = logbuf->pidToName(pid);
logbuf->unlock();
if (!comm) {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 4aa2c9f..8b609cb 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -72,7 +72,7 @@
// 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;
@@ -87,7 +87,7 @@
}
++it;
}
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
}
// We may have been triggered by a SIGHUP. Release any sleeping reader
@@ -95,7 +95,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 +111,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;
@@ -209,16 +209,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 +318,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 +337,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 +360,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 +386,7 @@
bool end_set = false;
bool end_always = false;
- LogTimeEntry::lock();
+ LogTimeEntry::rdlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
@@ -431,7 +428,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);
@@ -655,14 +652,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();
@@ -1022,15 +1019,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 +1040,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 +1053,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 +1065,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 +1087,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
@@ -1148,7 +1145,7 @@
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,7 +1168,7 @@
(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);
@@ -1181,20 +1178,20 @@
}
skip = maxSkip;
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
}
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return max;
}
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..51edd86 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -76,7 +76,7 @@
class LogBuffer {
LogBufferElementCollection mLogElements;
- pthread_mutex_t mLogElementsLock;
+ pthread_rwlock_t mLogElementsLock;
LogStatistics stats;
@@ -154,7 +154,7 @@
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);
}
@@ -164,11 +164,14 @@
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:
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 04a620c..381c974 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -121,7 +121,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 +129,7 @@
commName = android::tidToName(mPid);
}
if (!commName) {
- parent->lock();
+ parent->wrlock();
commName = parent->pidToName(mPid);
parent->unlock();
}
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/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..d20d90e 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -15,6 +15,7 @@
*/
#include <fcntl.h>
+#include <inttypes.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
@@ -23,19 +24,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 +78,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 +152,10 @@
tagTable.add(tag, element);
}
}
+
+ if (!element->getDropped()) {
+ tagNameTable.add(TagNameKey(element), element);
+ }
}
void LogStatistics::subtract(LogBufferElement* element) {
@@ -124,6 +167,10 @@
--mDroppedElements[log_id];
}
+ if (mOldest[log_id] < element->getRealTime()) {
+ mOldest[log_id] = element->getRealTime();
+ }
+
if (log_id == LOG_ID_KERNEL) {
return;
}
@@ -148,6 +195,10 @@
tagTable.subtract(tag, element);
}
}
+
+ if (!element->getDropped()) {
+ tagNameTable.subtract(TagNameKey(element), element);
+ }
}
// Atomically set an entry to drop
@@ -158,6 +209,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 +233,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 +306,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 +444,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 +468,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 +515,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 +679,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 +806,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;
}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 066b7de..945fc0a 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;
}
@@ -307,7 +321,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 +331,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 +353,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 +389,7 @@
uid(android::pidToUid(tid)),
name(android::tidToName(tid)) {
}
- explicit TidEntry(LogBufferElement* element)
+ explicit TidEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
tid(element->getTid()),
pid(element->getPid()),
@@ -387,7 +401,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 +426,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 +456,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 +476,7 @@
return android::tagToName(tag);
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
if (uid != element->getUid()) {
uid = -1;
}
@@ -476,6 +490,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 +672,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 +701,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 +719,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 +737,7 @@
enable = true;
}
+ void addTotal(LogBufferElement* entry);
void add(LogBufferElement* entry);
void subtract(LogBufferElement* entry);
// entry->setDropped(1) must follow this call
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/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/main.cpp b/logd/main.cpp
index 946485b..18029eb 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -407,6 +407,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/logd_test.cpp b/logd/tests/logd_test.cpp
index c81aa32..a1d154a 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -426,7 +426,7 @@
" 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 +434,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,21 +469,23 @@
}
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);
@@ -1005,16 +1007,18 @@
}
// 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 0;
+ }
}
// The key here is we are root, but we are in u:r:shell:s0,
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/ueventd.rc b/rootdir/ueventd.rc
index f5c93b7..29fea01 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.
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
new file mode 100644
index 0000000..81cf315
--- /dev/null
+++ b/shell_and_utilities/Android.bp
@@ -0,0 +1,13 @@
+phony {
+ name: "shell_and_utilities",
+ required: [
+ "bzip2",
+ "grep",
+ "gzip",
+ "mkshrc",
+ "reboot",
+ "sh",
+ "toolbox",
+ "toybox",
+ ],
+}
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
new file mode 100644
index 0000000..5d10c18
--- /dev/null
+++ b/shell_and_utilities/README.md
@@ -0,0 +1,157 @@
+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 in the tree up to and including KitKat).
+
+Initially Android had a very limited command-line provided by its
+own "toolbox" binary. These days 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 before we started in earnest 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.
+
+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.
+
+
+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
+
+
+Current AOSP
+------------
+
+BSD: dd grep
+
+bzip2: bzcat bzip2 bunzip2
+
+toolbox: getevent gzip newfs\_msdos gunzip zcat
+
+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 head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall ln load\_policy log logname losetup
+ls lsmod lsof lsusb md5sum microcom mkdir 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
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index d6ead1a..aa755ed 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -95,3 +95,13 @@
LOCAL_MODULE := grep
LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,egrep fgrep,ln -sf grep $(TARGET_OUT)/bin/$(t);)
include $(BUILD_EXECUTABLE)
+
+
+# We build gzip separately, so it can provide gunzip and zcat too.
+include $(CLEAR_VARS)
+LOCAL_MODULE := gzip
+LOCAL_SRC_FILES := gzip.c
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_SHARED_LIBRARIES += libz
+LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,gunzip zcat,ln -sf gzip $(TARGET_OUT)/bin/$(t);)
+include $(BUILD_EXECUTABLE)
diff --git a/toolbox/gzip.c b/toolbox/gzip.c
new file mode 100644
index 0000000..62c4518
--- /dev/null
+++ b/toolbox/gzip.c
@@ -0,0 +1,261 @@
+/* gzip.c - gzip/gunzip/zcat tools for gzip data
+ *
+ * Copyright 2017 The Android Open Source Project
+ *
+ * GZIP RFC: http://www.ietf.org/rfc/rfc1952.txt
+
+TODO: port to toybox.
+
+*/
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <zlib.h>
+
+// toybox-style flags/globals.
+#define FLAG_c 1
+#define FLAG_d 2
+#define FLAG_f 4
+#define FLAG_k 8
+static struct {
+ int optflags;
+} toys;
+static struct {
+ int level;
+} TT;
+
+static void xstat(const char *path, struct stat *sb)
+{
+ if (stat(path, sb)) error(1, errno, "stat %s", path);
+}
+
+static void fix_time(const char *path, struct stat *sb)
+{
+ struct timespec times[] = { sb->st_atim, sb->st_mtim };
+
+ if (utimensat(AT_FDCWD, path, times, 0)) error(1, errno, "utimes");
+}
+
+static FILE *xfdopen(const char *name, int flags, mode_t open_mode,
+ const char *mode)
+{
+ FILE *fp;
+ int fd;
+
+ if (!strcmp(name, "-")) fd = dup((*mode == 'r') ? 0 : 1);
+ else fd = open(name, flags, open_mode);
+
+ if (fd == -1) error(1, errno, "open %s (%s)", name, mode);
+ fp = fdopen(fd, mode);
+ if (fp == NULL) error(1, errno, "fopen %s (%s)", name, mode);
+ return fp;
+}
+
+static gzFile xgzopen(const char *name, int flags, mode_t open_mode,
+ const char *mode)
+{
+ gzFile f;
+ int fd;
+
+ if (!strcmp(name, "-")) fd = dup((*mode == 'r') ? 0 : 1);
+ else fd = open(name, flags, open_mode);
+
+ if (fd == -1) error(1, errno, "open %s (%s)", name, mode);
+ f = gzdopen(fd, mode);
+ if (f == NULL) error(1, errno, "gzdopen %s (%s)", name, mode);
+ return f;
+}
+
+static void gzfatal(gzFile f, char *what)
+{
+ int err;
+ const char *msg = gzerror(f, &err);
+
+ error(1, (err == Z_ERRNO) ? errno : 0, "%s: %s", what, msg);
+}
+
+static void gunzip(char *arg)
+{
+ struct stat sb;
+ char buf[BUFSIZ];
+ int len, both_files;
+ char *in_name, *out_name;
+ gzFile in;
+ FILE *out;
+
+ // "gunzip x.gz" will decompress "x.gz" to "x".
+ len = strlen(arg);
+ if (len > 3 && !strcmp(arg+len-3, ".gz")) {
+ in_name = strdup(arg);
+ out_name = strdup(arg);
+ out_name[len-3] = '\0';
+ } else if (!strcmp(arg, "-")) {
+ // "-" means stdin; assume output to stdout.
+ // TODO: require -f to read compressed data from tty?
+ in_name = strdup("-");
+ out_name = strdup("-");
+ } else error(1, 0, "unknown suffix");
+
+ if (toys.optflags&FLAG_c) {
+ free(out_name);
+ out_name = strdup("-");
+ }
+
+ both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
+ if (both_files) xstat(in_name, &sb);
+
+ in = xgzopen(in_name, O_RDONLY, 0, "r");
+ out = xfdopen(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
+ both_files?sb.st_mode:0666, "w");
+
+ while ((len = gzread(in, buf, sizeof(buf))) > 0) {
+ if (fwrite(buf, 1, len, out) != (size_t) len) error(1, errno, "fwrite");
+ }
+ if (len < 0) gzfatal(in, "gzread");
+ if (fclose(out)) error(1, errno, "fclose");
+ if (gzclose(in) != Z_OK) error(1, 0, "gzclose");
+
+ if (both_files) fix_time(out_name, &sb);
+ if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
+ free(in_name);
+ free(out_name);
+}
+
+static void gzip(char *in_name)
+{
+ char buf[BUFSIZ];
+ size_t len;
+ char *out_name;
+ FILE *in;
+ gzFile out;
+ struct stat sb;
+ int both_files;
+
+ if (toys.optflags&FLAG_c) {
+ out_name = strdup("-");
+ } else {
+ if (asprintf(&out_name, "%s.gz", in_name) == -1) {
+ error(1, errno, "asprintf");
+ }
+ }
+
+ both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
+ if (both_files) xstat(in_name, &sb);
+
+ snprintf(buf, sizeof(buf), "w%d", TT.level);
+ in = xfdopen(in_name, O_RDONLY, 0, "r");
+ out = xgzopen(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
+ both_files?sb.st_mode:0, buf);
+
+ while ((len = fread(buf, 1, sizeof(buf), in)) > 0) {
+ if (gzwrite(out, buf, len) != (int) len) gzfatal(out, "gzwrite");
+ }
+ if (ferror(in)) error(1, errno, "fread");
+ if (fclose(in)) error(1, errno, "fclose");
+ if (gzclose(out) != Z_OK) error(1, 0, "gzclose");
+
+ if (both_files) fix_time(out_name, &sb);
+ if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
+ free(out_name);
+}
+
+static void do_file(char *arg)
+{
+ if (toys.optflags&FLAG_d) gunzip(arg);
+ else gzip(arg);
+}
+
+static void usage()
+{
+ char *cmd = basename(getprogname());
+
+ printf("usage: %s [-c] [-d] [-f] [-#] [FILE...]\n", cmd);
+ printf("\n");
+ if (!strcmp(cmd, "zcat")) {
+ printf("Decompress files to stdout. Like `gzip -dc`.\n");
+ printf("\n");
+ printf("-c\tOutput to stdout\n");
+ printf("-f\tForce: allow read from tty\n");
+ } else if (!strcmp(cmd, "gunzip")) {
+ printf("Decompress files. With no files, decompresses stdin to stdout.\n");
+ printf("On success, the input files are removed and replaced by new\n");
+ printf("files without the .gz suffix.\n");
+ printf("\n");
+ printf("-c\tOutput to stdout\n");
+ printf("-f\tForce: allow read from tty\n");
+ printf("-k\tKeep input files (don't remove)\n");
+ } else { // gzip
+ printf("Compress files. With no files, compresses stdin to stdout.\n");
+ printf("On success, the input files are removed and replaced by new\n");
+ printf("files with the .gz suffix.\n");
+ printf("\n");
+ printf("-c\tOutput to stdout\n");
+ printf("-d\tDecompress (act as gunzip)\n");
+ printf("-f\tForce: allow overwrite of output file\n");
+ printf("-k\tKeep input files (don't remove)\n");
+ printf("-#\tCompression level 1-9 (1:fastest, 6:default, 9:best)\n");
+ }
+ printf("\n");
+}
+
+int main(int argc, char *argv[])
+{
+ char *cmd = basename(argv[0]);
+ int opt_ch;
+
+ toys.optflags = 0;
+ TT.level = 6;
+
+ if (!strcmp(cmd, "gunzip")) {
+ // gunzip == gzip -d
+ toys.optflags = FLAG_d;
+ } else if (!strcmp(cmd, "zcat")) {
+ // zcat == gzip -dc
+ toys.optflags = (FLAG_c|FLAG_d);
+ }
+
+ while ((opt_ch = getopt(argc, argv, "cdfhk123456789")) != -1) {
+ switch (opt_ch) {
+ case 'c': toys.optflags |= FLAG_c; break;
+ case 'd': toys.optflags |= FLAG_d; break;
+ case 'f': toys.optflags |= FLAG_f; break;
+ case 'k': toys.optflags |= FLAG_k; break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ TT.level = opt_ch - '0';
+ break;
+
+ default:
+ usage();
+ return 1;
+ }
+ }
+
+ if (optind == argc) {
+ // With no arguments, we go from stdin to stdout.
+ toys.optflags |= FLAG_c;
+ do_file("-");
+ return 0;
+ }
+
+ // Otherwise process each file in turn.
+ while (optind < argc) do_file(argv[optind++]);
+ return 0;
+}
diff --git a/trusty/Android.bp b/trusty/Android.bp
new file mode 100644
index 0000000..1b2e2c7
--- /dev/null
+++ b/trusty/Android.bp
@@ -0,0 +1,3 @@
+subdirs = [
+ "libtrusty",
+]
diff --git a/trusty/keymaster/Android.mk b/trusty/keymaster/Android.mk
index 0ebf52d..2d614ae 100644
--- a/trusty/keymaster/Android.mk
+++ b/trusty/keymaster/Android.mk
@@ -32,7 +32,7 @@
LOCAL_MODULE := trusty_keymaster_tipc
LOCAL_SRC_FILES := \
trusty_keymaster_device.cpp \
- trusty_keymaster_ipc.c \
+ trusty_keymaster_ipc.cpp \
trusty_keymaster_main.cpp
LOCAL_SHARED_LIBRARIES := \
libcrypto \
@@ -40,6 +40,7 @@
libkeymaster1 \
libtrusty \
libkeymaster_messages \
+ libsoftkeymasterdevice \
liblog
include $(BUILD_EXECUTABLE)
@@ -53,7 +54,7 @@
LOCAL_MODULE := keystore.trusty
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_SRC_FILES := module.cpp \
- trusty_keymaster_ipc.c \
+ trusty_keymaster_ipc.cpp \
trusty_keymaster_device.cpp
LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
LOCAL_SHARED_LIBRARIES := \
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/keymaster_ipc.h
index 48fa53d..b38eb05 100644
--- a/trusty/keymaster/keymaster_ipc.h
+++ b/trusty/keymaster/keymaster_ipc.h
@@ -16,13 +16,15 @@
#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_REQ_SHIFT = 1,
KM_GENERATE_KEY = (0 << KEYMASTER_REQ_SHIFT),
KM_BEGIN_OPERATION = (1 << KEYMASTER_REQ_SHIFT),
@@ -40,6 +42,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 +55,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..5f16fd0 100644
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -25,49 +25,53 @@
#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;
+const uint32_t RECV_BUF_SIZE = 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 +79,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 +119,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 +139,510 @@
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;
+ 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;
+ 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;
+ request.random_data.Reinitialize(data, data_length);
+ AddEntropyResponse response;
+ 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;
+ request.SetKeyMaterial(*key_blob);
+ AddClientAndAppData(client_id, app_data, &request);
+
+ GetKeyCharacteristicsResponse response;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ request.purpose = purpose;
+ request.SetKeyMaterial(*key);
+ request.additional_params.Reinitialize(*in_params);
+
+ BeginOperationResponse response;
+ 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;
+ 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;
+ 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) {
+ return KM_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (out_params) {
+ *out_params = {};
+ }
+ if (output) {
+ *output = {};
+ }
+
+ FinishOperationRequest request;
+ 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;
+ 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;
+ request.op_handle = operation_handle;
+ AbortOperationResponse response;
+ 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 +652,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,7 +765,7 @@
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) {
ALOGE("tipc error: %d\n", rc);
@@ -458,8 +775,8 @@
ALOGV("Received %d byte response\n", rsp_size);
}
- const keymaster_message* msg = (keymaster_message *) recv_buf;
- const uint8_t *p = msg->payload;
+ const keymaster_message* msg = (keymaster_message*)recv_buf;
+ const uint8_t* p = msg->payload;
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 +787,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_ipc.c b/trusty/keymaster/trusty_keymaster_ipc.cpp
similarity index 75%
rename from trusty/keymaster/trusty_keymaster_ipc.c
rename to trusty/keymaster/trusty_keymaster_ipc.cpp
index 88546af..cdc2778 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.c
+++ b/trusty/keymaster/trusty_keymaster_ipc.cpp
@@ -26,8 +26,8 @@
#include <log/log.h>
#include <trusty/tipc.h>
-#include "trusty_keymaster_ipc.h"
#include "keymaster_ipc.h"
+#include "trusty_keymaster_ipc.h"
#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
@@ -43,15 +43,15 @@
return 0;
}
-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) {
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);
+ struct keymaster_message* msg = reinterpret_cast<struct keymaster_message*>(malloc(msg_size));
msg->cmd = cmd;
memcpy(msg->payload, in, in_size);
@@ -59,31 +59,30 @@
free(msg);
if (rc < 0) {
- ALOGE("failed to send cmd (%d) to %s: %s\n", cmd,
- KEYMASTER_PORT, strerror(errno));
+ 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));
+ 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);
+ if ((size_t)rc < sizeof(struct keymaster_message)) {
+ ALOGE("invalid response size (%d)\n", (int)rc);
return -EINVAL;
}
- msg = (struct keymaster_message *) out;
+ 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);
+ *out_size = ((size_t)rc) - sizeof(struct keymaster_message);
return rc;
}
@@ -92,4 +91,3 @@
tipc_close(handle_);
}
}
-
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)