libnetutil: Check dhcp respose packet length am: 61f25d4a36 am: cee6d38c55 am: 29d054046f am: c29049f4f8 am: 3de04e9840 am: 19702dc499 am: 8191e9ba87 am: 804323c195 am: 4f5f00d879 am: 3a1ff1ad71 am: bab7b1ecd9 am: cdf0fc6018 am: 9f1398c6e3 Change-Id: I4c46a36846dd8f6588f29bc84de9bc3f1d7c088a
diff --git a/CleanSpec.mk b/CleanSpec.mk index 5b5eff4..d5b7554 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk
@@ -60,3 +60,13 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/gatekeeper.$(TARGET_DEVICE).so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/vendor) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libtrusty.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libtrusty.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/hw/keystore.trusty.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/keystore.trusty.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/hw/gatekeeper.trusty.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/gatekeeper.trusty.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/secure-storage-unit-test) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/storageproxyd) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/tipc-test) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/trusty_keymaster_tipc)
diff --git a/adb/Android.mk b/adb/Android.mk index 6ed01fa..ece0645 100644 --- a/adb/Android.mk +++ b/adb/Android.mk
@@ -360,8 +360,6 @@ LOCAL_MODULE := adbd LOCAL_FORCE_STATIC_EXECUTABLE := true -LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) -LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) LOCAL_SANITIZE := $(adb_target_sanitize) LOCAL_STRIP_MODULE := keep_symbols
diff --git a/adb/adb.cpp b/adb/adb.cpp index bfb1144..6b30be8 100644 --- a/adb/adb.cpp +++ b/adb/adb.cpp
@@ -49,6 +49,7 @@ #include "adb_auth.h" #include "adb_io.h" #include "adb_listeners.h" +#include "adb_unique_fd.h" #include "adb_utils.h" #include "sysdeps/chrono.h" #include "transport.h" @@ -256,19 +257,6 @@ 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. @@ -369,7 +357,7 @@ if (p->msg.arg0){ send_packet(p, t); #if ADB_HOST - SendConnectOnHost(t); + send_connect(t); #endif } else { t->SetConnectionState(kCsOffline); @@ -656,6 +644,26 @@ #endif +static void ReportServerStartupFailure(pid_t pid) { + fprintf(stderr, "ADB server didn't ACK\n"); + fprintf(stderr, "Full server startup log: %s\n", GetLogFilePath().c_str()); + fprintf(stderr, "Server had pid: %d\n", pid); + + unique_fd fd(adb_open(GetLogFilePath().c_str(), O_RDONLY)); + if (fd == -1) return; + + // Let's not show more than 128KiB of log... + adb_lseek(fd, -128 * 1024, SEEK_END); + std::string content; + if (!android::base::ReadFdToString(fd, &content)) return; + + std::string header = android::base::StringPrintf("--- adb starting (pid %d) ---", pid); + std::vector<std::string> lines = android::base::Split(content, "\n"); + int i = lines.size() - 1; + while (i >= 0 && lines[i] != header) --i; + while (static_cast<size_t>(i) < lines.size()) fprintf(stderr, "%s\n", lines[i++].c_str()); +} + int launch_server(const std::string& socket_spec) { #if defined(_WIN32) /* we need to start the server in the background */ @@ -835,7 +843,8 @@ memcmp(temp, expected, expected_length) == 0) { got_ack = true; } else { - fprintf(stderr, "ADB server didn't ACK\n"); + ReportServerStartupFailure(GetProcessId(process_handle.get())); + return -1; } } else { const DWORD err = GetLastError(); @@ -909,12 +918,9 @@ "--reply-fd", reply_fd, NULL); // this should not return fprintf(stderr, "adb: execl returned %d: %s\n", result, strerror(errno)); - } else { + } else { // parent side of the fork - - char temp[3]; - - temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C'; + char temp[3] = {}; // wait for the "OK\n" message adb_close(fd[1]); int ret = adb_read(fd[0], temp, 3); @@ -925,7 +931,7 @@ return -1; } if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') { - fprintf(stderr, "ADB server didn't ACK\n" ); + ReportServerStartupFailure(pid); return -1; } } @@ -937,8 +943,8 @@ // Try to handle a network forwarding request. // This returns 1 on success, 0 on failure, and -1 to indicate this is not // a forwarding-related request. -int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd) -{ +int handle_forward_request(const char* service, TransportType type, const char* serial, + TransportId transport_id, int reply_fd) { if (!strcmp(service, "list-forward")) { // Create the list of forward redirections. std::string listeners = format_listeners(); @@ -991,7 +997,8 @@ } std::string error_msg; - atransport* transport = acquire_one_transport(type, serial, nullptr, &error_msg); + atransport* transport = + acquire_one_transport(type, serial, transport_id, nullptr, &error_msg); if (!transport) { SendFail(reply_fd, error_msg); return 1; @@ -1049,8 +1056,8 @@ return 0; } -int handle_host_request(const char* service, TransportType type, - const char* serial, int reply_fd, asocket* s) { +int handle_host_request(const char* service, TransportType type, const char* serial, + TransportId transport_id, int reply_fd, asocket* s) { if (strcmp(service, "kill") == 0) { fprintf(stderr, "adb server killed by remote request\n"); fflush(stdout); @@ -1070,7 +1077,14 @@ if (!strncmp(service, "transport", strlen("transport"))) { TransportType type = kTransportAny; - if (!strncmp(service, "transport-usb", strlen("transport-usb"))) { + if (!strncmp(service, "transport-id:", strlen("transport-id:"))) { + service += strlen("transport-id:"); + transport_id = strtoll(service, const_cast<char**>(&service), 10); + if (*service != '\0') { + SendFail(reply_fd, "invalid transport id"); + return 1; + } + } else if (!strncmp(service, "transport-usb", strlen("transport-usb"))) { type = kTransportUsb; } else if (!strncmp(service, "transport-local", strlen("transport-local"))) { type = kTransportLocal; @@ -1082,7 +1096,7 @@ } std::string error; - atransport* t = acquire_one_transport(type, serial, nullptr, &error); + atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error); if (t != nullptr) { s->transport = t; SendOkay(reply_fd); @@ -1125,7 +1139,7 @@ if (!strcmp(service, "features")) { std::string error; - atransport* t = acquire_one_transport(type, serial, nullptr, &error); + atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error); if (t != nullptr) { SendOkay(reply_fd, FeatureSetToString(t->features())); } else { @@ -1178,7 +1192,7 @@ // These always report "unknown" rather than the actual error, for scripts. if (!strcmp(service, "get-serialno")) { std::string error; - atransport* t = acquire_one_transport(type, serial, nullptr, &error); + atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error); if (t) { return SendOkay(reply_fd, t->serial ? t->serial : "unknown"); } else { @@ -1187,7 +1201,7 @@ } if (!strcmp(service, "get-devpath")) { std::string error; - atransport* t = acquire_one_transport(type, serial, nullptr, &error); + atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error); if (t) { return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown"); } else { @@ -1196,7 +1210,7 @@ } if (!strcmp(service, "get-state")) { std::string error; - atransport* t = acquire_one_transport(type, serial, nullptr, &error); + atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error); if (t) { return SendOkay(reply_fd, t->connection_state_name()); } else { @@ -1214,7 +1228,7 @@ if (!strcmp(service, "reconnect")) { std::string response; - atransport* t = acquire_one_transport(type, serial, nullptr, &response, true); + atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &response, true); if (t != nullptr) { kick_transport(t); response = @@ -1223,7 +1237,7 @@ return SendOkay(reply_fd, response); } - int ret = handle_forward_request(service, type, serial, reply_fd); + int ret = handle_forward_request(service, type, serial, transport_id, reply_fd); if (ret >= 0) return ret - 1; return -1;
diff --git a/adb/adb.h b/adb/adb.h index d6b2b81..98e2604 100644 --- a/adb/adb.h +++ b/adb/adb.h
@@ -56,6 +56,7 @@ // Increment this when we want to force users to start a new adb server. #define ADB_SERVER_VERSION 39 +using TransportId = uint64_t; class atransport; struct amessage { @@ -149,7 +150,7 @@ int service_to_fd(const char* name, const atransport* transport); #if ADB_HOST -asocket *host_service_to_socket(const char* name, const char *serial); +asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id); #endif #if !ADB_HOST @@ -159,7 +160,8 @@ int create_jdwp_connection_fd(int jdwp_pid); #endif -int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd); +int handle_forward_request(const char* service, TransportType type, const char* serial, + TransportId transport_id, int reply_fd); #if !ADB_HOST void framebuffer_service(int fd, void *cookie); @@ -216,15 +218,13 @@ #define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2) #endif -int handle_host_request(const char* service, TransportType type, const char* serial, int reply_fd, asocket *s); +int handle_host_request(const char* service, TransportType type, const char* serial, + TransportId transport_id, int reply_fd, asocket* s); void handle_online(atransport *t); void handle_offline(atransport *t); void send_connect(atransport *t); -#if ADB_HOST -void SendConnectOnHost(atransport* t); -#endif void parse_banner(const std::string&, atransport* t);
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp index e533a00..849a6e7 100644 --- a/adb/adb_client.cpp +++ b/adb/adb_client.cpp
@@ -20,6 +20,7 @@ #include "adb_client.h" #include <errno.h> +#include <inttypes.h> #include <limits.h> #include <stdarg.h> #include <stdio.h> @@ -46,12 +47,20 @@ static TransportType __adb_transport = kTransportAny; static const char* __adb_serial = NULL; +static TransportId __adb_transport_id = 0; static const char* __adb_server_socket_spec; -void adb_set_transport(TransportType type, const char* serial) { +void adb_set_transport(TransportType type, const char* serial, TransportId transport_id) { __adb_transport = type; __adb_serial = serial; + __adb_transport_id = transport_id; +} + +void adb_get_transport(TransportType* type, const char** serial, TransportId* transport_id) { + if (type) *type = __adb_transport; + if (serial) *serial = __adb_serial; + if (transport_id) *transport_id = __adb_transport_id; } void adb_set_socket_spec(const char* socket_spec) { @@ -63,7 +72,10 @@ static int switch_socket_transport(int fd, std::string* error) { std::string service; - if (__adb_serial) { + if (__adb_transport_id) { + service += "host:transport-id:"; + service += std::to_string(__adb_transport_id); + } else if (__adb_serial) { service += "host:transport:"; service += __adb_serial; } else { @@ -292,15 +304,18 @@ return true; } -std::string format_host_command(const char* command, TransportType type, const char* serial) { - if (serial) { - return android::base::StringPrintf("host-serial:%s:%s", serial, command); +std::string format_host_command(const char* command) { + if (__adb_transport_id) { + return android::base::StringPrintf("host-transport-id:%" PRIu64 ":%s", __adb_transport_id, + command); + } else if (__adb_serial) { + return android::base::StringPrintf("host-serial:%s:%s", __adb_serial, command); } const char* prefix = "host"; - if (type == kTransportUsb) { + if (__adb_transport == kTransportUsb) { prefix = "host-usb"; - } else if (type == kTransportLocal) { + } else if (__adb_transport == kTransportLocal) { prefix = "host-local"; } return android::base::StringPrintf("%s:%s", prefix, command); @@ -308,7 +323,7 @@ bool adb_get_feature_set(FeatureSet* feature_set, std::string* error) { std::string result; - if (adb_query(format_host_command("features", __adb_transport, __adb_serial), &result, error)) { + if (adb_query(format_host_command("features"), &result, error)) { *feature_set = StringToFeatureSet(result); return true; }
diff --git a/adb/adb_client.h b/adb/adb_client.h index fabec00..fca435e 100644 --- a/adb/adb_client.h +++ b/adb/adb_client.h
@@ -40,7 +40,9 @@ std::string* _Nonnull error); // Set the preferred transport to connect to. -void adb_set_transport(TransportType type, const char* _Nullable serial); +void adb_set_transport(TransportType type, const char* _Nullable serial, TransportId transport_id); +void adb_get_transport(TransportType* _Nullable type, const char* _Nullable* _Nullable serial, + TransportId* _Nullable transport_id); // Set the socket specification for the adb server. // This function can only be called once, and the argument must live to the end of the process. @@ -57,8 +59,7 @@ bool adb_status(int fd, std::string* _Nonnull error); // Create a host command corresponding to selected transport type/serial. -std::string format_host_command(const char* _Nonnull command, TransportType type, - const char* _Nullable serial); +std::string format_host_command(const char* _Nonnull command); // Get the feature set of the current preferred transport. bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error);
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp index 6f2403d..b236fb3 100644 --- a/adb/adb_utils.cpp +++ b/adb/adb_utils.cpp
@@ -157,7 +157,12 @@ } std::string dump_hex(const void* data, size_t byte_count) { - byte_count = std::min(byte_count, size_t(16)); + size_t truncate_len = 16; + bool truncated = false; + if (byte_count > truncate_len) { + byte_count = truncate_len; + truncated = true; + } const uint8_t* p = reinterpret_cast<const uint8_t*>(data); @@ -172,6 +177,10 @@ line.push_back(isprint(ch) ? ch : '.'); } + if (truncated) { + line += " [truncated]"; + } + return line; } @@ -278,3 +287,29 @@ fprintf(stderr, "\n"); return 1; } + +std::string GetLogFilePath() { +#if defined(_WIN32) + const char log_name[] = "adb.log"; + WCHAR temp_path[MAX_PATH]; + + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx + DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path); + if (nchars >= arraysize(temp_path) || nchars == 0) { + // If string truncation or some other error. + fatal("cannot retrieve temporary file path: %s\n", + android::base::SystemErrorCodeToString(GetLastError()).c_str()); + } + + std::string temp_path_utf8; + if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) { + fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8"); + } + + return temp_path_utf8 + log_name; +#else + const char* tmp_dir = getenv("TMPDIR"); + if (tmp_dir == nullptr) tmp_dir = "/tmp"; + return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid()); +#endif +}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h index 11c0ec9..f764a0e 100644 --- a/adb/adb_utils.h +++ b/adb/adb_utils.h
@@ -89,4 +89,6 @@ } }; +std::string GetLogFilePath(); + #endif
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp index 372a3b4..abef86a 100644 --- a/adb/bugreport.cpp +++ b/adb/bugreport.cpp
@@ -102,7 +102,7 @@ std::vector<const char*> srcs{src_file_.c_str()}; SetLineMessage("pulling"); status_ = - br_->DoSyncPull(srcs, destination.c_str(), true, line_message_.c_str()) ? 0 : 1; + br_->DoSyncPull(srcs, destination.c_str(), false, line_message_.c_str()) ? 0 : 1; if (status_ != 0) { fprintf(stderr, "Bug report finished but could not be copied to '%s'.\n" @@ -195,13 +195,13 @@ DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback); }; -int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) { +int Bugreport::DoIt(int argc, const char** argv) { if (argc > 2) return syntax_error("adb bugreport [PATH]"); // Gets bugreportz version. std::string bugz_stdout, bugz_stderr; DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr); - int status = SendShellCommand(transport_type, serial, "bugreportz -v", false, &version_callback); + int status = SendShellCommand("bugreportz -v", false, &version_callback); std::string bugz_version = android::base::Trim(bugz_stderr); std::string bugz_output = android::base::Trim(bugz_stdout); @@ -214,7 +214,7 @@ fprintf(stderr, "Failed to get bugreportz version, which is only available on devices " "running Android 7.0 or later.\nTrying a plain-text bug report instead.\n"); - return SendShellCommand(transport_type, serial, "bugreport", false); + return SendShellCommand("bugreport", false); } // But if user explicitly asked for a zipped bug report, fails instead (otherwise calling @@ -265,7 +265,7 @@ bugz_command = "bugreportz"; } BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this); - return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback); + return SendShellCommand(bugz_command, false, &bugz_callback); } void Bugreport::UpdateProgress(const std::string& message, int progress_percentage) { @@ -274,10 +274,9 @@ LinePrinter::INFO); } -int Bugreport::SendShellCommand(TransportType transport_type, const char* serial, - const std::string& command, bool disable_shell_protocol, +int Bugreport::SendShellCommand(const std::string& command, bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) { - return send_shell_command(transport_type, serial, command, disable_shell_protocol, callback); + return send_shell_command(command, disable_shell_protocol, callback); } bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
diff --git a/adb/bugreport.h b/adb/bugreport.h index d9a4468..413439b 100644 --- a/adb/bugreport.h +++ b/adb/bugreport.h
@@ -29,14 +29,13 @@ public: Bugreport() : line_printer_() { } - int DoIt(TransportType transport_type, const char* serial, int argc, const char** argv); + int DoIt(int argc, const char** argv); protected: // Functions below are abstractions of external functions so they can be // mocked on tests. virtual int SendShellCommand( - TransportType transport_type, const char* serial, const std::string& command, - bool disable_shell_protocol, + const std::string& command, bool disable_shell_protocol, StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK); virtual bool DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp index d3787b4..72ca59a 100644 --- a/adb/bugreport_test.cpp +++ b/adb/bugreport_test.cpp
@@ -51,8 +51,8 @@ // Empty functions so tests don't need to be linked against commandline.cpp DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr); -int send_shell_command(TransportType transport_type, const char* serial, const std::string& command, - bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) { +int send_shell_command(const std::string& command, bool disable_shell_protocol, + StandardStreamsCallbackInterface* callback) { ADD_FAILURE() << "send_shell_command() should have been mocked"; return -42; } @@ -62,7 +62,7 @@ kStreamStderr, }; -// gmock black magic to provide a WithArg<4>(WriteOnStdout(output)) matcher +// gmock black magic to provide a WithArg<2>(WriteOnStdout(output)) matcher typedef void OnStandardStreamsCallbackFunction(StandardStreamsCallbackInterface*); class OnStandardStreamsCallbackAction : public ActionInterface<OnStandardStreamsCallbackFunction> { @@ -118,9 +118,8 @@ class BugreportMock : public Bugreport { public: - MOCK_METHOD5(SendShellCommand, - int(TransportType transport_type, const char* serial, const std::string& command, - bool disable_shell_protocol, StandardStreamsCallbackInterface* callback)); + MOCK_METHOD3(SendShellCommand, int(const std::string& command, bool disable_shell_protocol, + StandardStreamsCallbackInterface* callback)); MOCK_METHOD4(DoSyncPull, bool(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs, const char* name)); MOCK_METHOD2(UpdateProgress, void(const std::string&, int)); @@ -136,10 +135,9 @@ } void ExpectBugreportzVersion(const std::string& version) { - EXPECT_CALL(br_, - SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _)) - .WillOnce(DoAll(WithArg<4>(WriteOnStderr(version.c_str())), - WithArg<4>(ReturnCallbackDone(0)))); + EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _)) + .WillOnce(DoAll(WithArg<2>(WriteOnStderr(version.c_str())), + WithArg<2>(ReturnCallbackDone(0)))); } void ExpectProgress(int progress_percentage, const std::string& file = "file.zip") { @@ -153,26 +151,26 @@ // Tests when called with invalid number of arguments TEST_F(BugreportTest, InvalidNumberArgs) { const char* args[] = {"bugreport", "to", "principal"}; - ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 3, args)); + ASSERT_EQ(1, br_.DoIt(3, args)); } // Tests the 'adb bugreport' option when the device does not support 'bugreportz' - it falls back // to the flat-file format ('bugreport' binary on device) TEST_F(BugreportTest, NoArgumentsPreNDevice) { // clang-format off - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _)) - .WillOnce(DoAll(WithArg<4>(WriteOnStderr("")), + EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _)) + .WillOnce(DoAll(WithArg<2>(WriteOnStderr("")), // Write some bogus output on stdout to make sure it's ignored - WithArg<4>(WriteOnStdout("Dude, where is my bugreportz?")), - WithArg<4>(ReturnCallbackDone(0)))); + WithArg<2>(WriteOnStdout("Dude, where is my bugreportz?")), + WithArg<2>(ReturnCallbackDone(0)))); // clang-format on std::string bugreport = "Reported the bug was."; CaptureStdout(); - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreport", false, _)) - .WillOnce(DoAll(WithArg<4>(WriteOnStdout(bugreport)), Return(0))); + EXPECT_CALL(br_, SendShellCommand("bugreport", false, _)) + .WillOnce(DoAll(WithArg<2>(WriteOnStdout(bugreport)), Return(0))); const char* args[] = {"bugreport"}; - ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args)); + ASSERT_EQ(0, br_.DoIt(1, args)); ASSERT_THAT(GetCapturedStdout(), StrEq(bugreport)); } @@ -183,15 +181,15 @@ std::string dest_file = android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR); - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _)) - .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")), - WithArg<4>(ReturnCallbackDone()))); + EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _)) + .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")), + WithArg<2>(ReturnCallbackDone()))); EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file), - true, StrEq("pulling da_bugreport.zip"))) + false, StrEq("pulling da_bugreport.zip"))) .WillOnce(Return(true)); const char* args[] = {"bugreport"}; - ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args)); + ASSERT_EQ(0, br_.DoIt(1, args)); } // Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.1 - it will @@ -201,47 +199,47 @@ std::string dest_file = android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR); ExpectProgress(50, "da_bugreport.zip"); - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) - .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")), - WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")), - WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip\n")), - WithArg<4>(ReturnCallbackDone()))); + EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)) + .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")), + WithArg<2>(WriteOnStdout("PROGRESS:50/100\n")), + WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip\n")), + WithArg<2>(ReturnCallbackDone()))); EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file), - true, StrEq("pulling da_bugreport.zip"))) + false, StrEq("pulling da_bugreport.zip"))) .WillOnce(Return(true)); const char* args[] = {"bugreport"}; - ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args)); + ASSERT_EQ(0, br_.DoIt(1, args)); } // Tests 'adb bugreport file.zip' when it succeeds and device does not support progress. TEST_F(BugreportTest, OkNDevice) { ExpectBugreportzVersion("1.0"); - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _)) - .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")), - WithArg<4>(ReturnCallbackDone()))); + EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _)) + .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")), + WithArg<2>(ReturnCallbackDone()))); EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"), - true, StrEq("pulling file.zip"))) + false, StrEq("pulling file.zip"))) .WillOnce(Return(true)); const char* args[] = {"bugreport", "file.zip"}; - ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(0, br_.DoIt(2, args)); } // Tests 'adb bugreport file.zip' when it succeeds but response was sent in // multiple buffer writers and without progress updates. TEST_F(BugreportTest, OkNDeviceSplitBuffer) { ExpectBugreportzVersion("1.0"); - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _)) - .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device")), - WithArg<4>(WriteOnStdout("/bugreport.zip")), - WithArg<4>(ReturnCallbackDone()))); + EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _)) + .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device")), + WithArg<2>(WriteOnStdout("/bugreport.zip")), + WithArg<2>(ReturnCallbackDone()))); EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"), - true, StrEq("pulling file.zip"))) + false, StrEq("pulling file.zip"))) .WillOnce(Return(true)); const char* args[] = {"bugreport", "file.zip"}; - ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(0, br_.DoIt(2, args)); } // Tests 'adb bugreport file.zip' when it succeeds and displays progress. @@ -252,32 +250,32 @@ ExpectProgress(50); ExpectProgress(99); // clang-format off - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) + EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)) // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit... .WillOnce(DoAll( // Name might change on OK, so make sure the right one is picked. - WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")), + WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")), // Progress line in one write - WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), + WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")), // Add some bogus lines - WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")), + WithArg<2>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")), // Multiple progress lines in one write - WithArg<4>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")), + WithArg<2>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")), // Progress line in multiple writes - WithArg<4>(WriteOnStdout("PROG")), - WithArg<4>(WriteOnStdout("RESS:99")), - WithArg<4>(WriteOnStdout("/100\n")), + WithArg<2>(WriteOnStdout("PROG")), + WithArg<2>(WriteOnStdout("RESS:99")), + WithArg<2>(WriteOnStdout("/100\n")), // Split last message as well, just in case - WithArg<4>(WriteOnStdout("OK:/device/bugreport")), - WithArg<4>(WriteOnStdout(".zip")), - WithArg<4>(ReturnCallbackDone()))); + WithArg<2>(WriteOnStdout("OK:/device/bugreport")), + WithArg<2>(WriteOnStdout(".zip")), + WithArg<2>(ReturnCallbackDone()))); // clang-format on EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"), - true, StrEq("pulling file.zip"))) + false, StrEq("pulling file.zip"))) .WillOnce(Return(true)); const char* args[] = {"bugreport", "file.zip"}; - ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(0, br_.DoIt(2, args)); } // Tests 'adb bugreport file.zip' when it succeeds and displays progress, even if progress recedes. @@ -287,28 +285,28 @@ ExpectProgress(50); ExpectProgress(75); // clang-format off - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) + EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)) // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit... .WillOnce(DoAll( - WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")), - WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1% - WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")), // 50% + WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")), + WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")), // 1% + WithArg<2>(WriteOnStdout("PROGRESS:50/100\n")), // 50% // 25% should be ignored becaused it receded. - WithArg<4>(WriteOnStdout("PROGRESS:25/100\n")), // 25% - WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75% + WithArg<2>(WriteOnStdout("PROGRESS:25/100\n")), // 25% + WithArg<2>(WriteOnStdout("PROGRESS:75/100\n")), // 75% // 75% should be ignored becaused it didn't change. - WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75% + WithArg<2>(WriteOnStdout("PROGRESS:75/100\n")), // 75% // Try a receeding percentage with a different max progress - WithArg<4>(WriteOnStdout("PROGRESS:700/1000\n")), // 70% - WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")), - WithArg<4>(ReturnCallbackDone()))); + WithArg<2>(WriteOnStdout("PROGRESS:700/1000\n")), // 70% + WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")), + WithArg<2>(ReturnCallbackDone()))); // clang-format on EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"), - true, StrEq("pulling file.zip"))) + false, StrEq("pulling file.zip"))) .WillOnce(Return(true)); const char* args[] = {"bugreport", "file.zip"}; - ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(0, br_.DoIt(2, args)); } // Tests 'adb bugreport file.zip' when it succeeds and displays the initial progress of 0% @@ -317,21 +315,21 @@ ExpectProgress(0); ExpectProgress(1); // clang-format off - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) + EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)) // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit... .WillOnce(DoAll( - WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")), - WithArg<4>(WriteOnStdout("PROGRESS:1/100000\n")), - WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1% - WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")), - WithArg<4>(ReturnCallbackDone()))); + WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")), + WithArg<2>(WriteOnStdout("PROGRESS:1/100000\n")), + WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")), // 1% + WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")), + WithArg<2>(ReturnCallbackDone()))); // clang-format on EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"), - true, StrEq("pulling file.zip"))) + false, StrEq("pulling file.zip"))) .WillOnce(Return(true)); const char* args[] = {"bugreport", "file.zip"}; - ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(0, br_.DoIt(2, args)); } // Tests 'adb bugreport dir' when it succeeds and destination is a directory. @@ -341,30 +339,30 @@ std::string dest_file = android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR); - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) - .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")), - WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")), - WithArg<4>(ReturnCallbackDone()))); + EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)) + .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")), + WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")), + WithArg<2>(ReturnCallbackDone()))); EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file), - true, StrEq("pulling da_bugreport.zip"))) + false, StrEq("pulling da_bugreport.zip"))) .WillOnce(Return(true)); const char* args[] = {"bugreport", td.path}; - ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(0, br_.DoIt(2, args)); } // Tests 'adb bugreport file' when it succeeds TEST_F(BugreportTest, OkNoExtension) { ExpectBugreportzVersion("1.1"); - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) - .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip\n")), - WithArg<4>(ReturnCallbackDone()))); + EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)) + .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip\n")), + WithArg<2>(ReturnCallbackDone()))); EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"), - true, StrEq("pulling file.zip"))) + false, StrEq("pulling file.zip"))) .WillOnce(Return(true)); const char* args[] = {"bugreport", "file"}; - ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(0, br_.DoIt(2, args)); } // Tests 'adb bugreport dir' when it succeeds and destination is a directory and device runs N. @@ -374,28 +372,28 @@ std::string dest_file = android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR); - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _)) - .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")), - WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")), - WithArg<4>(ReturnCallbackDone()))); + EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _)) + .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")), + WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")), + WithArg<2>(ReturnCallbackDone()))); EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file), - true, StrEq("pulling da_bugreport.zip"))) + false, StrEq("pulling da_bugreport.zip"))) .WillOnce(Return(true)); const char* args[] = {"bugreport", td.path}; - ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(0, br_.DoIt(2, args)); } // Tests 'adb bugreport file.zip' when the bugreport itself failed TEST_F(BugreportTest, BugreportzReturnedFail) { ExpectBugreportzVersion("1.1"); - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) + EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)) .WillOnce( - DoAll(WithArg<4>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<4>(ReturnCallbackDone()))); + DoAll(WithArg<2>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<2>(ReturnCallbackDone()))); CaptureStderr(); const char* args[] = {"bugreport", "file.zip"}; - ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(-1, br_.DoIt(2, args)); ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!")); } @@ -404,13 +402,13 @@ // multiple buffer writes TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) { ExpectBugreportzVersion("1.1"); - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) - .WillOnce(DoAll(WithArg<4>(WriteOnStdout("FAIL")), WithArg<4>(WriteOnStdout(":D'OH!\n")), - WithArg<4>(ReturnCallbackDone()))); + EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)) + .WillOnce(DoAll(WithArg<2>(WriteOnStdout("FAIL")), WithArg<2>(WriteOnStdout(":D'OH!\n")), + WithArg<2>(ReturnCallbackDone()))); CaptureStderr(); const char* args[] = {"bugreport", "file.zip"}; - ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(-1, br_.DoIt(2, args)); ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!")); } @@ -418,23 +416,22 @@ // response. TEST_F(BugreportTest, BugreportzReturnedUnsupported) { ExpectBugreportzVersion("1.1"); - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) - .WillOnce(DoAll(WithArg<4>(WriteOnStdout("bugreportz? What am I, a zombie?")), - WithArg<4>(ReturnCallbackDone()))); + EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)) + .WillOnce(DoAll(WithArg<2>(WriteOnStdout("bugreportz? What am I, a zombie?")), + WithArg<2>(ReturnCallbackDone()))); CaptureStderr(); const char* args[] = {"bugreport", "file.zip"}; - ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(-1, br_.DoIt(2, args)); ASSERT_THAT(GetCapturedStderr(), HasSubstr("bugreportz? What am I, a zombie?")); } // Tests 'adb bugreport file.zip' when the bugreportz -v command failed TEST_F(BugreportTest, BugreportzVersionFailed) { - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _)) - .WillOnce(Return(666)); + EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _)).WillOnce(Return(666)); const char* args[] = {"bugreport", "file.zip"}; - ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(666, br_.DoIt(2, args)); } // Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output. @@ -442,29 +439,28 @@ ExpectBugreportzVersion(""); const char* args[] = {"bugreport", "file.zip"}; - ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(-1, br_.DoIt(2, args)); } // Tests 'adb bugreport file.zip' when the main bugreportz command failed TEST_F(BugreportTest, BugreportzFailed) { ExpectBugreportzVersion("1.1"); - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) - .WillOnce(Return(666)); + EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)).WillOnce(Return(666)); const char* args[] = {"bugreport", "file.zip"}; - ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(666, br_.DoIt(2, args)); } // Tests 'adb bugreport file.zip' when the bugreport could not be pulled TEST_F(BugreportTest, PullFails) { ExpectBugreportzVersion("1.1"); - EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _)) - .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")), - WithArg<4>(ReturnCallbackDone()))); + EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)) + .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")), + WithArg<2>(ReturnCallbackDone()))); EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"), - true, HasSubstr("file.zip"))) + false, HasSubstr("file.zip"))) .WillOnce(Return(false)); const char* args[] = {"bugreport", "file.zip"}; - ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args)); + ASSERT_EQ(1, br_.DoIt(2, args)); }
diff --git a/adb/client/main.cpp b/adb/client/main.cpp index 62798cd..f0d0ce7 100644 --- a/adb/client/main.cpp +++ b/adb/client/main.cpp
@@ -39,33 +39,7 @@ #include "sysdeps/chrono.h" #include "transport.h" -static std::string GetLogFilePath() { -#if defined(_WIN32) - const char log_name[] = "adb.log"; - WCHAR temp_path[MAX_PATH]; - - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx - DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path); - if (nchars >= arraysize(temp_path) || nchars == 0) { - // If string truncation or some other error. - fatal("cannot retrieve temporary file path: %s\n", - android::base::SystemErrorCodeToString(GetLastError()).c_str()); - } - - std::string temp_path_utf8; - if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) { - fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8"); - } - - return temp_path_utf8 + log_name; -#else - const char* tmp_dir = getenv("TMPDIR"); - if (tmp_dir == nullptr) tmp_dir = "/tmp"; - return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid()); -#endif -} - -static void setup_daemon_logging(void) { +static void setup_daemon_logging() { const std::string log_file_path(GetLogFilePath()); int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640); if (fd == -1) {
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp index e7f44c6..8120199 100644 --- a/adb/client/usb_libusb.cpp +++ b/adb/client/usb_libusb.cpp
@@ -333,6 +333,13 @@ return; } + rc = libusb_set_interface_alt_setting(handle.get(), interface_num, 0); + if (rc != 0) { + LOG(WARNING) << "failed to set interface alt setting for device '" << device_serial + << "'" << libusb_error_name(rc); + return; + } + for (uint8_t endpoint : {bulk_in, bulk_out}) { rc = libusb_clear_halt(handle.get(), endpoint); if (rc != 0) { @@ -412,8 +419,13 @@ if (it != usb_handles.end()) { if (!it->second->device_handle) { // If the handle is null, we were never able to open the device. - unregister_usb_transport(it->second.get()); + + // Temporarily release the usb handles mutex to avoid deadlock. + std::unique_ptr<usb_handle> handle = std::move(it->second); usb_handles.erase(it); + lock.unlock(); + unregister_usb_transport(handle.get()); + lock.lock(); } else { // Closure of the transport will erase the usb_handle. }
diff --git a/adb/commandline.cpp b/adb/commandline.cpp index c9f1ee9..9f23473 100644 --- a/adb/commandline.cpp +++ b/adb/commandline.cpp
@@ -62,11 +62,11 @@ #include "shell_service.h" #include "sysdeps/chrono.h" -static int install_app(TransportType t, const char* serial, int argc, const char** argv); -static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv); -static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv); -static int install_app_legacy(TransportType t, const char* serial, int argc, const char** argv); -static int uninstall_app_legacy(TransportType t, const char* serial, int argc, const char** argv); +static int install_app(int argc, const char** argv); +static int install_multiple_app(int argc, const char** argv); +static int uninstall_app(int argc, const char** argv); +static int install_app_legacy(int argc, const char** argv); +static int uninstall_app_legacy(int argc, const char** argv); extern int gListenAll; @@ -90,6 +90,7 @@ " -d use USB device (error if multiple devices connected)\n" " -e use TCP/IP device (error if multiple TCP/IP devices available)\n" " -s SERIAL use device with given serial (overrides $ANDROID_SERIAL)\n" + " -t ID use device with given transport id\n" " -H name of adb server host [default=localhost]\n" " -P port of adb server [default=5037]\n" " -L SOCKET listen on given socket for adb server [default=tcp:localhost:5037]\n" @@ -685,6 +686,10 @@ // Parse shell-specific command-line options. argv[0] = "adb shell"; // So getopt(3) error messages start "adb shell". +#ifdef _WIN32 + // fixes "adb shell -l" crash on Windows, b/37284906 + __argv = const_cast<char**>(argv); +#endif optind = 1; // argv[0] is always "shell", so set `optind` appropriately. int opt; while ((opt = getopt(argc, const_cast<char**>(argv), "+e:ntTx")) != -1) { @@ -986,13 +991,16 @@ #endif /* !defined(_WIN32) */ } -static bool wait_for_device(const char* service, TransportType t, const char* serial) { +static bool wait_for_device(const char* service) { std::vector<std::string> components = android::base::Split(service, "-"); if (components.size() < 3 || components.size() > 4) { fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service); return false; } + TransportType t; + adb_get_transport(&t, nullptr, nullptr); + // Was the caller vague about what they'd like us to wait for? // If so, check they weren't more specific in their choice of transport type. if (components.size() == 3) { @@ -1019,7 +1027,7 @@ return false; } - std::string cmd = format_host_command(android::base::Join(components, "-").c_str(), t, serial); + std::string cmd = format_host_command(android::base::Join(components, "-").c_str()); return adb_command(cmd); } @@ -1065,8 +1073,8 @@ return true; } -int send_shell_command(TransportType transport_type, const char* serial, const std::string& command, - bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) { +int send_shell_command(const std::string& command, bool disable_shell_protocol, + StandardStreamsCallbackInterface* callback) { int fd; bool use_shell_protocol = false; @@ -1097,7 +1105,7 @@ } fprintf(stderr, "- waiting for device -\n"); - if (!wait_for_device("wait-for-device", transport_type, serial)) { + if (!wait_for_device("wait-for-device")) { return 1; } } @@ -1111,7 +1119,7 @@ return exit_code; } -static int logcat(TransportType transport, const char* serial, int argc, const char** argv) { +static int logcat(int argc, const char** argv) { char* log_tags = getenv("ANDROID_LOG_TAGS"); std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags); @@ -1128,7 +1136,7 @@ } // No need for shell protocol with logcat, always disable for simplicity. - return send_shell_command(transport, serial, cmd, true); + return send_shell_command(cmd, true); } static void write_zeros(int bytes, int fd) { @@ -1340,6 +1348,7 @@ // We need to check for -d and -e before we look at $ANDROID_SERIAL. const char* serial = nullptr; + TransportId transport_id = 0; while (argc > 0) { if (!strcmp(argv[0],"server")) { @@ -1359,7 +1368,7 @@ fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str); return 1; } - } else if (argv[0][0]=='-' && argv[0][1]=='s') { + } else if (!strncmp(argv[0], "-s", 2)) { if (isdigit(argv[0][2])) { serial = argv[0] + 2; } else { @@ -1368,6 +1377,19 @@ argc--; argv++; } + } else if (!strncmp(argv[0], "-t", 2)) { + const char* id; + if (isdigit(argv[0][2])) { + id = argv[0] + 2; + } else { + id = argv[1]; + argc--; + argv++; + } + transport_id = strtoll(id, const_cast<char**>(&id), 10); + if (*id != '\0') { + return syntax_error("invalid transport id"); + } } else if (!strcmp(argv[0],"-d")) { transport_type = kTransportUsb; } else if (!strcmp(argv[0],"-e")) { @@ -1451,7 +1473,7 @@ serial = getenv("ANDROID_SERIAL"); } - adb_set_transport(transport_type, serial); + adb_set_transport(transport_type, serial, transport_id); if (is_server) { if (no_daemon || is_daemon) { @@ -1478,7 +1500,7 @@ if (!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) { const char* service = argv[0]; - if (!wait_for_device(service, transport_type, serial)) { + if (!wait_for_device(service)) { return 1; } @@ -1589,7 +1611,7 @@ return adb_root(argv[0]) ? 0 : 1; } else if (!strcmp(argv[0], "bugreport")) { Bugreport bugreport; - return bugreport.DoIt(transport_type, serial, argc, argv); + return bugreport.DoIt(argc, argv); } else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) { bool reverse = !strcmp(argv[0], "reverse"); ++argv; @@ -1685,20 +1707,20 @@ else if (!strcmp(argv[0], "install")) { if (argc < 2) return syntax_error("install requires an argument"); if (_use_legacy_install()) { - return install_app_legacy(transport_type, serial, argc, argv); + return install_app_legacy(argc, argv); } - return install_app(transport_type, serial, argc, argv); + return install_app(argc, argv); } else if (!strcmp(argv[0], "install-multiple")) { if (argc < 2) return syntax_error("install-multiple requires an argument"); - return install_multiple_app(transport_type, serial, argc, argv); + return install_multiple_app(argc, argv); } else if (!strcmp(argv[0], "uninstall")) { if (argc < 2) return syntax_error("uninstall requires an argument"); if (_use_legacy_install()) { - return uninstall_app_legacy(transport_type, serial, argc, argv); + return uninstall_app_legacy(argc, argv); } - return uninstall_app(transport_type, serial, argc, argv); + return uninstall_app(argc, argv); } else if (!strcmp(argv[0], "sync")) { std::string src; @@ -1752,11 +1774,11 @@ !strcmp(argv[0],"get-serialno") || !strcmp(argv[0],"get-devpath")) { - return adb_query_command(format_host_command(argv[0], transport_type, serial)); + return adb_query_command(format_host_command(argv[0])); } /* other commands */ else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) { - return logcat(transport_type, serial, argc, argv); + return logcat(argc, argv); } else if (!strcmp(argv[0],"ppp")) { return ppp(argc, argv); @@ -1819,7 +1841,7 @@ return adb_query_command("host:host-features"); } else if (!strcmp(argv[0], "reconnect")) { if (argc == 1) { - return adb_query_command(format_host_command(argv[0], transport_type, serial)); + return adb_query_command(format_host_command(argv[0])); } else if (argc == 2) { if (!strcmp(argv[1], "device")) { std::string err; @@ -1838,7 +1860,7 @@ return 1; } -static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) { +static int uninstall_app(int argc, const char** argv) { // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device std::string cmd = "cmd package"; while (argc-- > 0) { @@ -1854,10 +1876,10 @@ cmd += " " + escape_arg(*argv++); } - return send_shell_command(transport, serial, cmd, false); + return send_shell_command(cmd, false); } -static int install_app(TransportType transport, const char* serial, int argc, const char** argv) { +static int install_app(int argc, const char** argv) { // The last argument must be the APK file const char* file = argv[argc - 1]; if (!android::base::EndsWithIgnoreCase(file, ".apk")) { @@ -1910,9 +1932,7 @@ return 1; } -static int install_multiple_app(TransportType transport, const char* serial, int argc, - const char** argv) -{ +static int install_multiple_app(int argc, const char** argv) { // Find all APK arguments starting at end. // All other arguments passed through verbatim. int first_apk = -1; @@ -2037,17 +2057,17 @@ return EXIT_FAILURE; } -static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) { +static int pm_command(int argc, const char** argv) { std::string cmd = "pm"; while (argc-- > 0) { cmd += " " + escape_arg(*argv++); } - return send_shell_command(transport, serial, cmd, false); + return send_shell_command(cmd, false); } -static int uninstall_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) { +static int uninstall_app_legacy(int argc, const char** argv) { /* if the user choose the -k option, we refuse to do it until devices are out with the option to uninstall the remaining data somehow (adb/ui) */ int i; @@ -2063,15 +2083,15 @@ } /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */ - return pm_command(transport, serial, argc, argv); + return pm_command(argc, argv); } -static int delete_file(TransportType transport, const char* serial, const std::string& filename) { +static int delete_file(const std::string& filename) { std::string cmd = "rm -f " + escape_arg(filename); - return send_shell_command(transport, serial, cmd, false); + return send_shell_command(cmd, false); } -static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) { +static int install_app_legacy(int argc, const char** argv) { static const char *const DATA_DEST = "/data/local/tmp/%s"; static const char *const SD_DEST = "/sdcard/tmp/%s"; const char* where = DATA_DEST; @@ -2100,9 +2120,9 @@ where, android::base::Basename(argv[last_apk]).c_str()); if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk; argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */ - result = pm_command(transport, serial, argc, argv); + result = pm_command(argc, argv); cleanup_apk: - delete_file(transport, serial, apk_dest); + delete_file(apk_dest); return result; }
diff --git a/adb/commandline.h b/adb/commandline.h index 9ba69a3..36cd798 100644 --- a/adb/commandline.h +++ b/adb/commandline.h
@@ -91,8 +91,8 @@ // Connects to the device "shell" service with |command| and prints the // resulting output. // if |callback| is non-null, stdout/stderr output will be handled by it. -int send_shell_command(TransportType transport_type, const char* serial, const std::string& command, - bool disable_shell_protocol, StandardStreamsCallbackInterface* callback = - &DEFAULT_STANDARD_STREAMS_CALLBACK); +int send_shell_command( + const std::string& command, bool disable_shell_protocol, + StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK); #endif // COMMANDLINE_H
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp index 1c94298..3c27582 100644 --- a/adb/daemon/main.cpp +++ b/adb/daemon/main.cpp
@@ -233,8 +233,8 @@ adb_device_banner = optarg; break; case 'v': - printf("Android Debug Bridge Daemon version %d.%d.%d (%s)\n", ADB_VERSION_MAJOR, - ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_VERSION); + printf("Android Debug Bridge Daemon version %d.%d.%d\n", ADB_VERSION_MAJOR, + ADB_VERSION_MINOR, ADB_SERVER_VERSION); return 0; default: // getopt already prints "adbd: invalid option -- %c" for us.
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp index 2acf661..3448ee0 100644 --- a/adb/file_sync_service.cpp +++ b/adb/file_sync_service.cpp
@@ -283,25 +283,25 @@ // reading and throwing away ID_DATA packets until the other side notices // that we've reported an error. while (true) { - if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail; + if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) break; if (msg.data.id == ID_DONE) { - goto abort; + break; } else if (msg.data.id != ID_DATA) { char id[5]; memcpy(id, &msg.data.id, sizeof(msg.data.id)); id[4] = '\0'; D("handle_send_fail received unexpected id '%s' during failure", id); - goto abort; + break; } if (msg.data.size > buffer.size()) { D("handle_send_fail received oversized packet of length '%u' during failure", msg.data.size); - goto abort; + break; } - if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort; + if (!ReadFdExactly(s, &buffer[0], msg.data.size)) break; } abort:
diff --git a/adb/services.cpp b/adb/services.cpp index 9605e6e..1660846 100644 --- a/adb/services.cpp +++ b/adb/services.cpp
@@ -58,6 +58,7 @@ #include "transport.h" struct stinfo { + const char* service_name; void (*func)(int fd, void *cookie); int fd; void *cookie; @@ -65,7 +66,7 @@ static void service_bootstrap_func(void* x) { stinfo* sti = reinterpret_cast<stinfo*>(x); - adb_thread_setname(android::base::StringPrintf("service %d", sti->fd)); + adb_thread_setname(android::base::StringPrintf("%s svc %d", sti->service_name, sti->fd)); sti->func(sti->fd, sti->cookie); free(sti); } @@ -159,8 +160,7 @@ return true; } -void reboot_service(int fd, void* arg) -{ +void reboot_service(int fd, void* arg) { if (reboot_service_impl(fd, static_cast<const char*>(arg))) { // Don't return early. Give the reboot command time to take effect // to avoid messing up scripts which do "adb reboot && adb wait-for-device" @@ -187,7 +187,7 @@ return -1; } VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1]; - if (handle_forward_request(command, kTransportAny, nullptr, s[1]) < 0) { + if (handle_forward_request(command, kTransportAny, nullptr, 0, s[1]) < 0) { SendFail(s[1], "not a reverse forwarding command"); } adb_close(s[1]); @@ -235,8 +235,7 @@ #endif // !ADB_HOST -static int create_service_thread(void (*func)(int, void *), void *cookie) -{ +static int create_service_thread(const char* service_name, void (*func)(int, void*), void* cookie) { int s[2]; if (adb_socketpair(s)) { printf("cannot create service socket pair\n"); @@ -257,6 +256,7 @@ if (sti == nullptr) { fatal("cannot allocate stinfo"); } + sti->service_name = service_name; sti->func = func; sti->cookie = cookie; sti->fd = s[1]; @@ -280,7 +280,7 @@ } else if(!strncmp("dev:", name, 4)) { ret = unix_open(name + 4, O_RDWR | O_CLOEXEC); } else if(!strncmp(name, "framebuffer:", 12)) { - ret = create_service_thread(framebuffer_service, 0); + ret = create_service_thread("fb", framebuffer_service, nullptr); } else if (!strncmp(name, "jdwp:", 5)) { ret = create_jdwp_connection_fd(atoi(name+5)); } else if(!strncmp(name, "shell", 5)) { @@ -288,17 +288,17 @@ } else if(!strncmp(name, "exec:", 5)) { ret = StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone); } else if(!strncmp(name, "sync:", 5)) { - ret = create_service_thread(file_sync_service, NULL); + ret = create_service_thread("sync", file_sync_service, nullptr); } else if(!strncmp(name, "remount:", 8)) { - ret = create_service_thread(remount_service, NULL); + ret = create_service_thread("remount", remount_service, nullptr); } else if(!strncmp(name, "reboot:", 7)) { void* arg = strdup(name + 7); if (arg == NULL) return -1; - ret = create_service_thread(reboot_service, arg); + ret = create_service_thread("reboot", reboot_service, arg); } else if(!strncmp(name, "root:", 5)) { - ret = create_service_thread(restart_root_service, NULL); + ret = create_service_thread("root", restart_root_service, nullptr); } else if(!strncmp(name, "unroot:", 7)) { - ret = create_service_thread(restart_unroot_service, NULL); + ret = create_service_thread("unroot", restart_unroot_service, nullptr); } else if(!strncmp(name, "backup:", 7)) { ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(), @@ -311,17 +311,20 @@ if (sscanf(name + 6, "%d", &port) != 1) { return -1; } - ret = create_service_thread(restart_tcp_service, (void *) (uintptr_t) port); + ret = create_service_thread("tcp", restart_tcp_service, reinterpret_cast<void*>(port)); } else if(!strncmp(name, "usb:", 4)) { - ret = create_service_thread(restart_usb_service, NULL); + ret = create_service_thread("usb", restart_usb_service, nullptr); } else if (!strncmp(name, "reverse:", 8)) { ret = reverse_service(name + 8); } else if(!strncmp(name, "disable-verity:", 15)) { - ret = create_service_thread(set_verity_enabled_state_service, (void*)0); + ret = create_service_thread("verity-on", set_verity_enabled_state_service, + reinterpret_cast<void*>(0)); } else if(!strncmp(name, "enable-verity:", 15)) { - ret = create_service_thread(set_verity_enabled_state_service, (void*)1); + ret = create_service_thread("verity-off", set_verity_enabled_state_service, + reinterpret_cast<void*>(1)); } else if (!strcmp(name, "reconnect")) { - ret = create_service_thread(reconnect_service, const_cast<atransport*>(transport)); + ret = create_service_thread("reconnect", reconnect_service, + const_cast<atransport*>(transport)); #endif } if (ret >= 0) { @@ -334,6 +337,7 @@ struct state_info { TransportType transport_type; std::string serial; + TransportId transport_id; ConnectionState state; }; @@ -346,7 +350,8 @@ bool is_ambiguous = false; std::string error = "unknown error"; const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL; - atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error); + atransport* t = acquire_one_transport(sinfo->transport_type, serial, sinfo->transport_id, + &is_ambiguous, &error); if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) { SendOkay(fd); break; @@ -437,7 +442,7 @@ #endif #if ADB_HOST -asocket* host_service_to_socket(const char* name, const char* serial) { +asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id) { if (!strcmp(name,"track-devices")) { return create_device_tracker(); } else if (android::base::StartsWith(name, "wait-for-")) { @@ -450,6 +455,7 @@ } if (serial) sinfo->serial = serial; + sinfo->transport_id = transport_id; if (android::base::StartsWith(name, "local")) { name += strlen("local"); @@ -478,11 +484,17 @@ return nullptr; } - int fd = create_service_thread(wait_for_state, sinfo.release()); + int fd = create_service_thread("wait", wait_for_state, sinfo.get()); + if (fd != -1) { + sinfo.release(); + } return create_local_socket(fd); } else if (!strncmp(name, "connect:", 8)) { char* host = strdup(name + 8); - int fd = create_service_thread(connect_service, host); + int fd = create_service_thread("connect", connect_service, host); + if (fd == -1) { + free(host); + } return create_local_socket(fd); } return NULL;
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp index ee821f8..5b48da0 100644 --- a/adb/shell_service.cpp +++ b/adb/shell_service.cpp
@@ -435,8 +435,7 @@ void Subprocess::ThreadHandler(void* userdata) { Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata); - adb_thread_setname(android::base::StringPrintf( - "shell srvc %d", subprocess->pid())); + adb_thread_setname(android::base::StringPrintf("shell svc %d", subprocess->pid())); D("passing data streams for PID %d", subprocess->pid()); subprocess->PassDataStreams();
diff --git a/adb/socket.h b/adb/socket.h index 4acdf4a..64d05a9 100644 --- a/adb/socket.h +++ b/adb/socket.h
@@ -19,84 +19,83 @@ #include <stddef.h> +#include <memory> + #include "fdevent.h" struct apacket; class atransport; /* An asocket represents one half of a connection between a local and -** remote entity. A local asocket is bound to a file descriptor. A -** remote asocket is bound to the protocol engine. -*/ + * remote entity. A local asocket is bound to a file descriptor. A + * remote asocket is bound to the protocol engine. + */ struct asocket { - /* chain pointers for the local/remote list of - ** asockets that this asocket lives in - */ - asocket *next; - asocket *prev; + /* chain pointers for the local/remote list of + * asockets that this asocket lives in + */ + asocket* next; + asocket* prev; - /* the unique identifier for this asocket - */ + /* the unique identifier for this asocket + */ unsigned id; - /* flag: set when the socket's peer has closed - ** but packets are still queued for delivery - */ - int closing; + /* flag: set when the socket's peer has closed + * but packets are still queued for delivery + */ + int closing; // flag: set when the socket failed to write, so the socket will not wait to // write packets and close directly. bool has_write_error; - /* flag: quit adbd when both ends close the - ** local service socket - */ - int exit_on_close; + /* flag: quit adbd when both ends close the + * local service socket + */ + int exit_on_close; - /* the asocket we are connected to - */ + // the asocket we are connected to + asocket* peer; - asocket *peer; - - /* For local asockets, the fde is used to bind - ** us to our fd event system. For remote asockets - ** these fields are not used. - */ + /* For local asockets, the fde is used to bind + * us to our fd event system. For remote asockets + * these fields are not used. + */ fdevent fde; int fd; - /* queue of apackets waiting to be written - */ - apacket *pkt_first; - apacket *pkt_last; + // queue of apackets waiting to be written + apacket* pkt_first; + apacket* pkt_last; - /* enqueue is called by our peer when it has data - ** for us. It should return 0 if we can accept more - ** data or 1 if not. If we return 1, we must call - ** peer->ready() when we once again are ready to - ** receive data. - */ - int (*enqueue)(asocket *s, apacket *pkt); + /* enqueue is called by our peer when it has data + * for us. It should return 0 if we can accept more + * data or 1 if not. If we return 1, we must call + * peer->ready() when we once again are ready to + * receive data. + */ + int (*enqueue)(asocket* s, apacket* pkt); - /* ready is called by the peer when it is ready for - ** us to send data via enqueue again - */ - void (*ready)(asocket *s); + /* ready is called by the peer when it is ready for + * us to send data via enqueue again + */ + void (*ready)(asocket* s); - /* shutdown is called by the peer before it goes away. - ** the socket should not do any further calls on its peer. - ** Always followed by a call to close. Optional, i.e. can be NULL. - */ - void (*shutdown)(asocket *s); + /* shutdown is called by the peer before it goes away. + * the socket should not do any further calls on its peer. + * Always followed by a call to close. Optional, i.e. can be NULL. + */ + void (*shutdown)(asocket* s); - /* close is called by the peer when it has gone away. - ** we are not allowed to make any further calls on the - ** peer once our close method is called. - */ - void (*close)(asocket *s); + /* close is called by the peer when it has gone away. + * we are not allowed to make any further calls on the + * peer once our close method is called. + */ + void (*close)(asocket* s); - /* A socket is bound to atransport */ - atransport *transport; + /* A socket is bound to atransport */ + atransport* transport; size_t get_max_payload() const; };
diff --git a/adb/sockets.cpp b/adb/sockets.cpp index e0143c6..c53fbb4 100644 --- a/adb/sockets.cpp +++ b/adb/sockets.cpp
@@ -96,7 +96,7 @@ } void remove_socket(asocket* s) { - // socket_list_lock should already be held + std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock); if (s->prev && s->next) { s->prev->next = s->next; s->next->prev = s->prev; @@ -430,10 +430,11 @@ } #if ADB_HOST -static asocket* create_host_service_socket(const char* name, const char* serial) { +static asocket* create_host_service_socket(const char* name, const char* serial, + TransportId transport_id) { asocket* s; - s = host_service_to_socket(name, serial); + s = host_service_to_socket(name, serial, transport_id); if (s != NULL) { D("LS(%d) bound to '%s'", s->id, name); @@ -658,6 +659,7 @@ #if ADB_HOST char* service = nullptr; char* serial = nullptr; + TransportId transport_id = 0; TransportType type = kTransportAny; #endif @@ -715,6 +717,14 @@ serial = service; service = serial_end + 1; } + } else if (!strncmp(service, "host-transport-id:", strlen("host-transport-id:"))) { + service += strlen("host-transport-id:"); + transport_id = strtoll(service, &service, 10); + + if (*service != ':') { + return -1; + } + service++; } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) { type = kTransportUsb; service += strlen("host-usb:"); @@ -736,7 +746,7 @@ ** the OKAY or FAIL message and all we have to do ** is clean up. */ - if (handle_host_request(service, type, serial, s->peer->fd, s) == 0) { + if (handle_host_request(service, type, serial, transport_id, s->peer->fd, s) == 0) { /* XXX fail message? */ D("SS(%d): handled host service '%s'", s->id, service); goto fail; @@ -751,7 +761,7 @@ ** if no such service exists, we'll fail out ** and tear down here. */ - s2 = create_host_service_socket(service, serial); + s2 = create_host_service_socket(service, serial, transport_id); if (s2 == 0) { D("SS(%d): couldn't create host service '%s'", s->id, service); SendFail(s->peer->fd, "unknown host service"); @@ -783,7 +793,7 @@ #else /* !ADB_HOST */ if (s->transport == nullptr) { std::string error_msg = "unknown failure"; - s->transport = acquire_one_transport(kTransportAny, nullptr, nullptr, &error_msg); + s->transport = acquire_one_transport(kTransportAny, nullptr, 0, nullptr, &error_msg); if (s->transport == nullptr) { SendFail(s->peer->fd, error_msg); goto fail;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h index 49c7847..0abb680 100644 --- a/adb/sysdeps.h +++ b/adb/sysdeps.h
@@ -582,18 +582,12 @@ #ifdef __APPLE__ return pthread_setname_np(name.c_str()); #else - const char *s = name.c_str(); - - // pthread_setname_np fails rather than truncating long strings. - const int max_task_comm_len = 16; // including the null terminator - if (name.length() > (max_task_comm_len - 1)) { - char buf[max_task_comm_len]; - strncpy(buf, name.c_str(), sizeof(buf) - 1); - buf[sizeof(buf) - 1] = '\0'; - s = buf; - } - - return pthread_setname_np(pthread_self(), s) ; + // Both bionic and glibc's pthread_setname_np fails rather than truncating long strings. + // glibc doesn't have strlcpy, so we have to fake it. + char buf[16]; // MAX_TASK_COMM_LEN, but that's not exported by the kernel headers. + strncpy(buf, name.c_str(), sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + return pthread_setname_np(pthread_self(), buf); #endif }
diff --git a/adb/test_device.py b/adb/test_device.py index 9e1a2ec..ddceda9 100644 --- a/adb/test_device.py +++ b/adb/test_device.py
@@ -1237,7 +1237,7 @@ return m.group(2) return None - def test_killed_when_pushing_a_large_file(self): + def disabled_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 @@ -1268,7 +1268,7 @@ # 4. The device should be online self.assertEqual(self._get_device_state(serialno), 'device') - def test_killed_when_pulling_a_large_file(self): + def disabled_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
diff --git a/adb/transport.cpp b/adb/transport.cpp index 2bbbefd..c644893 100644 --- a/adb/transport.cpp +++ b/adb/transport.cpp
@@ -21,6 +21,7 @@ #include <ctype.h> #include <errno.h> +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -36,6 +37,7 @@ #include <android-base/quick_exit.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> +#include <android-base/thread_annotations.h> #include "adb.h" #include "adb_auth.h" @@ -46,10 +48,11 @@ static void transport_unref(atransport *t); +// TODO: unordered_map<TransportId, atransport*> static auto& transport_list = *new std::list<atransport*>(); static auto& pending_list = *new std::list<atransport*>(); -static std::mutex& transport_lock = *new std::mutex(); +static auto& transport_lock = *new std::recursive_mutex(); const char* const kFeatureShell2 = "shell_v2"; const char* const kFeatureCmd = "cmd"; @@ -57,6 +60,11 @@ const char* const kFeatureLibusb = "libusb"; const char* const kFeaturePushSync = "push_sync"; +TransportId NextTransportId() { + static std::atomic<TransportId> next(1); + return next++; +} + static std::string dump_packet(const char* name, const char* func, apacket* p) { unsigned command = p->msg.command; int len = p->msg.data_length; @@ -298,9 +306,11 @@ } void kick_transport(atransport* t) { - std::lock_guard<std::mutex> lock(transport_lock); + std::lock_guard<std::recursive_mutex> lock(transport_lock); // As kick_transport() can be called from threads without guarantee that t is valid, // check if the transport is in transport_list first. + // + // TODO(jmgao): WTF? Is this actually true? if (std::find(transport_list.begin(), transport_list.end(), t) != transport_list.end()) { t->Kick(); } @@ -330,7 +340,7 @@ device_tracker** pnode = &device_tracker_list; device_tracker* node = *pnode; - std::lock_guard<std::mutex> lock(transport_lock); + std::lock_guard<std::recursive_mutex> lock(transport_lock); while (node) { if (node == tracker) { *pnode = node->next; @@ -403,7 +413,7 @@ // Check if all of the USB transports are connected. bool iterate_transports(std::function<bool(const atransport*)> fn) { - std::lock_guard<std::mutex> lock(transport_lock); + std::lock_guard<std::recursive_mutex> lock(transport_lock); for (const auto& t : transport_list) { if (!fn(t)) { return false; @@ -507,7 +517,7 @@ adb_close(t->fd); { - std::lock_guard<std::mutex> lock(transport_lock); + std::lock_guard<std::recursive_mutex> lock(transport_lock); transport_list.remove(t); } @@ -546,7 +556,7 @@ } { - std::lock_guard<std::mutex> lock(transport_lock); + std::lock_guard<std::recursive_mutex> lock(transport_lock); pending_list.remove(t); transport_list.push_front(t); } @@ -573,7 +583,7 @@ void kick_all_transports() { // To avoid only writing part of a packet to a transport after exit, kick all transports. - std::lock_guard<std::mutex> lock(transport_lock); + std::lock_guard<std::recursive_mutex> lock(transport_lock); for (auto t : transport_list) { t->Kick(); } @@ -603,15 +613,15 @@ static void transport_unref(atransport* t) { CHECK(t != nullptr); - size_t old_refcount = t->ref_count--; - CHECK_GT(old_refcount, 0u); - - if (old_refcount == 1u) { + std::lock_guard<std::recursive_mutex> lock(transport_lock); + CHECK_GT(t->ref_count, 0u); + t->ref_count--; + if (t->ref_count == 0) { D("transport: %s unref (kicking and closing)", t->serial); t->close(t); remove_transport(t); } else { - D("transport: %s unref (count=%zu)", t->serial, old_refcount - 1); + D("transport: %s unref (count=%zu)", t->serial, t->ref_count); } } @@ -638,11 +648,15 @@ return !*to_test; } -atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous, - std::string* error_out, bool accept_any_state) { +atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id, + bool* is_ambiguous, std::string* error_out, + bool accept_any_state) { atransport* result = nullptr; - if (serial) { + if (transport_id != 0) { + *error_out = + android::base::StringPrintf("no device with transport id '%" PRIu64 "'", transport_id); + } else if (serial) { *error_out = android::base::StringPrintf("device '%s' not found", serial); } else if (type == kTransportLocal) { *error_out = "no emulators found"; @@ -652,7 +666,7 @@ *error_out = "no devices found"; } - std::unique_lock<std::mutex> lock(transport_lock); + std::unique_lock<std::recursive_mutex> lock(transport_lock); for (const auto& t : transport_list) { if (t->GetConnectionState() == kCsNoPerm) { #if ADB_HOST @@ -661,8 +675,12 @@ continue; } - // Check for matching serial number. - if (serial) { + if (transport_id) { + if (t->id == transport_id) { + result = t; + break; + } + } else if (serial) { if (t->MatchesTarget(serial)) { if (result) { *error_out = "more than one device"; @@ -728,9 +746,6 @@ } int atransport::Write(apacket* p) { -#if ADB_HOST - std::lock_guard<std::mutex> lock(write_msg_lock_); -#endif return write_func_(p, this); } @@ -738,11 +753,6 @@ 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); } } @@ -889,18 +899,23 @@ #if ADB_HOST +// We use newline as our delimiter, make sure to never output it. +static std::string sanitize(std::string str, bool alphanumeric) { + auto pred = alphanumeric ? [](const char c) { return !isalnum(c); } + : [](const char c) { return c == '\n'; }; + std::replace_if(str.begin(), str.end(), pred, '_'); + return str; +} + static void append_transport_info(std::string* result, const char* key, const char* value, - bool sanitize) { + bool alphanumeric) { if (value == nullptr || *value == '\0') { return; } *result += ' '; *result += key; - - for (const char* p = value; *p; ++p) { - result->push_back((!sanitize || isalnum(*p)) ? *p : '_'); - } + *result += sanitize(value, alphanumeric); } static void append_transport(const atransport* t, std::string* result, bool long_listing) { @@ -920,6 +935,11 @@ append_transport_info(result, "product:", t->product, false); append_transport_info(result, "model:", t->model, true); append_transport_info(result, "device:", t->device, false); + + // Put id at the end, so that anyone parsing the output here can always find it by scanning + // backwards from newlines, even with hypothetical devices named 'transport_id:1'. + *result += " transport_id:"; + *result += std::to_string(t->id); } *result += '\n'; } @@ -927,7 +947,7 @@ std::string list_transports(bool long_listing) { std::string result; - std::lock_guard<std::mutex> lock(transport_lock); + std::lock_guard<std::recursive_mutex> lock(transport_lock); for (const auto& t : transport_list) { append_transport(t, &result, long_listing); } @@ -935,7 +955,7 @@ } void close_usb_devices(std::function<bool(const atransport*)> predicate) { - std::lock_guard<std::mutex> lock(transport_lock); + std::lock_guard<std::recursive_mutex> lock(transport_lock); for (auto& t : transport_list) { if (predicate(t)) { t->Kick(); @@ -964,7 +984,7 @@ return -1; } - std::unique_lock<std::mutex> lock(transport_lock); + std::unique_lock<std::recursive_mutex> lock(transport_lock); for (const auto& transport : pending_list) { if (transport->serial && strcmp(serial, transport->serial) == 0) { VLOG(TRANSPORT) << "socket transport " << transport->serial @@ -996,7 +1016,7 @@ atransport* find_transport(const char* serial) { atransport* result = nullptr; - std::lock_guard<std::mutex> lock(transport_lock); + std::lock_guard<std::recursive_mutex> lock(transport_lock); for (auto& t : transport_list) { if (t->serial && strcmp(serial, t->serial) == 0) { result = t; @@ -1008,7 +1028,7 @@ } void kick_all_tcp_devices() { - std::lock_guard<std::mutex> lock(transport_lock); + std::lock_guard<std::recursive_mutex> lock(transport_lock); for (auto& t : transport_list) { if (t->IsTcpDevice()) { // Kicking breaks the read_transport thread of this transport out of any read, then @@ -1037,7 +1057,7 @@ } { - std::lock_guard<std::mutex> lock(transport_lock); + std::lock_guard<std::recursive_mutex> lock(transport_lock); pending_list.push_front(t); } @@ -1046,7 +1066,7 @@ // This should only be used for transports with connection_state == kCsNoPerm. void unregister_usb_transport(usb_handle* usb) { - std::lock_guard<std::mutex> lock(transport_lock); + std::lock_guard<std::recursive_mutex> lock(transport_lock); transport_list.remove_if( [usb](atransport* t) { return t->usb == usb && t->GetConnectionState() == kCsNoPerm; }); } @@ -1079,11 +1099,4 @@ 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 4a89ed9..0f1a1d4 100644 --- a/adb/transport.h +++ b/adb/transport.h
@@ -54,14 +54,17 @@ // The server supports `push --sync`. extern const char* const kFeaturePushSync; +TransportId NextTransportId(); + class atransport { -public: + public: // TODO(danalbert): We expose waaaaaaay too much stuff because this was // historically just a struct, but making the whole thing a more idiomatic // class in one go is a very large change. Given how bad our testing is, // it's better to do this piece by piece. - atransport(ConnectionState state = kCsOffline) : ref_count(0), connection_state_(state) { + atransport(ConnectionState state = kCsOffline) + : id(NextTransportId()), connection_state_(state) { transport_fde = {}; protocol_version = A_VERSION; max_payload = MAX_PAYLOAD; @@ -72,12 +75,8 @@ 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_; - } + void SetKickFunction(void (*kick_func)(atransport*)) { kick_func_ = kick_func; } + bool IsKicked() { return kicked_; } int Write(apacket* p); void Kick(); @@ -85,10 +84,11 @@ ConnectionState GetConnectionState() const; void SetConnectionState(ConnectionState state); + const TransportId id; int fd = -1; int transport_socket = -1; fdevent transport_fde; - std::atomic<size_t> ref_count; + size_t ref_count = 0; uint32_t sync_token = 0; bool online = false; TransportType type = kTransportAny; @@ -122,7 +122,6 @@ #if ADB_HOST std::shared_ptr<RSA> NextKey(); - bool SetSendConnectOnError(); #endif char token[TOKEN_SIZE] = {}; @@ -181,8 +180,6 @@ 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); @@ -191,12 +188,14 @@ /* * Obtain a transport from the available transports. * If serial is non-null then only the device with that serial will be chosen. + * If transport_id is non-zero then only the device with that transport ID will be chosen. * If multiple devices/emulators would match, *is_ambiguous (if non-null) * is set to true and nullptr returned. * If no suitable transport is found, error is set and nullptr returned. */ -atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous, - std::string* error_out, bool accept_any_state = false); +atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id, + bool* is_ambiguous, std::string* error_out, + bool accept_any_state = false); void kick_transport(atransport* t); void update_transports(void);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp index 809ed89..9cd378c 100644 --- a/adb/transport_local.cpp +++ b/adb/transport_local.cpp
@@ -388,6 +388,25 @@ D("transport: qemu_socket_thread() exiting"); return; } + +// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka +// goldfish) as the transport. This can either be explicitly set by the +// service.adb.transport property, or be inferred from ro.kernel.qemu that is +// set to "1" for ranchu/goldfish. +static bool use_qemu_goldfish() { + // Legacy way to detect if adbd should use the goldfish pipe is to check for + // ro.kernel.qemu, keep that behaviour for backward compatibility. + if (android::base::GetBoolProperty("ro.kernel.qemu", false)) { + return true; + } + // If service.adb.transport is present and is set to "goldfish", use the + // QEMUD pipe. + if (android::base::GetProperty("service.adb.transport", "") == "goldfish") { + return true; + } + return false; +} + #endif // !ADB_HOST void local_init(int port) @@ -401,13 +420,7 @@ #else // For the adbd daemon in the system image we need to distinguish // between the device, and the emulator. - if (android::base::GetBoolProperty("ro.kernel.qemu", false)) { - // Running inside the emulator: use QEMUD pipe as the transport. - func = qemu_socket_thread; - } else { - // Running inside the device: use TCP socket as the transport. - func = server_socket_thread; - } + func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread; debug_name = "server"; #endif // !ADB_HOST
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp index 7e8ae67..e5845f0 100644 --- a/adb/transport_usb.cpp +++ b/adb/transport_usb.cpp
@@ -103,13 +103,6 @@ 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; } @@ -162,8 +155,7 @@ return 0; } -static void remote_close(atransport *t) -{ +static void remote_close(atransport* t) { usb_close(t->usb); t->usb = 0; }
diff --git a/adf/libadf/Android.bp b/adf/libadf/Android.bp index c276c53..8eef2ea 100644 --- a/adf/libadf/Android.bp +++ b/adf/libadf/Android.bp
@@ -12,8 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -cc_library_static { +cc_library { name: "libadf", + vendor_available: true, + vndk: { + enabled: true, + }, srcs: ["adf.cpp"], cflags: ["-Werror"], local_include_dirs: ["include"],
diff --git a/base/Android.bp b/base/Android.bp index b636dc3..6c3a593 100644 --- a/base/Android.bp +++ b/base/Android.bp
@@ -41,6 +41,10 @@ vendor_available: true, clang: true, host_supported: true, + vndk: { + enabled: true, + support_system_process: true, + }, srcs: [ "file.cpp", "logging.cpp",
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp index 5eedf3b..b6bf701 100644 --- a/base/chrono_utils.cpp +++ b/base/chrono_utils.cpp
@@ -33,5 +33,10 @@ #endif // __ANDROID__ } +std::ostream& operator<<(std::ostream& os, const Timer& t) { + os << t.duration().count() << "ms"; + return os; +} + } // namespace base } // namespace android
diff --git a/base/chrono_utils_test.cpp b/base/chrono_utils_test.cpp index 057132d..da442f4 100644 --- a/base/chrono_utils_test.cpp +++ b/base/chrono_utils_test.cpp
@@ -19,6 +19,9 @@ #include <time.h> #include <chrono> +#include <sstream> +#include <string> +#include <thread> #include <gtest/gtest.h> @@ -42,5 +45,36 @@ EXPECT_EQ(now, boot_seconds); } +template <typename T> +void ExpectAboutEqual(T expected, T actual) { + auto expected_upper_bound = expected * 1.05f; + auto expected_lower_bound = expected * .95; + EXPECT_GT(expected_upper_bound, actual); + EXPECT_LT(expected_lower_bound, actual); +} + +TEST(ChronoUtilsTest, TimerDurationIsSane) { + auto start = boot_clock::now(); + Timer t; + std::this_thread::sleep_for(50ms); + auto stop = boot_clock::now(); + auto stop_timer = t.duration(); + + auto expected = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start); + ExpectAboutEqual(expected, stop_timer); +} + +TEST(ChronoUtilsTest, TimerOstream) { + Timer t; + std::this_thread::sleep_for(50ms); + auto stop_timer = t.duration().count(); + std::stringstream os; + os << t; + decltype(stop_timer) stop_timer_from_stream; + os >> stop_timer_from_stream; + EXPECT_NE(0, stop_timer); + ExpectAboutEqual(stop_timer, stop_timer_from_stream); +} + } // namespace base -} // namespace android \ No newline at end of file +} // namespace android
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h index 0086425..7679d4c 100644 --- a/base/include/android-base/chrono_utils.h +++ b/base/include/android-base/chrono_utils.h
@@ -18,6 +18,9 @@ #define ANDROID_BASE_CHRONO_UTILS_H #include <chrono> +#include <sstream> + +using namespace std::chrono_literals; namespace android { namespace base { @@ -31,6 +34,20 @@ static time_point now(); }; +class Timer { + public: + Timer() : start_(boot_clock::now()) {} + + std::chrono::milliseconds duration() const { + return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_); + } + + private: + boot_clock::time_point start_; +}; + +std::ostream& operator<<(std::ostream& os, const Timer& t); + } // namespace base } // namespace android
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h index 2d5a6f6..c9cc1ab 100755 --- a/base/include/android-base/utf8.h +++ b/base/include/android-base/utf8.h
@@ -22,6 +22,8 @@ #else // Bring in prototypes for standard APIs so that we can import them into the utf8 namespace. #include <fcntl.h> // open +#include <stdio.h> // fopen +#include <sys/stat.h> // mkdir #include <unistd.h> // unlink #endif @@ -53,6 +55,19 @@ // Convert a UTF-8 std::string (including any embedded NULL characters) to // UTF-16. Returns whether the conversion was done successfully. bool UTF8ToWide(const std::string& utf8, std::wstring* utf16); + +// Convert a file system path, represented as a NULL-terminated string of +// UTF-8 characters, to a UTF-16 string representing the same file system +// path using the Windows extended-lengh path representation. +// +// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH: +// ```The Windows API has many functions that also have Unicode versions to +// permit an extended-length path for a maximum total path length of 32,767 +// characters. To specify an extended-length path, use the "\\?\" prefix. +// For example, "\\?\D:\very long path".``` +// +// Returns whether the conversion was done successfully. +bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16); #endif // The functions in the utf8 namespace take UTF-8 strings. For Windows, these @@ -73,9 +88,13 @@ namespace utf8 { #ifdef _WIN32 +FILE* fopen(const char* name, const char* mode); +int mkdir(const char* name, mode_t mode); int open(const char* name, int flags, ...); int unlink(const char* name); #else +using ::fopen; +using ::mkdir; using ::open; using ::unlink; #endif
diff --git a/base/utf8.cpp b/base/utf8.cpp index 3cca700..5984fb0 100644 --- a/base/utf8.cpp +++ b/base/utf8.cpp
@@ -19,7 +19,9 @@ #include "android-base/utf8.h" #include <fcntl.h> +#include <stdio.h> +#include <algorithm> #include <string> #include "android-base/logging.h" @@ -153,12 +155,58 @@ return UTF8ToWide(utf8.c_str(), utf8.length(), utf16); } +static bool isDriveLetter(wchar_t c) { + return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z'); +} + +bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16) { + if (!UTF8ToWide(utf8, utf16)) { + return false; + } + // Note: Although most Win32 File I/O API are limited to MAX_PATH (260 + // characters), the CreateDirectory API is limited to 248 characters. + if (utf16->length() >= 248) { + // If path is of the form "x:\" or "x:/" + if (isDriveLetter((*utf16)[0]) && (*utf16)[1] == L':' && + ((*utf16)[2] == L'\\' || (*utf16)[2] == L'/')) { + // Append long path prefix, and make sure there are no unix-style + // separators to ensure a fully compliant Win32 long path string. + utf16->insert(0, LR"(\\?\)"); + std::replace(utf16->begin(), utf16->end(), L'/', L'\\'); + } + } + return true; +} + // Versions of standard library APIs that support UTF-8 strings. namespace utf8 { +FILE* fopen(const char* name, const char* mode) { + std::wstring name_utf16; + if (!UTF8PathToWindowsLongPath(name, &name_utf16)) { + return nullptr; + } + + std::wstring mode_utf16; + if (!UTF8ToWide(mode, &mode_utf16)) { + return nullptr; + } + + return _wfopen(name_utf16.c_str(), mode_utf16.c_str()); +} + +int mkdir(const char* name, mode_t mode) { + std::wstring name_utf16; + if (!UTF8PathToWindowsLongPath(name, &name_utf16)) { + return -1; + } + + return _wmkdir(name_utf16.c_str()); +} + int open(const char* name, int flags, ...) { std::wstring name_utf16; - if (!UTF8ToWide(name, &name_utf16)) { + if (!UTF8PathToWindowsLongPath(name, &name_utf16)) { return -1; } @@ -175,7 +223,7 @@ int unlink(const char* name) { std::wstring name_utf16; - if (!UTF8ToWide(name, &name_utf16)) { + if (!UTF8PathToWindowsLongPath(name, &name_utf16)) { return -1; }
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp index ae8fc8c..fcb25c3 100644 --- a/base/utf8_test.cpp +++ b/base/utf8_test.cpp
@@ -18,7 +18,12 @@ #include <gtest/gtest.h> +#include <fcntl.h> +#include <stdlib.h> + #include "android-base/macros.h" +#include "android-base/test_utils.h" +#include "android-base/unique_fd.h" namespace android { namespace base { @@ -408,5 +413,76 @@ EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null)); } +TEST(UTF8PathToWindowsLongPathTest, DontAddPrefixIfShorterThanMaxPath) { + std::string utf8 = "c:\\mypath\\myfile.txt"; + + std::wstring wide; + EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide)); + + EXPECT_EQ(std::string::npos, wide.find(LR"(\\?\)")); +} + +TEST(UTF8PathToWindowsLongPathTest, AddPrefixIfLongerThanMaxPath) { + std::string utf8 = "c:\\mypath"; + while (utf8.length() < 300 /* MAX_PATH is 260 */) { + utf8 += "\\mypathsegment"; + } + + std::wstring wide; + EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide)); + + EXPECT_EQ(0U, wide.find(LR"(\\?\)")); + EXPECT_EQ(std::string::npos, wide.find(L"/")); +} + +TEST(UTF8PathToWindowsLongPathTest, AddPrefixAndFixSeparatorsIfLongerThanMaxPath) { + std::string utf8 = "c:/mypath"; + while (utf8.length() < 300 /* MAX_PATH is 260 */) { + utf8 += "/mypathsegment"; + } + + std::wstring wide; + EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide)); + + EXPECT_EQ(0U, wide.find(LR"(\\?\)")); + EXPECT_EQ(std::string::npos, wide.find(L"/")); +} + +namespace utf8 { + +TEST(Utf8FilesTest, CanCreateOpenAndDeleteFileWithLongPath) { + TemporaryDir td; + + // Create long directory path + std::string utf8 = td.path; + while (utf8.length() < 300 /* MAX_PATH is 260 */) { + utf8 += "\\mypathsegment"; + EXPECT_EQ(0, mkdir(utf8.c_str(), 0)); + } + + // Create file + utf8 += "\\test-file.bin"; + int flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY; + int mode = 0666; + android::base::unique_fd fd(open(utf8.c_str(), flags, mode)); + EXPECT_NE(-1, fd.get()); + + // Close file + fd.reset(); + EXPECT_EQ(-1, fd.get()); + + // Open file with fopen + FILE* file = fopen(utf8.c_str(), "rb"); + EXPECT_NE(nullptr, file); + + if (file) { + fclose(file); + } + + // Delete file + EXPECT_EQ(0, unlink(utf8.c_str())); +} + +} // namespace utf8 } // namespace base } // namespace android
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp index a4c2160..f887d46 100644 --- a/bootstat/bootstat.cpp +++ b/bootstat/bootstat.cpp
@@ -85,12 +85,13 @@ fprintf(stderr, "Usage: %s [options]\n", cmd); fprintf(stderr, "options include:\n" - " -h, --help Show this help\n" - " -l, --log Log all metrics to logstorage\n" - " -p, --print Dump the boot event records to the console\n" - " -r, --record Record the timestamp of a named boot event\n" - " --value Optional value to associate with the boot event\n" - " --record_boot_reason Record the reason why the device booted\n" + " -h, --help Show this help\n" + " -l, --log Log all metrics to logstorage\n" + " -p, --print Dump the boot event records to the console\n" + " -r, --record Record the timestamp of a named boot event\n" + " --value Optional value to associate with the boot event\n" + " --record_boot_complete Record metrics related to the time for the device boot\n" + " --record_boot_reason Record the reason why the device booted\n" " --record_time_since_factory_reset Record the time since the device was reset\n"); } @@ -168,6 +169,13 @@ {"wdog_bark", 42}, {"wdog_bite", 43}, {"wdog_reset", 44}, + {"shutdown,", 45}, // Trailing comma is intentional. + {"shutdown,userrequested", 46}, + {"reboot,bootloader", 47}, + {"reboot,cold", 48}, + {"reboot,recovery", 49}, + {"thermal_shutdown", 50}, + {"s3_wakeup", 51} }; // Converts a string value representing the reason the system booted to an @@ -350,7 +358,12 @@ // Records the boot_reason metric by querying the ro.boot.bootreason system // property. void RecordBootReason() { - int32_t boot_reason = BootReasonStrToEnum(GetProperty("ro.boot.bootreason")); + std::string boot_reason_str = GetProperty("ro.boot.bootreason"); + android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT, + android::metricslogger::FIELD_PLATFORM_REASON, + boot_reason_str); + + int32_t boot_reason = BootReasonStrToEnum(boot_reason_str); BootEventRecordStore boot_event_store; boot_event_store.AddBootEventWithValue("boot_reason", boot_reason); }
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc index f4756d5..2c48fae 100644 --- a/bootstat/bootstat.rc +++ b/bootstat/bootstat.rc
@@ -1,7 +1,39 @@ # This file is the LOCAL_INIT_RC file for the bootstat command. on post-fs-data - mkdir /data/misc/bootstat 0700 root root + mkdir /data/misc/bootstat 0700 system log + # To deal with ota transition resulting from a change in DAC from + # root.root to system.log, may be deleted after ota has settled. + chown system log /data/misc/bootstat/absolute_boot_time + chown system log /data/misc/bootstat/boot_complete + chown system log /data/misc/bootstat/boot_complete_no_encryption + chown system log /data/misc/bootstat/boot_reason + chown system log /data/misc/bootstat/boottime.bootloader.1BLE + chown system log /data/misc/bootstat/boottime.bootloader.1BLL + chown system log /data/misc/bootstat/boottime.bootloader.2BLE + chown system log /data/misc/bootstat/boottime.bootloader.2BLL + chown system log /data/misc/bootstat/boottime.bootloader.AVB + chown system log /data/misc/bootstat/boottime.bootloader.KD + chown system log /data/misc/bootstat/boottime.bootloader.KL + chown system log /data/misc/bootstat/boottime.bootloader.ODT + chown system log /data/misc/bootstat/boottime.bootloader.SW + chown system log /data/misc/bootstat/boottime.bootloader.total + chown system log /data/misc/bootstat/build_date + chown system log /data/misc/bootstat/factory_reset + chown system log /data/misc/bootstat/factory_reset_boot_complete + chown system log /data/misc/bootstat/factory_reset_boot_complete_no_encryption + chown system log /data/misc/bootstat/factory_reset_current_time + chown system log /data/misc/bootstat/factory_reset_record_value + chown system log /data/misc/bootstat/last_boot_time_utc + chown system log /data/misc/bootstat/ota_boot_complete + chown system log /data/misc/bootstat/ota_boot_complete_no_encryption + chown system log /data/misc/bootstat/post_decrypt_time_elapsed + chown system log /data/misc/bootstat/ro.boottime.init + chown system log /data/misc/bootstat/ro.boottime.init.cold_boot_wait + chown system log /data/misc/bootstat/ro.boottime.init.selinux + chown system log /data/misc/bootstat/time_since_factory_reset + chown system log /data/misc/bootstat/time_since_last_boot + # end ota transitional support # Record the time at which the user has successfully entered the pin to decrypt # the device, /data is decrypted, and the system is entering the main boot phase. @@ -10,7 +42,7 @@ # property:init.svc.bootanim=running: The boot animation is running # property:ro.crypto.type=block: FDE device on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block - exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed + exec - system log -- /system/bin/bootstat -r post_decrypt_time_elapsed # sys.logbootcomplete is a signal to enable the bootstat logging mechanism. # This signaling is necessary to prevent logging boot metrics after a runtime @@ -33,13 +65,13 @@ # Record boot complete metrics. on property:sys.boot_completed=1 && property:sys.logbootcomplete=1 # Record boot_complete and related stats (decryption, etc). - exec - root root -- /system/bin/bootstat --record_boot_complete + exec - system log -- /system/bin/bootstat --record_boot_complete # Record the boot reason. - exec - root root -- /system/bin/bootstat --record_boot_reason + exec - system log -- /system/bin/bootstat --record_boot_reason # Record time since factory reset. - exec - root root -- /system/bin/bootstat --record_time_since_factory_reset + exec - system log -- /system/bin/bootstat --record_time_since_factory_reset # Log all boot events. - exec - root root -- /system/bin/bootstat -l + exec - system log -- /system/bin/bootstat -l
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp index 5565cfd..f86aaa0 100644 --- a/debuggerd/Android.bp +++ b/debuggerd/Android.bp
@@ -274,7 +274,7 @@ "libbase", "libdebuggerd_client", "liblog", - "libselinux", + "libprocinfo", ], local_include_dirs: ["include"],
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp index 8420f03..9c2f0d6 100644 --- a/debuggerd/client/debuggerd_client_test.cpp +++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -27,6 +27,7 @@ #include <gtest/gtest.h> #include <android-base/file.h> +#include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> @@ -36,8 +37,20 @@ using namespace std::chrono_literals; using android::base::unique_fd; +static int getThreadCount() { + int threadCount = 1024; + std::vector<std::string> characteristics = + android::base::Split(android::base::GetProperty("ro.build.characteristics", ""), ","); + if (std::find(characteristics.begin(), characteristics.end(), "embedded") + != characteristics.end()) { + // 128 is the realistic number for iot devices. + threadCount = 128; + } + return threadCount; +} + TEST(debuggerd_client, race) { - static constexpr int THREAD_COUNT = 1024; + static int THREAD_COUNT = getThreadCount(); pid_t forkpid = fork(); ASSERT_NE(-1, forkpid);
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp index 5db0e5f..4b1e51d 100644 --- a/debuggerd/crash_dump.cpp +++ b/debuggerd/crash_dump.cpp
@@ -44,6 +44,9 @@ #include <private/android_filesystem_config.h> #include <procinfo/process.h> +#define ATRACE_TAG ATRACE_TAG_BIONIC +#include <utils/Trace.h> + #include "backtrace.h" #include "tombstone.h" #include "utility.h" @@ -101,6 +104,7 @@ } static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data) { + ATRACE_CALL(); android::base::unique_fd amfd(socket_local_client( "/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM)); if (amfd.get() == -1) { @@ -176,6 +180,7 @@ } static void drop_capabilities() { + ATRACE_CALL(); __user_cap_header_struct capheader; memset(&capheader, 0, sizeof(capheader)); capheader.version = _LINUX_CAPABILITY_VERSION_3; @@ -194,6 +199,8 @@ } int main(int argc, char** argv) { + atrace_begin(ATRACE_TAG, "before reparent"); + pid_t target = getppid(); bool tombstoned_connected = false; unique_fd tombstoned_socket; @@ -261,6 +268,8 @@ PLOG(FATAL) << "parent died"; } + atrace_end(ATRACE_TAG); + // Reparent ourselves to init, so that the signal handler can waitpid on the // original process to avoid leaving a zombie for non-fatal dumps. pid_t forkpid = fork(); @@ -270,6 +279,8 @@ exit(0); } + ATRACE_NAME("after reparent"); + // Die if we take too long. // // Note: processes with many threads and minidebug-info can take a bit to @@ -278,42 +289,53 @@ std::string attach_error; - // Seize the main thread. - if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) { - LOG(FATAL) << attach_error; - } - - // Seize the siblings. std::map<pid_t, std::string> threads; + { - std::set<pid_t> siblings; - if (!android::procinfo::GetProcessTids(target, &siblings)) { - PLOG(FATAL) << "failed to get process siblings"; + ATRACE_NAME("ptrace"); + // Seize the main thread. + if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) { + LOG(FATAL) << attach_error; } - // but not the already attached main thread. - siblings.erase(main_tid); - // or the handler pseudothread. - siblings.erase(pseudothread_tid); + // Seize the siblings. + { + std::set<pid_t> siblings; + if (!android::procinfo::GetProcessTids(target, &siblings)) { + PLOG(FATAL) << "failed to get process siblings"; + } - for (pid_t sibling_tid : siblings) { - if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) { - LOG(WARNING) << attach_error; - } else { - threads.emplace(sibling_tid, get_thread_name(sibling_tid)); + // but not the already attached main thread. + siblings.erase(main_tid); + // or the handler pseudothread. + siblings.erase(pseudothread_tid); + + for (pid_t sibling_tid : siblings) { + if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) { + LOG(WARNING) << attach_error; + } else { + threads.emplace(sibling_tid, get_thread_name(sibling_tid)); + } } } } // Collect the backtrace map, open files, and process/thread names, while we still have caps. - std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(main_tid)); - if (!backtrace_map) { - LOG(FATAL) << "failed to create backtrace map"; + std::unique_ptr<BacktraceMap> backtrace_map; + { + ATRACE_NAME("backtrace map"); + backtrace_map.reset(BacktraceMap::Create(main_tid)); + if (!backtrace_map) { + LOG(FATAL) << "failed to create backtrace map"; + } } // Collect the list of open files. OpenFilesList open_files; - populate_open_files_list(target, &open_files); + { + ATRACE_NAME("open files"); + populate_open_files_list(target, &open_files); + } std::string process_name = get_process_name(main_tid); threads.emplace(main_tid, get_thread_name(main_tid)); @@ -321,9 +343,12 @@ // Drop our capabilities now that we've attached to the threads we care about. drop_capabilities(); - const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type); - LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum; - tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum); + { + ATRACE_NAME("tombstoned_connect"); + const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type); + LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum; + tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum); + } // Write a '\1' to stdout to tell the crashing process to resume. // It also restores the value of PR_SET_DUMPABLE at this point. @@ -352,9 +377,12 @@ } siginfo_t siginfo = {}; - if (!wait_for_signal(main_tid, &siginfo)) { - printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno)); - exit(1); + { + ATRACE_NAME("wait_for_signal"); + if (!wait_for_signal(main_tid, &siginfo)) { + printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno)); + exit(1); + } } int signo = siginfo.si_signo; @@ -376,8 +404,10 @@ std::string amfd_data; if (backtrace) { + ATRACE_NAME("dump_backtrace"); dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, process_name, threads, 0); } else { + ATRACE_NAME("engrave_tombstone"); engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid, process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr); }
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp index 6970201..f57349b 100644 --- a/debuggerd/crasher/crasher.cpp +++ b/debuggerd/crasher/crasher.cpp
@@ -134,6 +134,14 @@ free(buf); // GCC is smart enough to warn about this, but we're doing it deliberately. } +noinline void leak() { + while (true) { + void* mapping = + mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + static_cast<volatile char*>(mapping)[0] = 'a'; + } +} + noinline void sigsegv_non_null() { int* a = (int *)(&do_action); *a = 42; @@ -160,8 +168,8 @@ fprintf(stderr, " stack-overflow recurse until the stack overflows\n"); fprintf(stderr, " nostack crash with a NULL stack pointer\n"); fprintf(stderr, "\n"); - fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n"); fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n"); + fprintf(stderr, " leak leak memory until we get OOM-killed\n"); fprintf(stderr, "\n"); fprintf(stderr, " abort call abort()\n"); fprintf(stderr, " assert call assert() without a function\n"); @@ -265,6 +273,8 @@ return pthread_join(0, nullptr); } else if (!strcasecmp(arg, "heap-usage")) { abuse_heap(); + } else if (!strcasecmp(arg, "leak")) { + leak(); } else if (!strcasecmp(arg, "SIGSEGV-unmapped")) { char* map = reinterpret_cast<char*>(mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0));
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp index 6298ace..b016e23 100644 --- a/debuggerd/debuggerd.cpp +++ b/debuggerd/debuggerd.cpp
@@ -27,7 +27,7 @@ #include <android-base/parseint.h> #include <android-base/unique_fd.h> #include <debuggerd/client.h> -#include <selinux/selinux.h> +#include <procinfo/process.h> #include "util.h" using android::base::unique_fd; @@ -66,6 +66,24 @@ usage(1); } + if (getuid() != 0) { + errx(1, "root is required"); + } + + // Check to see if the process exists and that we can actually send a signal to it. + android::procinfo::ProcessInfo proc_info; + if (!android::procinfo::GetProcessInfo(pid, &proc_info)) { + err(1, "failed to fetch info for process %d", pid); + } + + if (proc_info.state == android::procinfo::kProcessStateZombie) { + errx(1, "process %d is a zombie", pid); + } + + if (kill(pid, 0) != 0) { + err(1, "cannot send signal to process %d", pid); + } + unique_fd piperead, pipewrite; if (!Pipe(&piperead, &pipewrite)) { err(1, "failed to create pipe");
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp index 2015157..b51fc66 100644 --- a/debuggerd/debuggerd_test.cpp +++ b/debuggerd/debuggerd_test.cpp
@@ -88,8 +88,12 @@ } \ } while (0) +#define ASSERT_BACKTRACE_FRAME(result, frame_name) \ + ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX \ + R"(/libc.so \()" frame_name R"(\+)") + static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd, - DebuggerdDumpType intercept_type) { + InterceptStatus* status, DebuggerdDumpType intercept_type) { intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET)); if (intercept_fd->get() == -1) { @@ -136,7 +140,7 @@ << ", received " << rc; } - ASSERT_EQ(InterceptStatus::kRegistered, response.status); + *status = response.status; } class CrasherTest : public ::testing::Test { @@ -180,7 +184,9 @@ FAIL() << "crasher hasn't been started"; } - tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, intercept_type); + InterceptStatus status; + tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &status, intercept_type); + ASSERT_EQ(InterceptStatus::kRegistered, status); } void CrasherTest::FinishIntercept(int* result) { @@ -305,7 +311,7 @@ std::string result; ConsumeFd(std::move(output_fd), &result); - ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(abort)"); + ASSERT_BACKTRACE_FRAME(result, "abort"); } TEST_F(CrasherTest, signal) { @@ -441,7 +447,7 @@ FinishIntercept(&intercept_result); ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; ConsumeFd(std::move(output_fd), &result); - ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)"); + ASSERT_BACKTRACE_FRAME(result, "read"); int status; ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED)); @@ -452,7 +458,7 @@ FinishIntercept(&intercept_result); ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; ConsumeFd(std::move(output_fd), &result); - ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(abort)"); + ASSERT_BACKTRACE_FRAME(result, "abort"); } TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) { @@ -472,7 +478,7 @@ std::string result; ConsumeFd(std::move(output_fd), &result); - ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(abort)"); + ASSERT_BACKTRACE_FRAME(result, "abort"); } TEST_F(CrasherTest, capabilities) { @@ -529,7 +535,7 @@ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; ConsumeFd(std::move(output_fd), &result); ASSERT_MATCH(result, R"(name: thread_name\s+>>> .+debuggerd_test(32|64) <<<)"); - ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)"); + ASSERT_BACKTRACE_FRAME(result, "tgkill"); } TEST_F(CrasherTest, fake_pid) { @@ -560,7 +566,7 @@ std::string result; ConsumeFd(std::move(output_fd), &result); - ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)"); + ASSERT_BACKTRACE_FRAME(result, "tgkill"); } TEST(crash_dump, zombie) { @@ -598,7 +604,9 @@ pid_t pid = 123'456'789 + i; unique_fd intercept_fd, output_fd; - tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone); + InterceptStatus status; + tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone); + ASSERT_EQ(InterceptStatus::kRegistered, status); { unique_fd tombstoned_socket, input_fd; @@ -630,7 +638,9 @@ pid_t pid = pid_base + dump; unique_fd intercept_fd, output_fd; - tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone); + InterceptStatus status; + tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone); + ASSERT_EQ(InterceptStatus::kRegistered, status); // Pretend to crash, and then immediately close the socket. unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName, @@ -661,7 +671,9 @@ pid_t pid = pid_base + dump; unique_fd intercept_fd, output_fd; - tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone); + InterceptStatus status; + tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone); + ASSERT_EQ(InterceptStatus::kRegistered, status); { unique_fd tombstoned_socket, input_fd; @@ -685,3 +697,65 @@ thread.join(); } } + +TEST(tombstoned, java_trace_intercept_smoke) { + // Using a "real" PID is a little dangerous here - if the test fails + // or crashes, we might end up getting a bogus / unreliable stack + // trace. + const pid_t self = getpid(); + + unique_fd intercept_fd, output_fd; + InterceptStatus status; + tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace); + ASSERT_EQ(InterceptStatus::kRegistered, status); + + // First connect to tombstoned requesting a native backtrace. This + // should result in a "regular" FD and not the installed intercept. + const char native[] = "native"; + unique_fd tombstoned_socket, input_fd; + ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdNativeBacktrace)); + ASSERT_TRUE(android::base::WriteFully(input_fd.get(), native, sizeof(native))); + tombstoned_notify_completion(tombstoned_socket.get()); + + // Then, connect to tombstoned asking for a java backtrace. This *should* + // trigger the intercept. + const char java[] = "java"; + ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdJavaBacktrace)); + ASSERT_TRUE(android::base::WriteFully(input_fd.get(), java, sizeof(java))); + tombstoned_notify_completion(tombstoned_socket.get()); + + char outbuf[sizeof(java)]; + ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf))); + ASSERT_STREQ("java", outbuf); +} + +TEST(tombstoned, multiple_intercepts) { + const pid_t fake_pid = 1'234'567; + unique_fd intercept_fd, output_fd; + InterceptStatus status; + tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace); + ASSERT_EQ(InterceptStatus::kRegistered, status); + + unique_fd intercept_fd_2, output_fd_2; + tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &status, kDebuggerdNativeBacktrace); + ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, status); +} + +TEST(tombstoned, intercept_any) { + const pid_t fake_pid = 1'234'567; + + unique_fd intercept_fd, output_fd; + InterceptStatus status; + tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdNativeBacktrace); + ASSERT_EQ(InterceptStatus::kRegistered, status); + + const char any[] = "any"; + unique_fd tombstoned_socket, input_fd; + ASSERT_TRUE(tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdAnyIntercept)); + ASSERT_TRUE(android::base::WriteFully(input_fd.get(), any, sizeof(any))); + tombstoned_notify_completion(tombstoned_socket.get()); + + char outbuf[sizeof(any)]; + ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf))); + ASSERT_STREQ("any", outbuf); +}
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp index 325210d..6be59e7 100644 --- a/debuggerd/libdebuggerd/test/tombstone_test.cpp +++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -220,21 +220,21 @@ map.start = 0xa434000; map.end = 0xa435000; map.offset = 0x1000; - map.load_base = 0xd000; + map.load_bias = 0xd000; map.flags = PROT_WRITE; map_mock_->AddMap(map); map.start = 0xa534000; map.end = 0xa535000; map.offset = 0x3000; - map.load_base = 0x2000; + map.load_bias = 0x2000; map.flags = PROT_EXEC; map_mock_->AddMap(map); map.start = 0xa634000; map.end = 0xa635000; map.offset = 0; - map.load_base = 0; + map.load_bias = 0; map.flags = PROT_READ | PROT_WRITE | PROT_EXEC; map.name = "/system/lib/fake.so"; map_mock_->AddMap(map); @@ -244,20 +244,20 @@ std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - const char* expected_dump = \ -"\nmemory map:\n" + const char* expected_dump = + "\nmemory map:\n" #if defined(__LP64__) -" 00000000'0a234000-00000000'0a234fff --- 0 1000\n" -" 00000000'0a334000-00000000'0a334fff r-- f000 1000\n" -" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n" -" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n" -" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; + " 00000000'0a234000-00000000'0a234fff --- 0 1000\n" + " 00000000'0a334000-00000000'0a334fff r-- f000 1000\n" + " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" + " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" + " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; #else -" 0a234000-0a234fff --- 0 1000\n" -" 0a334000-0a334fff r-- f000 1000\n" -" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n" -" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n" -" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; + " 0a234000-0a234fff --- 0 1000\n" + " 0a334000-0a334fff r-- f000 1000\n" + " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" + " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" + " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); @@ -274,21 +274,21 @@ map.start = 0xa434000; map.end = 0xa435000; map.offset = 0x1000; - map.load_base = 0xd000; + map.load_bias = 0xd000; map.flags = PROT_WRITE; map_mock_->AddMap(map); map.start = 0xa534000; map.end = 0xa535000; map.offset = 0x3000; - map.load_base = 0x2000; + map.load_bias = 0x2000; map.flags = PROT_EXEC; map_mock_->AddMap(map); map.start = 0xa634000; map.end = 0xa635000; map.offset = 0; - map.load_base = 0; + map.load_bias = 0; map.flags = PROT_READ | PROT_WRITE | PROT_EXEC; map.name = "/system/lib/fake.so"; map_mock_->AddMap(map); @@ -304,18 +304,18 @@ std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - const char* expected_dump = \ -"\nmemory map: (fault address prefixed with --->)\n" + const char* expected_dump = + "\nmemory map: (fault address prefixed with --->)\n" #if defined(__LP64__) -"--->Fault address falls at 00000000'00001000 before any mapped regions\n" -" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n" -" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n" -" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; + "--->Fault address falls at 00000000'00001000 before any mapped regions\n" + " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" + " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" + " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; #else -"--->Fault address falls at 00001000 before any mapped regions\n" -" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n" -" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n" -" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; + "--->Fault address falls at 00001000 before any mapped regions\n" + " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" + " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" + " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); @@ -332,21 +332,21 @@ map.start = 0xa434000; map.end = 0xa435000; map.offset = 0x1000; - map.load_base = 0xd000; + map.load_bias = 0xd000; map.flags = PROT_WRITE; map_mock_->AddMap(map); map.start = 0xa534000; map.end = 0xa535000; map.offset = 0x3000; - map.load_base = 0x2000; + map.load_bias = 0x2000; map.flags = PROT_EXEC; map_mock_->AddMap(map); map.start = 0xa634000; map.end = 0xa635000; map.offset = 0; - map.load_base = 0; + map.load_bias = 0; map.flags = PROT_READ | PROT_WRITE | PROT_EXEC; map.name = "/system/lib/fake.so"; map_mock_->AddMap(map); @@ -362,18 +362,18 @@ std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - const char* expected_dump = \ -"\nmemory map: (fault address prefixed with --->)\n" + const char* expected_dump = + "\nmemory map: (fault address prefixed with --->)\n" #if defined(__LP64__) -" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n" -"--->Fault address falls at 00000000'0a533000 between mapped regions\n" -" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n" -" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; + " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" + "--->Fault address falls at 00000000'0a533000 between mapped regions\n" + " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" + " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; #else -" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n" -"--->Fault address falls at 0a533000 between mapped regions\n" -" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n" -" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; + " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" + "--->Fault address falls at 0a533000 between mapped regions\n" + " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" + " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); @@ -390,21 +390,21 @@ map.start = 0xa434000; map.end = 0xa435000; map.offset = 0x1000; - map.load_base = 0xd000; + map.load_bias = 0xd000; map.flags = PROT_WRITE; map_mock_->AddMap(map); map.start = 0xa534000; map.end = 0xa535000; map.offset = 0x3000; - map.load_base = 0x2000; + map.load_bias = 0x2000; map.flags = PROT_EXEC; map_mock_->AddMap(map); map.start = 0xa634000; map.end = 0xa635000; map.offset = 0; - map.load_base = 0; + map.load_bias = 0; map.flags = PROT_READ | PROT_WRITE | PROT_EXEC; map.name = "/system/lib/fake.so"; map_mock_->AddMap(map); @@ -420,16 +420,16 @@ std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - const char* expected_dump = \ -"\nmemory map: (fault address prefixed with --->)\n" + const char* expected_dump = + "\nmemory map: (fault address prefixed with --->)\n" #if defined(__LP64__) -" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n" -"--->00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n" -" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; + " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" + "--->00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" + " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; #else -" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n" -"--->0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n" -" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; + " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" + "--->0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" + " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); @@ -446,21 +446,21 @@ map.start = 0xa434000; map.end = 0xa435000; map.offset = 0x1000; - map.load_base = 0xd000; + map.load_bias = 0xd000; map.flags = PROT_WRITE; map_mock_->AddMap(map); map.start = 0xa534000; map.end = 0xa535000; map.offset = 0x3000; - map.load_base = 0x2000; + map.load_bias = 0x2000; map.flags = PROT_EXEC; map_mock_->AddMap(map); map.start = 0xa634000; map.end = 0xa635000; map.offset = 0; - map.load_base = 0; + map.load_bias = 0; map.flags = PROT_READ | PROT_WRITE | PROT_EXEC; map.name = "/system/lib/fake.so"; map_mock_->AddMap(map); @@ -480,18 +480,18 @@ std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - const char* expected_dump = \ -"\nmemory map: (fault address prefixed with --->)\n" + const char* expected_dump = + "\nmemory map: (fault address prefixed with --->)\n" #if defined(__LP64__) -" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n" -" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load base 0x2000)\n" -" 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n" -"--->Fault address falls at 00001234'5a534040 after any mapped regions\n"; + " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" + " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" + " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n" + "--->Fault address falls at 00001234'5a534040 after any mapped regions\n"; #else -" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n" -" 0a534000-0a534fff --x 3000 1000 (load base 0x2000)\n" -" 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n" -"--->Fault address falls at 0f534040 after any mapped regions\n"; + " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" + " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" + " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n" + "--->Fault address falls at 0f534040 after any mapped regions\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); @@ -508,7 +508,7 @@ map.start = 0xa434000; map.end = 0xa435000; map.offset = 0x1000; - map.load_base = 0xd000; + map.load_bias = 0xd000; map.flags = PROT_WRITE; map_mock_->AddMap(map); @@ -520,12 +520,12 @@ std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - const char* expected_dump = \ -"\nmemory map:\n" + const char* expected_dump = + "\nmemory map:\n" #if defined(__LP64__) -" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n"; + " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"; #else -" 0a434000-0a434fff -w- 1000 1000 (load base 0xd000)\n"; + " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp index edc7be5..b9a0bc7 100644 --- a/debuggerd/libdebuggerd/tombstone.cpp +++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -168,6 +168,26 @@ case TRAP_BRANCH: return "TRAP_BRANCH"; case TRAP_HWBKPT: return "TRAP_HWBKPT"; } + if ((code & 0xff) == SIGTRAP) { + switch ((code >> 8) & 0xff) { + case PTRACE_EVENT_FORK: + return "PTRACE_EVENT_FORK"; + case PTRACE_EVENT_VFORK: + return "PTRACE_EVENT_VFORK"; + case PTRACE_EVENT_CLONE: + return "PTRACE_EVENT_CLONE"; + case PTRACE_EVENT_EXEC: + return "PTRACE_EVENT_EXEC"; + case PTRACE_EVENT_VFORK_DONE: + return "PTRACE_EVENT_VFORK_DONE"; + case PTRACE_EVENT_EXIT: + return "PTRACE_EVENT_EXIT"; + case PTRACE_EVENT_SECCOMP: + return "PTRACE_EVENT_SECCOMP"; + case PTRACE_EVENT_STOP: + return "PTRACE_EVENT_STOP"; + } + } static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code"); break; } @@ -395,15 +415,17 @@ } ScopedBacktraceMapIteratorLock lock(map); - _LOG(log, logtype::MAPS, "\n"); - if (!print_fault_address_marker) { - _LOG(log, logtype::MAPS, "memory map:\n"); - } else { - _LOG(log, logtype::MAPS, "memory map: (fault address prefixed with --->)\n"); + _LOG(log, logtype::MAPS, + "\n" + "memory map (%zu entries):\n", + map->size()); + if (print_fault_address_marker) { if (map->begin() != map->end() && addr < map->begin()->start) { _LOG(log, logtype::MAPS, "--->Fault address falls at %s before any mapped regions\n", get_addr_string(addr).c_str()); print_fault_address_marker = false; + } else { + _LOG(log, logtype::MAPS, "(fault address prefixed with --->)\n"); } } @@ -446,11 +468,11 @@ line += " (BuildId: " + build_id + ")"; } } - if (it->load_base != 0) { + if (it->load_bias != 0) { if (space_needed) { line += ' '; } - line += StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base); + line += StringPrintf(" (load bias 0x%" PRIxPTR ")", it->load_bias); } _LOG(log, logtype::MAPS, "%s\n", line.c_str()); } @@ -763,10 +785,22 @@ dump_abort_message(backtrace.get(), &log, abort_msg_address); dump_registers(&log, ucontext); - // TODO: Dump registers from the ucontext. if (backtrace->Unwind(0, ucontext)) { dump_backtrace_and_stack(backtrace.get(), &log); } else { ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid); } + + // TODO: Make this match the format of dump_all_maps above. + _LOG(&log, logtype::MAPS, "memory map:\n"); + android::base::unique_fd maps_fd(open("/proc/self/maps", O_RDONLY | O_CLOEXEC)); + if (maps_fd == -1) { + _LOG(&log, logtype::MAPS, " failed to open /proc/self/maps: %s", strerror(errno)); + } else { + char buf[256]; + ssize_t rc; + while ((rc = TEMP_FAILURE_RETRY(read(maps_fd.get(), buf, sizeof(buf)))) > 0) { + android::base::WriteFully(tombstone_fd, buf, rc); + } + } }
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp index 09cff45..93c7fb5 100644 --- a/debuggerd/tombstoned/tombstoned.cpp +++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -103,7 +103,7 @@ } static CrashQueue* for_anrs() { - static CrashQueue queue("/data/anr", "anr_" /* file_name_prefix */, + static CrashQueue queue("/data/anr", "trace_" /* file_name_prefix */, GetIntProperty("tombstoned.max_anr_count", 64), 4 /* max_concurrent_dumps */); return &queue; @@ -194,7 +194,7 @@ }; // Whether java trace dumps are produced via tombstoned. -static constexpr bool kJavaTraceDumpsEnabled = false; +static constexpr bool kJavaTraceDumpsEnabled = true; // Forward declare the callbacks so they can be placed in a sensible order. static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, void*); @@ -343,7 +343,14 @@ } if (!crash->crash_path.empty()) { - LOG(ERROR) << "Tombstone written to: " << crash->crash_path; + if (crash->crash_type == kDebuggerdJavaBacktrace) { + LOG(ERROR) << "Traces for pid " << crash->crash_pid << " written to: " << crash->crash_path; + } else { + // NOTE: Several tools parse this log message to figure out where the + // tombstone associated with a given native crash was written. Any changes + // to this message must be carefully considered. + LOG(ERROR) << "Tombstone written to: " << crash->crash_path; + } } fail:
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc index b8345ca..53ef01c 100644 --- a/debuggerd/tombstoned/tombstoned.rc +++ b/debuggerd/tombstoned/tombstoned.rc
@@ -7,4 +7,5 @@ socket tombstoned_crash seqpacket 0666 system system socket tombstoned_intercept seqpacket 0666 system system + socket tombstoned_java_trace seqpacket 0666 system system writepid /dev/cpuset/system-background/tasks
diff --git a/demangle/Android.bp b/demangle/Android.bp index ce617a7..e55c886 100644 --- a/demangle/Android.bp +++ b/demangle/Android.bp
@@ -28,10 +28,8 @@ cc_library { name: "libdemangle", - - vendor_available: true, - defaults: ["libdemangle_defaults"], + vendor_available: true, srcs: [ "Demangler.cpp",
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index 271ca95..5f2267c 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp
@@ -93,6 +93,9 @@ static unsigned second_offset = 0x00f00000; static unsigned tags_offset = 0x00000100; +static bool g_disable_verity = false; +static bool g_disable_verification = false; + static const std::string convert_fbe_marker_filename("convert_fbe"); enum fb_buffer_type { @@ -419,6 +422,10 @@ " --skip-reboot Will not reboot the device when\n" " performing commands that normally\n" " trigger a reboot.\n" + " --disable-verity Set the disable-verity flag in the\n" + " the vbmeta image being flashed.\n" + " --disable-verification Set the disable-verification flag in" + " the vbmeta image being flashed.\n" #if !defined(_WIN32) " --wipe-and-use-fbe On devices which support it,\n" " erase userdata and cache, and\n" @@ -858,10 +865,55 @@ return load_buf_fd(transport, fd.release(), buf); } +static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf) { + // Buffer needs to be at least the size of the VBMeta struct which + // is 256 bytes. + if (buf->sz < 256) { + return; + } + + int fd = make_temporary_fd(); + if (fd == -1) { + die("Failed to create temporary file for vbmeta rewriting"); + } + + std::string data; + if (!android::base::ReadFdToString(buf->fd, &data)) { + die("Failed reading from vbmeta"); + } + + // There's a 32-bit big endian |flags| field at offset 120 where + // bit 0 corresponds to disable-verity and bit 1 corresponds to + // disable-verification. + // + // See external/avb/libavb/avb_vbmeta_image.h for the layout of + // the VBMeta struct. + if (g_disable_verity) { + data[123] |= 0x01; + } + if (g_disable_verification) { + data[123] |= 0x02; + } + + if (!android::base::WriteStringToFd(data, fd)) { + die("Failed writing to modified vbmeta"); + } + close(buf->fd); + buf->fd = fd; + lseek(fd, 0, SEEK_SET); +} + static void flash_buf(const char *pname, struct fastboot_buffer *buf) { sparse_file** s; + // Rewrite vbmeta if that's what we're flashing and modification has been requested. + if ((g_disable_verity || g_disable_verification) && + (strcmp(pname, "vbmeta") == 0 || strcmp(pname, "vbmeta_a") == 0 || + strcmp(pname, "vbmeta_b") == 0)) { + rewrite_vbmeta_buffer(buf); + } + switch (buf->type) { case FB_BUFFER_SPARSE: { std::vector<std::pair<sparse_file*, int64_t>> sparse_files; @@ -1411,7 +1463,7 @@ if (fs_generator_generate(gen, output.path, size, initial_dir, eraseBlkSize, logicalBlkSize)) { - fprintf(stderr, "Cannot generate image: %s\n", strerror(errno)); + die("Cannot generate image for %s\n", partition); return; } @@ -1470,6 +1522,8 @@ {"set-active", optional_argument, 0, 'a'}, {"skip-secondary", no_argument, 0, 0}, {"skip-reboot", no_argument, 0, 0}, + {"disable-verity", no_argument, 0, 0}, + {"disable-verification", no_argument, 0, 0}, #if !defined(_WIN32) {"wipe-and-use-fbe", no_argument, 0, 0}, #endif @@ -1555,6 +1609,10 @@ skip_secondary = true; } else if (strcmp("skip-reboot", longopts[longindex].name) == 0 ) { skip_reboot = true; + } else if (strcmp("disable-verity", longopts[longindex].name) == 0 ) { + g_disable_verity = true; + } else if (strcmp("disable-verification", longopts[longindex].name) == 0 ) { + g_disable_verification = true; #if !defined(_WIN32) } else if (strcmp("wipe-and-use-fbe", longopts[longindex].name) == 0) { wants_wipe = true;
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp index 4a4a7c0..709f061 100644 --- a/fastboot/fs.cpp +++ b/fastboot/fs.cpp
@@ -124,6 +124,8 @@ } mke2fs_args.push_back("-E"); mke2fs_args.push_back(ext_attr.c_str()); + mke2fs_args.push_back("-O"); + mke2fs_args.push_back("uninit_bg"); mke2fs_args.push_back(fileName); std::string size_str = std::to_string(partSize / block_size);
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp index 4441ad0..7fd67c2 100644 --- a/fs_mgr/Android.bp +++ b/fs_mgr/Android.bp
@@ -21,20 +21,6 @@ }, local_include_dirs: ["include/"], cppflags: ["-Werror"], - static_libs: [ - "libfec", - "libfec_rs", - "libbase", - "libcrypto_utils", - "libcrypto", - "libext4_utils", - "libsquashfs_utils", - "libselinux", - "libavb", - ], - whole_static_libs: [ - "liblogwrap", - ], } cc_library_static { @@ -46,12 +32,28 @@ "fs_mgr.cpp", "fs_mgr_dm_ioctl.cpp", "fs_mgr_format.cpp", - "fs_mgr_fstab.cpp", - "fs_mgr_slotselect.cpp", "fs_mgr_verity.cpp", "fs_mgr_avb.cpp", "fs_mgr_avb_ops.cpp", - "fs_mgr_boot_config.cpp", + ], + static_libs: [ + "libfec", + "libfec_rs", + "libbase", + "libcrypto_utils", + "libcrypto", + "libext4_utils", + "libsquashfs_utils", + "libselinux", + "libavb", + "libfstab", + ], + export_static_lib_headers: [ + "libfstab", + ], + whole_static_libs: [ + "liblogwrap", + "libfstab", ], product_variables: { debuggable: { @@ -62,3 +64,16 @@ }, }, } + +cc_library_static { + name: "libfstab", + vendor_available: true, + defaults: ["fs_mgr_defaults"], + srcs: [ + "fs_mgr_fstab.cpp", + "fs_mgr_boot_config.cpp", + "fs_mgr_slotselect.cpp", + ], + export_include_dirs: ["include_fstab"], + header_libs: ["libbase_headers"], +}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp index e009383..91ed496 100644 --- a/fs_mgr/fs_mgr.cpp +++ b/fs_mgr/fs_mgr.cpp
@@ -32,6 +32,7 @@ #include <unistd.h> #include <memory> +#include <thread> #include <android-base/file.h> #include <android-base/properties.h> @@ -78,43 +79,33 @@ FS_STAT_E2FSCK_F_ALWAYS = 0x0004, FS_STAT_UNCLEAN_SHUTDOWN = 0x0008, FS_STAT_QUOTA_ENABLED = 0x0010, - FS_STAT_TUNE2FS_FAILED = 0x0020, FS_STAT_RO_MOUNT_FAILED = 0x0040, FS_STAT_RO_UNMOUNT_FAILED = 0x0080, FS_STAT_FULL_MOUNT_FAILED = 0x0100, FS_STAT_E2FSCK_FAILED = 0x0200, FS_STAT_E2FSCK_FS_FIXED = 0x0400, FS_STAT_EXT4_INVALID_MAGIC = 0x0800, + FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000, + FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000, + FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000, }; -/* - * gettime() - returns the time in seconds of the system's monotonic clock or - * zero on error. - */ -static time_t gettime(void) -{ - struct timespec ts; - int ret; +// TODO: switch to inotify() +bool fs_mgr_wait_for_file(const std::string& filename, + const std::chrono::milliseconds relative_timeout) { + auto start_time = std::chrono::steady_clock::now(); - ret = clock_gettime(CLOCK_MONOTONIC, &ts); - if (ret < 0) { - PERROR << "clock_gettime(CLOCK_MONOTONIC) failed"; - return 0; + while (true) { + if (!access(filename.c_str(), F_OK) || errno != ENOENT) { + return true; + } + + std::this_thread::sleep_for(50ms); + + auto now = std::chrono::steady_clock::now(); + auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time); + if (time_elapsed > relative_timeout) return false; } - - return ts.tv_sec; -} - -static int wait_for_file(const char *filename, int timeout) -{ - struct stat info; - time_t timeout_time = gettime() + timeout; - int ret = -1; - - while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0)) - usleep(10000); - - return ret; } static void log_fs_stat(const char* blk_device, int fs_stat) @@ -128,10 +119,16 @@ } } +static bool is_extfs(const std::string& fs_type) { + return fs_type == "ext4" || fs_type == "ext3" || fs_type == "ext2"; +} + static bool should_force_check(int fs_stat) { - return fs_stat & (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED | - FS_STAT_TUNE2FS_FAILED | FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED | - FS_STAT_FULL_MOUNT_FAILED | FS_STAT_E2FSCK_FAILED); + return fs_stat & + (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED | + FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED | FS_STAT_FULL_MOUNT_FAILED | + FS_STAT_E2FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED | + FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED); } static void check_fs(const char *blk_device, char *fs_type, char *target, int *fs_stat) @@ -144,7 +141,7 @@ const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device}; /* Check for the types of filesystems we know how to check */ - if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) { + if (is_extfs(fs_type)) { if (*fs_stat & FS_STAT_EXT4_INVALID_MAGIC) { // will fail, so do not try return; } @@ -242,186 +239,208 @@ return; } -/* Function to read the primary superblock */ -static int read_super_block(int fd, struct ext4_super_block *sb) -{ - off64_t ret; - - ret = lseek64(fd, 1024, SEEK_SET); - if (ret < 0) - return ret; - - ret = read(fd, sb, sizeof(*sb)); - if (ret < 0) - return ret; - if (ret != sizeof(*sb)) - return ret; - - return 0; -} - -static ext4_fsblk_t ext4_blocks_count(struct ext4_super_block *es) -{ +static ext4_fsblk_t ext4_blocks_count(const struct ext4_super_block* es) { return ((ext4_fsblk_t)le32_to_cpu(es->s_blocks_count_hi) << 32) | - le32_to_cpu(es->s_blocks_count_lo); + le32_to_cpu(es->s_blocks_count_lo); } -static ext4_fsblk_t ext4_r_blocks_count(struct ext4_super_block *es) -{ +static ext4_fsblk_t ext4_r_blocks_count(const struct ext4_super_block* es) { return ((ext4_fsblk_t)le32_to_cpu(es->s_r_blocks_count_hi) << 32) | - le32_to_cpu(es->s_r_blocks_count_lo); + le32_to_cpu(es->s_r_blocks_count_lo); } -static int do_quota_with_shutdown_check(char *blk_device, char *fs_type, - struct fstab_rec *rec, int *fs_stat) -{ - int force_check = 0; - if (!strcmp(fs_type, "ext4")) { - /* - * Some system images do not have tune2fs for licensing reasons - * Detect these and skip reserve blocks. - */ - if (access(TUNE2FS_BIN, X_OK)) { - LERROR << "Not running " << TUNE2FS_BIN << " on " - << blk_device << " (executable not in system image)"; - } else { - const char* arg1 = nullptr; - const char* arg2 = nullptr; - int status = 0; - int ret = 0; - android::base::unique_fd fd( - TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC))); - if (fd >= 0) { - struct ext4_super_block sb; - ret = read_super_block(fd, &sb); - if (ret < 0) { - PERROR << "Can't read '" << blk_device << "' super block"; - return force_check; - } - if (sb.s_magic != EXT4_SUPER_MAGIC) { - LINFO << "Invalid ext4 magic:0x" << std::hex << sb.s_magic << "," << blk_device; - *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC; - return 0; // not a valid fs, tune2fs, fsck, and mount will all fail. - } - *fs_stat |= FS_STAT_IS_EXT4; - LINFO << "superblock s_max_mnt_count:" << sb.s_max_mnt_count << "," << blk_device; - if (sb.s_max_mnt_count == 0xffff) { // -1 (int16) in ext2, but uint16 in ext4 - *fs_stat |= FS_STAT_NEW_IMAGE_VERSION; - } - if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 || - (sb.s_state & EXT4_VALID_FS) == 0) { - LINFO << __FUNCTION__ << "(): was not clealy shutdown, state flag:" - << std::hex << sb.s_state - << "incompat flag:" << std::hex << sb.s_feature_incompat; - force_check = 1; - *fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN; - } - int has_quota = (sb.s_feature_ro_compat - & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0; - int want_quota = fs_mgr_is_quota(rec) != 0; +// Read the primary superblock from an ext4 filesystem. On failure return +// false. If it's not an ext4 filesystem, also set FS_STAT_EXT4_INVALID_MAGIC. +static bool read_ext4_superblock(const char* blk_device, struct ext4_super_block* sb, int* fs_stat) { + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC))); - if (has_quota == want_quota) { - LINFO << "Requested quota status is match on " << blk_device; - return force_check; - } else if (want_quota) { - LINFO << "Enabling quota on " << blk_device; - arg1 = "-Oquota"; - arg2 = "-Qusrquota,grpquota"; - force_check = 1; - *fs_stat |= FS_STAT_QUOTA_ENABLED; - } else { - LINFO << "Disabling quota on " << blk_device; - arg1 = "-Q^usrquota,^grpquota"; - arg2 = "-O^quota"; - } - } else { - PERROR << "Failed to open '" << blk_device << "'"; - return force_check; - } - - const char *tune2fs_argv[] = { - TUNE2FS_BIN, - arg1, - arg2, - blk_device, - }; - ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv), - const_cast<char **>(tune2fs_argv), - &status, true, LOG_KLOG | LOG_FILE, - true, NULL, NULL, 0); - if (ret < 0) { - /* No need to check for error in fork, we can't really handle it now */ - LERROR << "Failed trying to run " << TUNE2FS_BIN; - *fs_stat |= FS_STAT_TUNE2FS_FAILED; - } - } + if (fd < 0) { + PERROR << "Failed to open '" << blk_device << "'"; + return false; } - return force_check; + + if (pread(fd, sb, sizeof(*sb), 1024) != sizeof(*sb)) { + PERROR << "Can't read '" << blk_device << "' superblock"; + return false; + } + + if (sb->s_magic != EXT4_SUPER_MAGIC) { + LINFO << "Invalid ext4 magic:0x" << std::hex << sb->s_magic << " " + << "on '" << blk_device << "'"; + // not a valid fs, tune2fs, fsck, and mount will all fail. + *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC; + return false; + } + *fs_stat |= FS_STAT_IS_EXT4; + LINFO << "superblock s_max_mnt_count:" << sb->s_max_mnt_count << "," << blk_device; + if (sb->s_max_mnt_count == 0xffff) { // -1 (int16) in ext2, but uint16 in ext4 + *fs_stat |= FS_STAT_NEW_IMAGE_VERSION; + } + return true; } -static void do_reserved_size(char *blk_device, char *fs_type, struct fstab_rec *rec, int *fs_stat) -{ - /* Check for the types of filesystems we know how to check */ - if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) { - /* - * Some system images do not have tune2fs for licensing reasons - * Detect these and skip reserve blocks. - */ - if (access(TUNE2FS_BIN, X_OK)) { - LERROR << "Not running " << TUNE2FS_BIN << " on " - << blk_device << " (executable not in system image)"; +// Some system images do not have tune2fs for licensing reasons. +// Detect these and skip running it. +static bool tune2fs_available(void) { + return access(TUNE2FS_BIN, X_OK) == 0; +} + +static bool run_tune2fs(const char* argv[], int argc) { + int ret; + + ret = android_fork_execvp_ext(argc, const_cast<char**>(argv), nullptr, true, + LOG_KLOG | LOG_FILE, true, nullptr, nullptr, 0); + return ret == 0; +} + +// Enable/disable quota support on the filesystem if needed. +static void tune_quota(const char* blk_device, const struct fstab_rec* rec, + const struct ext4_super_block* sb, int* fs_stat) { + bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0; + bool want_quota = fs_mgr_is_quota(rec) != 0; + + if (has_quota == want_quota) { + return; + } + + if (!tune2fs_available()) { + LERROR << "Unable to " << (want_quota ? "enable" : "disable") << " quotas on " << blk_device + << " because " TUNE2FS_BIN " is missing"; + return; + } + + const char* argv[] = {TUNE2FS_BIN, nullptr, nullptr, blk_device}; + + if (want_quota) { + LINFO << "Enabling quotas on " << blk_device; + argv[1] = "-Oquota"; + argv[2] = "-Qusrquota,grpquota"; + *fs_stat |= FS_STAT_QUOTA_ENABLED; + } else { + LINFO << "Disabling quotas on " << blk_device; + argv[1] = "-O^quota"; + argv[2] = "-Q^usrquota,^grpquota"; + } + + if (!run_tune2fs(argv, ARRAY_SIZE(argv))) { + LERROR << "Failed to run " TUNE2FS_BIN " to " << (want_quota ? "enable" : "disable") + << " quotas on " << blk_device; + *fs_stat |= FS_STAT_TOGGLE_QUOTAS_FAILED; + } +} + +// Set the number of reserved filesystem blocks if needed. +static void tune_reserved_size(const char* blk_device, const struct fstab_rec* rec, + const struct ext4_super_block* sb, int* fs_stat) { + if (!(rec->fs_mgr_flags & MF_RESERVEDSIZE)) { + return; + } + + // The size to reserve is given in the fstab, but we won't reserve more + // than 2% of the filesystem. + const uint64_t max_reserved_blocks = ext4_blocks_count(sb) * 0.02; + uint64_t reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(sb); + + if (reserved_blocks > max_reserved_blocks) { + LWARNING << "Reserved blocks " << reserved_blocks << " is too large; " + << "capping to " << max_reserved_blocks; + reserved_blocks = max_reserved_blocks; + } + + if (ext4_r_blocks_count(sb) == reserved_blocks) { + return; + } + + if (!tune2fs_available()) { + LERROR << "Unable to set the number of reserved blocks on " << blk_device + << " because " TUNE2FS_BIN " is missing"; + return; + } + + char buf[32]; + const char* argv[] = {TUNE2FS_BIN, "-r", buf, blk_device}; + + snprintf(buf, sizeof(buf), "%" PRIu64, reserved_blocks); + LINFO << "Setting reserved block count on " << blk_device << " to " << reserved_blocks; + if (!run_tune2fs(argv, ARRAY_SIZE(argv))) { + LERROR << "Failed to run " TUNE2FS_BIN " to set the number of reserved blocks on " + << blk_device; + *fs_stat |= FS_STAT_SET_RESERVED_BLOCKS_FAILED; + } +} + +// Enable file-based encryption if needed. +static void tune_encrypt(const char* blk_device, const struct fstab_rec* rec, + const struct ext4_super_block* sb, int* fs_stat) { + bool has_encrypt = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) != 0; + bool want_encrypt = fs_mgr_is_file_encrypted(rec) != 0; + + if (has_encrypt || !want_encrypt) { + return; + } + + if (!tune2fs_available()) { + LERROR << "Unable to enable ext4 encryption on " << blk_device + << " because " TUNE2FS_BIN " is missing"; + return; + } + + const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device}; + + LINFO << "Enabling ext4 encryption on " << blk_device; + if (!run_tune2fs(argv, ARRAY_SIZE(argv))) { + LERROR << "Failed to run " TUNE2FS_BIN " to enable " + << "ext4 encryption on " << blk_device; + *fs_stat |= FS_STAT_ENABLE_ENCRYPTION_FAILED; + } +} + +// +// Prepare the filesystem on the given block device to be mounted. +// +// If the "check" option was given in the fstab record, or it seems that the +// filesystem was uncleanly shut down, we'll run fsck on the filesystem. +// +// If needed, we'll also enable (or disable) filesystem features as specified by +// the fstab record. +// +static int prepare_fs_for_mount(const char* blk_device, const struct fstab_rec* rec) { + int fs_stat = 0; + + if (is_extfs(rec->fs_type)) { + struct ext4_super_block sb; + + if (read_ext4_superblock(blk_device, &sb, &fs_stat)) { + if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 || + (sb.s_state & EXT4_VALID_FS) == 0) { + LINFO << "Filesystem on " << blk_device << " was not cleanly shutdown; " + << "state flags: 0x" << std::hex << sb.s_state << ", " + << "incompat feature flags: 0x" << std::hex << sb.s_feature_incompat; + fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN; + } + + // Note: quotas should be enabled before running fsck. + tune_quota(blk_device, rec, &sb, &fs_stat); } else { - LINFO << "Running " << TUNE2FS_BIN << " on " << blk_device; - - int status = 0; - int ret = 0; - unsigned long reserved_blocks = 0; - android::base::unique_fd fd( - TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC))); - if (fd >= 0) { - struct ext4_super_block sb; - ret = read_super_block(fd, &sb); - if (ret < 0) { - PERROR << "Can't read '" << blk_device << "' super block"; - return; - } - reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(&sb); - unsigned long reserved_threshold = ext4_blocks_count(&sb) * 0.02; - if (reserved_threshold < reserved_blocks) { - LWARNING << "Reserved blocks " << reserved_blocks - << " is too large"; - reserved_blocks = reserved_threshold; - } - - if (ext4_r_blocks_count(&sb) == reserved_blocks) { - LINFO << "Have reserved same blocks"; - return; - } - } else { - PERROR << "Failed to open '" << blk_device << "'"; - return; - } - - char buf[16] = {0}; - snprintf(buf, sizeof (buf), "-r %lu", reserved_blocks); - const char *tune2fs_argv[] = { - TUNE2FS_BIN, - buf, - blk_device, - }; - - ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv), - const_cast<char **>(tune2fs_argv), - &status, true, LOG_KLOG | LOG_FILE, - true, NULL, NULL, 0); - - if (ret < 0) { - /* No need to check for error in fork, we can't really handle it now */ - LERROR << "Failed trying to run " << TUNE2FS_BIN; - *fs_stat |= FS_STAT_TUNE2FS_FAILED; - } + return fs_stat; } } + + if ((rec->fs_mgr_flags & MF_CHECK) || + (fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) { + check_fs(blk_device, rec->fs_type, rec->mount_point, &fs_stat); + } + + if (is_extfs(rec->fs_type) && (rec->fs_mgr_flags & (MF_RESERVEDSIZE | MF_FILEENCRYPTION))) { + struct ext4_super_block sb; + + if (read_ext4_superblock(blk_device, &sb, &fs_stat)) { + tune_reserved_size(blk_device, rec, &sb, &fs_stat); + tune_encrypt(blk_device, rec, &sb, &fs_stat); + } + } + + return fs_stat; } static void remove_trailing_slashes(char *n) @@ -457,6 +476,16 @@ return rc; } +// Orange state means the device is unlocked, see the following link for details. +// https://source.android.com/security/verifiedboot/verified-boot#device_state +bool fs_mgr_is_device_unlocked() { + std::string verified_boot_state; + if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) { + return verified_boot_state == "orange"; + } + return false; +} + /* * __mount(): wrapper around the mount() system call which also * sets the underlying block device to read-only if the mount is read-only. @@ -476,10 +505,11 @@ if ((info.st_mode & S_IFMT) == S_IFLNK) unlink(target); mkdir(target, 0755); + errno = 0; ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options); save_errno = errno; - LINFO << __FUNCTION__ << "(source=" << source << ",target=" - << target << ",type=" << rec->fs_type << ")=" << ret; + PINFO << __FUNCTION__ << "(source=" << source << ",target=" << target + << ",type=" << rec->fs_type << ")=" << ret; if ((ret == 0) && (mountflags & MS_RDONLY) != 0) { fs_mgr_set_blk_ro(source); } @@ -559,10 +589,7 @@ continue; } - int fs_stat = 0; - int force_check = do_quota_with_shutdown_check(fstab->recs[i].blk_device, - fstab->recs[i].fs_type, - &fstab->recs[i], &fs_stat); + int fs_stat = prepare_fs_for_mount(fstab->recs[i].blk_device, &fstab->recs[i]); if (fs_stat & FS_STAT_EXT4_INVALID_MAGIC) { LERROR << __FUNCTION__ << "(): skipping mount, invalid ext4, mountpoint=" << fstab->recs[i].mount_point << " rec[" << i @@ -570,15 +597,6 @@ mount_errno = EINVAL; // continue bootup for FDE continue; } - if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) { - check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type, - fstab->recs[i].mount_point, &fs_stat); - } - - if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) { - do_reserved_size(fstab->recs[i].blk_device, fstab->recs[i].fs_type, - &fstab->recs[i], &fs_stat); - } int retry_count = 2; while (retry_count-- > 0) { @@ -756,19 +774,6 @@ } } -// TODO: add ueventd notifiers if they don't exist. -// This is just doing a wait_for_device for maximum of 1s -int fs_mgr_test_access(const char *device) { - int tries = 25; - while (tries--) { - if (!access(device, F_OK) || errno != ENOENT) { - return 0; - } - usleep(40 * 1000); - } - return -1; -} - bool is_device_secure() { int ret = -1; char value[PROP_VALUE_MAX]; @@ -829,9 +834,7 @@ } /* Translate LABEL= file system labels into block devices */ - if (!strcmp(fstab->recs[i].fs_type, "ext2") || - !strcmp(fstab->recs[i].fs_type, "ext3") || - !strcmp(fstab->recs[i].fs_type, "ext4")) { + if (is_extfs(fstab->recs[i].fs_type)) { int tret = translate_ext_labels(&fstab->recs[i]); if (tret < 0) { LERROR << "Could not translate label to block device"; @@ -839,8 +842,10 @@ } } - if (fstab->recs[i].fs_mgr_flags & MF_WAIT) { - wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT); + if (fstab->recs[i].fs_mgr_flags & MF_WAIT && + !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) { + LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all"; + continue; } if (fstab->recs[i].fs_mgr_flags & MF_AVB) { @@ -1047,22 +1052,12 @@ } /* First check the filesystem if requested */ - if (fstab->recs[i].fs_mgr_flags & MF_WAIT) { - wait_for_file(n_blk_device, WAIT_TIMEOUT); + if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) { + LERROR << "Skipping mounting '" << n_blk_device << "'"; + continue; } - int fs_stat = 0; - int force_check = do_quota_with_shutdown_check(n_blk_device, fstab->recs[i].fs_type, - &fstab->recs[i], &fs_stat); - - if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) { - check_fs(n_blk_device, fstab->recs[i].fs_type, - fstab->recs[i].mount_point, &fs_stat); - } - - if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) { - do_reserved_size(n_blk_device, fstab->recs[i].fs_type, &fstab->recs[i], &fs_stat); - } + int fs_stat = prepare_fs_for_mount(n_blk_device, &fstab->recs[i]); if (fstab->recs[i].fs_mgr_flags & MF_AVB) { if (!avb_handle) { @@ -1221,8 +1216,11 @@ fclose(zram_fp); } - if (fstab->recs[i].fs_mgr_flags & MF_WAIT) { - wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT); + if (fstab->recs[i].fs_mgr_flags & MF_WAIT && + !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) { + LERROR << "Skipping mkswap for '" << fstab->recs[i].blk_device << "'"; + ret = -1; + continue; } /* Initialize the swap area */
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp index 2c99aa7..7824cfa 100644 --- a/fs_mgr/fs_mgr_avb.cpp +++ b/fs_mgr/fs_mgr_avb.cpp
@@ -397,7 +397,7 @@ fstab_entry->blk_device = strdup(verity_blk_name.c_str()); // Makes sure we've set everything up properly. - if (wait_for_verity_dev && fs_mgr_test_access(verity_blk_name.c_str()) < 0) { + if (wait_for_verity_dev && !fs_mgr_wait_for_file(verity_blk_name, 1s)) { return false; } @@ -473,16 +473,6 @@ return true; } -// Orange state means the device is unlocked, see the following link for details. -// https://source.android.com/security/verifiedboot/verified-boot#device_state -static inline bool IsDeviceUnlocked() { - std::string verified_boot_state; - if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) { - return verified_boot_state == "orange"; - } - return false; -} - FsManagerAvbUniquePtr FsManagerAvbHandle::Open(const fstab& fstab) { FsManagerAvbOps avb_ops(fstab); return DoOpen(&avb_ops); @@ -498,7 +488,7 @@ } FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) { - bool is_device_unlocked = IsDeviceUnlocked(); + bool is_device_unlocked = fs_mgr_is_device_unlocked(); FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle()); if (!avb_handle) {
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp index ba1262f..43879fe 100644 --- a/fs_mgr/fs_mgr_avb_ops.cpp +++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -142,10 +142,8 @@ } std::string path = iter->second; - // Ensures the device path (a symlink created by init) is ready to - // access. fs_mgr_test_access() will test a few iterations if the - // path doesn't exist yet. - if (fs_mgr_test_access(path.c_str()) < 0) { + // Ensures the device path (a symlink created by init) is ready to access. + if (!fs_mgr_wait_for_file(path, 1s)) { return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; }
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp index ab5beed..9c5d3f3 100644 --- a/fs_mgr/fs_mgr_boot_config.cpp +++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -14,6 +14,8 @@ * limitations under the License. */ +#include <string> + #include <android-base/file.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> @@ -21,19 +23,11 @@ #include "fs_mgr_priv.h" -// Tries to get the boot config value in properties, kernel cmdline and -// device tree (in that order). returns 'true' if successfully found, 'false' -// otherwise -bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) { +// Tries to get the given boot config value from kernel cmdline. +// Returns true if successfully found, false otherwise. +bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) { FS_MGR_CHECK(out_val != nullptr); - // first check if we have "ro.boot" property already - *out_val = android::base::GetProperty("ro.boot." + key, ""); - if (!out_val->empty()) { - return true; - } - - // fallback to kernel cmdline, properties may not be ready yet std::string cmdline; std::string cmdline_key("androidboot." + key); if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) { @@ -48,12 +42,34 @@ } } + return false; +} + +// Tries to get the boot config value in properties, kernel cmdline and +// device tree (in that order). returns 'true' if successfully found, 'false' +// otherwise +bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) { + FS_MGR_CHECK(out_val != nullptr); + + // first check if we have "ro.boot" property already + *out_val = android::base::GetProperty("ro.boot." + key, ""); + if (!out_val->empty()) { + return true; + } + + // fallback to kernel cmdline, properties may not be ready yet + if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) { + return true; + } + // lastly, check the device tree if (is_dt_compatible()) { - std::string file_name = kAndroidDtDir + "/" + key; - // DT entries terminate with '\0' but so do the properties + std::string file_name = get_android_dt_dir() + "/" + key; if (android::base::ReadFileToString(file_name, out_val)) { - return true; + if (!out_val->empty()) { + out_val->pop_back(); // Trims the trailing '\0' out. + return true; + } } }
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp index 75feee7..fc88217 100644 --- a/fs_mgr/fs_mgr_format.cpp +++ b/fs_mgr/fs_mgr_format.cpp
@@ -38,7 +38,6 @@ { uint64_t dev_sz; int fd, rc = 0; - int status; if ((fd = open(fs_blkdev, O_WRONLY)) < 0) { PERROR << "Cannot open block device"; @@ -62,7 +61,7 @@ const char* const mke2fs_args[] = { "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", fs_blkdev, size_str.c_str(), nullptr}; - rc = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), &status, + rc = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), NULL, true, LOG_KLOG, true, nullptr, nullptr, 0); if (rc) { LERROR << "mke2fs returned " << rc; @@ -78,7 +77,7 @@ nullptr}; rc = android_fork_execvp_ext(arraysize(e2fsdroid_args), const_cast<char**>(e2fsdroid_args), - &status, true, LOG_KLOG, true, nullptr, nullptr, 0); + NULL, true, LOG_KLOG, true, nullptr, nullptr, 0); if (rc) { LERROR << "e2fsdroid returned " << rc; } @@ -88,10 +87,9 @@ static int format_f2fs(char *fs_blkdev) { - int status; const char* const args[] = {"/system/bin/make_f2fs", "-f", "-O encrypt", fs_blkdev, nullptr}; - return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), &status, true, + return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), NULL, true, LOG_KLOG, true, nullptr, nullptr, 0); }
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp index 6c527c5..eeac697 100644 --- a/fs_mgr/fs_mgr_fstab.cpp +++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -29,6 +29,8 @@ #include "fs_mgr_priv.h" +const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android"); + struct fs_mgr_flag_values { char *key_loc; char* key_dir; @@ -365,9 +367,26 @@ return f; } +static std::string init_android_dt_dir() { + std::string android_dt_dir; + // The platform may specify a custom Android DT path in kernel cmdline + if (!fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) { + // Fall back to the standard procfs-based path + android_dt_dir = kDefaultAndroidDtDir; + } + return android_dt_dir; +} + +// FIXME: The same logic is duplicated in system/core/init/ +const std::string& get_android_dt_dir() { + // Set once and saves time for subsequent calls to this function + static const std::string kAndroidDtDir = init_android_dt_dir(); + return kAndroidDtDir; +} + static bool is_dt_fstab_compatible() { std::string dt_value; - std::string file_name = kAndroidDtDir + "/fstab/compatible"; + std::string file_name = get_android_dt_dir() + "/fstab/compatible"; if (read_dt_file(file_name, &dt_value)) { if (dt_value == "android,fstab") { return true; @@ -383,7 +402,7 @@ return fstab; } - std::string fstabdir_name = kAndroidDtDir + "/fstab"; + std::string fstabdir_name = get_android_dt_dir() + "/fstab"; std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir); if (!fstabdir) return fstab; @@ -446,7 +465,7 @@ } bool is_dt_compatible() { - std::string file_name = kAndroidDtDir + "/compatible"; + std::string file_name = get_android_dt_dir() + "/compatible"; std::string dt_value; if (read_dt_file(file_name, &dt_value)) { if (dt_value == "android,firmware") { @@ -864,32 +883,26 @@ return fstab->fs_mgr_flags & MF_NOEMULATEDSD; } -int fs_mgr_is_notrim(struct fstab_rec *fstab) -{ +int fs_mgr_is_notrim(const struct fstab_rec* fstab) { return fstab->fs_mgr_flags & MF_NOTRIM; } -int fs_mgr_is_formattable(struct fstab_rec *fstab) -{ +int fs_mgr_is_formattable(const struct fstab_rec* fstab) { return fstab->fs_mgr_flags & (MF_FORMATTABLE); } -int fs_mgr_is_slotselect(struct fstab_rec *fstab) -{ +int fs_mgr_is_slotselect(const struct fstab_rec* fstab) { return fstab->fs_mgr_flags & MF_SLOTSELECT; } -int fs_mgr_is_nofail(struct fstab_rec *fstab) -{ +int fs_mgr_is_nofail(const struct fstab_rec* fstab) { return fstab->fs_mgr_flags & MF_NOFAIL; } -int fs_mgr_is_latemount(struct fstab_rec *fstab) -{ +int fs_mgr_is_latemount(const struct fstab_rec* fstab) { return fstab->fs_mgr_flags & MF_LATEMOUNT; } -int fs_mgr_is_quota(struct fstab_rec *fstab) -{ +int fs_mgr_is_quota(const struct fstab_rec* fstab) { return fstab->fs_mgr_flags & MF_QUOTA; }
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h index 3ca507b..0f62e18 100644 --- a/fs_mgr/fs_mgr_priv.h +++ b/fs_mgr/fs_mgr_priv.h
@@ -17,8 +17,12 @@ #ifndef __CORE_FS_MGR_PRIV_H #define __CORE_FS_MGR_PRIV_H +#include <chrono> +#include <string> + #include <android-base/logging.h> -#include <fs_mgr.h> + +#include "fs_mgr.h" #include "fs_mgr_priv_boot_config.h" /* The CHECK() in logging.h will use program invocation name as the tag. @@ -43,8 +47,6 @@ #define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000" -#define WAIT_TIMEOUT 20 - /* fstab has the following format: * * Any line starting with a # is a comment and ignored @@ -111,9 +113,14 @@ #define DM_BUF_SIZE 4096 +using namespace std::chrono_literals; + int fs_mgr_set_blk_ro(const char *blockdev); -int fs_mgr_test_access(const char *device); +bool fs_mgr_wait_for_file(const std::string& filename, + const std::chrono::milliseconds relative_timeout); bool fs_mgr_update_for_slotselect(struct fstab *fstab); +bool fs_mgr_is_device_unlocked(); +const std::string& get_android_dt_dir(); bool is_dt_compatible(); bool is_device_secure(); int load_verity_state(struct fstab_rec* fstab, int* mode);
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h index 8773d33..d98dc02 100644 --- a/fs_mgr/fs_mgr_priv_boot_config.h +++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -20,8 +20,7 @@ #include <sys/cdefs.h> #include <string> -const std::string kAndroidDtDir("/proc/device-tree/firmware/android"); - +bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val); bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val); #endif /* __CORE_FS_MGR_PRIV_BOOTCONFIG_H */
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp index 8904995..7f8e1e2 100644 --- a/fs_mgr/fs_mgr_verity.cpp +++ b/fs_mgr/fs_mgr_verity.cpp
@@ -348,10 +348,13 @@ static int was_verity_restart() { - static const char *files[] = { + static const char* files[] = { + // clang-format off + "/sys/fs/pstore/console-ramoops-0", "/sys/fs/pstore/console-ramoops", "/proc/last_kmsg", NULL + // clang-format on }; int i; @@ -689,27 +692,55 @@ return read_verity_state(fstab->verity_loc, offset, mode); } -static void update_verity_table_blk_device(char *blk_device, char **table) -{ - std::string result, word; +// Update the verity table using the actual block device path. +// Two cases: +// Case-1: verity table is shared for devices with different by-name prefix. +// Example: +// verity table token: /dev/block/bootdevice/by-name/vendor +// blk_device-1 (non-A/B): /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor +// blk_device-2 (A/B): /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor_a +// +// Case-2: append A/B suffix in the verity table. +// Example: +// verity table token: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor +// blk_device: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor_a +static void update_verity_table_blk_device(const std::string& blk_device, char** table, + bool slot_select) { + bool updated = false; + std::string result, ab_suffix; auto tokens = android::base::Split(*table, " "); + // If slot_select is set, it means blk_device is already updated with ab_suffix. + if (slot_select) ab_suffix = fs_mgr_get_slot_suffix(); + for (const auto& token : tokens) { - if (android::base::StartsWith(token, "/dev/block/") && - android::base::StartsWith(blk_device, token.c_str())) { - word = blk_device; + std::string new_token; + if (android::base::StartsWith(token, "/dev/block/")) { + if (token == blk_device) return; // no need to update if they're already the same. + std::size_t found1 = blk_device.find("by-name"); + std::size_t found2 = token.find("by-name"); + if (found1 != std::string::npos && found2 != std::string::npos && + blk_device.substr(found1) == token.substr(found2) + ab_suffix) { + new_token = blk_device; + } + } + + if (!new_token.empty()) { + updated = true; + LINFO << "Verity table: updated block device from '" << token << "' to '" << new_token + << "'"; } else { - word = token; + new_token = token; } if (result.empty()) { - result = word; + result = new_token; } else { - result += " " + word; + result += " " + new_token; } } - if (result.empty()) { + if (!updated) { return; } @@ -751,8 +782,8 @@ if (fec_verity_get_metadata(f, &verity) < 0) { PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'"; // Allow verity disabled when the device is unlocked without metadata - if ("0" == android::base::GetProperty("ro.boot.flash.locked", "")) { - retval = FS_MGR_SETUP_VERITY_DISABLED; + if (fs_mgr_is_device_unlocked()) { + retval = FS_MGR_SETUP_VERITY_SKIPPED; LWARNING << "Allow invalid metadata when the device is unlocked"; } goto out; @@ -810,9 +841,15 @@ // verify the signature on the table if (verify_verity_signature(verity) < 0) { + // Allow signature verification error when the device is unlocked + if (fs_mgr_is_device_unlocked()) { + retval = FS_MGR_SETUP_VERITY_SKIPPED; + LWARNING << "Allow signature verification error when the device is unlocked"; + goto out; + } if (params.mode == VERITY_MODE_LOGGING) { // the user has been warned, allow mounting without dm-verity - retval = FS_MGR_SETUP_VERITY_SUCCESS; + retval = FS_MGR_SETUP_VERITY_SKIPPED; goto out; } @@ -825,10 +862,9 @@ LINFO << "Enabling dm-verity for " << mount_point.c_str() << " (mode " << params.mode << ")"; - if (fstab->fs_mgr_flags & MF_SLOTSELECT) { - // Update the verity params using the actual block device path - update_verity_table_blk_device(fstab->blk_device, ¶ms.table); - } + // Update the verity params using the actual block device path + update_verity_table_blk_device(fstab->blk_device, ¶ms.table, + fstab->fs_mgr_flags & MF_SLOTSELECT); // load the verity mapping table if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms, @@ -899,7 +935,7 @@ } // make sure we've set everything up properly - if (wait_for_verity_dev && fs_mgr_test_access(fstab->blk_device) < 0) { + if (wait_for_verity_dev && !fs_mgr_wait_for_file(fstab->blk_device, 1s)) { goto out; }
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index e033d47..c74f6c8 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h
@@ -22,11 +22,7 @@ #include <stdbool.h> #include <linux/dm-ioctl.h> -// C++ only headers -// TODO: move this into separate header files under include/fs_mgr/*.h -#ifdef __cplusplus -#include <string> -#endif +#include <fstab/fstab.h> // Magic number at start of verity metadata #define VERITY_METADATA_MAGIC_NUMBER 0xb001b001 @@ -53,49 +49,10 @@ MOUNT_MODE_LATE = 2 }; -/* - * The entries must be kept in the same order as they were seen in the fstab. - * Unless explicitly requested, a lookup on mount point should always - * return the 1st one. - */ -struct fstab { - int num_entries; - struct fstab_rec *recs; - char *fstab_filename; -}; - -struct fstab_rec { - char *blk_device; - char *mount_point; - char *fs_type; - unsigned long flags; - char *fs_options; - int fs_mgr_flags; - char *key_loc; - char* key_dir; - char *verity_loc; - long long length; - char *label; - int partnum; - int swap_prio; - int max_comp_streams; - unsigned int zram_size; - uint64_t reserved_size; - unsigned int file_contents_mode; - unsigned int file_names_mode; - unsigned int erase_blk_size; - unsigned int logical_blk_size; -}; - // Callback function for verity status typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab, const char *mount_point, int mode, int status); -struct fstab *fs_mgr_read_fstab_default(); -struct fstab *fs_mgr_read_fstab_dt(); -struct fstab *fs_mgr_read_fstab(const char *fstab_path); -void fs_mgr_free_fstab(struct fstab *fstab); - #define FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED 7 #define FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION 6 #define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5 @@ -120,28 +77,6 @@ void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size); bool fs_mgr_load_verity_state(int* mode); bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback); -int fs_mgr_add_entry(struct fstab *fstab, - const char *mount_point, const char *fs_type, - const char *blk_device); -struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path); -int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab); -int fs_mgr_is_nonremovable(const struct fstab_rec *fstab); -int fs_mgr_is_verified(const struct fstab_rec *fstab); -int fs_mgr_is_verifyatboot(const struct fstab_rec *fstab); -int fs_mgr_is_avb(const struct fstab_rec *fstab); -int fs_mgr_is_encryptable(const struct fstab_rec *fstab); -int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab); -void fs_mgr_get_file_encryption_modes(const struct fstab_rec *fstab, - const char **contents_mode_ret, - const char **filenames_mode_ret); -int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab); -int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab); -int fs_mgr_is_notrim(struct fstab_rec *fstab); -int fs_mgr_is_formattable(struct fstab_rec *fstab); -int fs_mgr_is_slotselect(struct fstab_rec *fstab); -int fs_mgr_is_nofail(struct fstab_rec *fstab); -int fs_mgr_is_latemount(struct fstab_rec *fstab); -int fs_mgr_is_quota(struct fstab_rec *fstab); int fs_mgr_swapon_all(struct fstab *fstab); int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer); @@ -154,10 +89,4 @@ __END_DECLS -// C++ only functions -// TODO: move this into separate header files under include/fs_mgr/*.h -#ifdef __cplusplus -std::string fs_mgr_get_slot_suffix(); -#endif - #endif /* __CORE_FS_MGR_H */
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h new file mode 100644 index 0000000..15c8caf --- /dev/null +++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -0,0 +1,101 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CORE_FS_TAB_H +#define __CORE_FS_TAB_H + +#include <linux/dm-ioctl.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> + +// C++ only headers +// TODO: move this into separate header files under include/fs_mgr/*.h +#ifdef __cplusplus +#include <string> +#endif + +__BEGIN_DECLS + +/* + * The entries must be kept in the same order as they were seen in the fstab. + * Unless explicitly requested, a lookup on mount point should always + * return the 1st one. + */ +struct fstab { + int num_entries; + struct fstab_rec* recs; + char* fstab_filename; +}; + +struct fstab_rec { + char* blk_device; + char* mount_point; + char* fs_type; + unsigned long flags; + char* fs_options; + int fs_mgr_flags; + char* key_loc; + char* key_dir; + char* verity_loc; + long long length; + char* label; + int partnum; + int swap_prio; + int max_comp_streams; + unsigned int zram_size; + uint64_t reserved_size; + unsigned int file_contents_mode; + unsigned int file_names_mode; + unsigned int erase_blk_size; + unsigned int logical_blk_size; +}; + +struct fstab* fs_mgr_read_fstab_default(); +struct fstab* fs_mgr_read_fstab_dt(); +struct fstab* fs_mgr_read_fstab(const char* fstab_path); +void fs_mgr_free_fstab(struct fstab* fstab); + +int fs_mgr_add_entry(struct fstab* fstab, const char* mount_point, const char* fs_type, + const char* blk_device); +struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const char* path); +int fs_mgr_is_voldmanaged(const struct fstab_rec* fstab); +int fs_mgr_is_nonremovable(const struct fstab_rec* fstab); +int fs_mgr_is_verified(const struct fstab_rec* fstab); +int fs_mgr_is_verifyatboot(const struct fstab_rec* fstab); +int fs_mgr_is_avb(const struct fstab_rec* fstab); +int fs_mgr_is_encryptable(const struct fstab_rec* fstab); +int fs_mgr_is_file_encrypted(const struct fstab_rec* fstab); +void fs_mgr_get_file_encryption_modes(const struct fstab_rec* fstab, const char** contents_mode_ret, + const char** filenames_mode_ret); +int fs_mgr_is_convertible_to_fbe(const struct fstab_rec* fstab); +int fs_mgr_is_noemulatedsd(const struct fstab_rec* fstab); +int fs_mgr_is_notrim(const struct fstab_rec* fstab); +int fs_mgr_is_formattable(const struct fstab_rec* fstab); +int fs_mgr_is_slotselect(const struct fstab_rec* fstab); +int fs_mgr_is_nofail(const struct fstab_rec* fstab); +int fs_mgr_is_latemount(const struct fstab_rec* fstab); +int fs_mgr_is_quota(const struct fstab_rec* fstab); + +__END_DECLS + +// C++ only functions +// TODO: move this into separate header files under include/fs_mgr/*.h +#ifdef __cplusplus +std::string fs_mgr_get_slot_suffix(); +#endif + +#endif /* __CORE_FS_TAB_H */
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk index 0dfd9d8..28f0b07 100644 --- a/gatekeeperd/Android.mk +++ b/gatekeeperd/Android.mk
@@ -21,8 +21,7 @@ LOCAL_SRC_FILES := \ SoftGateKeeperDevice.cpp \ IGateKeeperService.cpp \ - gatekeeperd.cpp \ - IUserManager.cpp + gatekeeperd.cpp LOCAL_MODULE := gatekeeperd LOCAL_SHARED_LIBRARIES := \
diff --git a/gatekeeperd/IUserManager.cpp b/gatekeeperd/IUserManager.cpp deleted file mode 100644 index 8167d19..0000000 --- a/gatekeeperd/IUserManager.cpp +++ /dev/null
@@ -1,57 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "IUserManager" -#include <stdint.h> -#include <sys/types.h> -#include <utils/Log.h> -#include <binder/Parcel.h> - -#include "IUserManager.h" - -namespace android { - -class BpUserManager : public BpInterface<IUserManager> -{ -public: - explicit BpUserManager(const sp<IBinder>& impl) : - BpInterface<IUserManager>(impl) { - } - virtual int32_t getCredentialOwnerProfile(int32_t user_id) { - Parcel data, reply; - data.writeInterfaceToken(IUserManager::getInterfaceDescriptor()); - data.writeInt32(user_id); - status_t rc = remote()->transact(GET_CREDENTIAL_OWNER_PROFILE, data, &reply, 0); - if (rc != NO_ERROR) { - ALOGE("%s: failed (%d)\n", __func__, rc); - return -1; - } - - int32_t exception = reply.readExceptionCode(); - if (exception != 0) { - ALOGE("%s: got exception (%d)\n", __func__, exception); - return -1; - } - - return reply.readInt32(); - } - -}; - -IMPLEMENT_META_INTERFACE(UserManager, "android.os.IUserManager"); - -}; // namespace android -
diff --git a/gatekeeperd/IUserManager.h b/gatekeeperd/IUserManager.h deleted file mode 100644 index 640e9b5..0000000 --- a/gatekeeperd/IUserManager.h +++ /dev/null
@@ -1,46 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef IUSERMANAGER_H_ -#define IUSERMANAGER_H_ - -#include <inttypes.h> -#include <utils/Errors.h> -#include <binder/IInterface.h> -#include <binder/Parcel.h> -#include <utils/Vector.h> - -namespace android { - -/* -* Communication channel to UserManager -*/ -class IUserManager : public IInterface { - public: - // must be kept in sync with IUserManager.aidl - enum { - GET_CREDENTIAL_OWNER_PROFILE = IBinder::FIRST_CALL_TRANSACTION + 0, - }; - - virtual int32_t getCredentialOwnerProfile(int32_t user_id) = 0; - - DECLARE_META_INTERFACE(UserManager); -}; - -}; // namespace android - -#endif // IUSERMANAGER_H_ -
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h index cb02a6f..2f4f4d7 100644 --- a/gatekeeperd/SoftGateKeeper.h +++ b/gatekeeperd/SoftGateKeeper.h
@@ -26,11 +26,11 @@ } #include <android-base/memory.h> -#include <UniquePtr.h> #include <gatekeeper/gatekeeper.h> #include <iostream> #include <unordered_map> +#include <memory> namespace gatekeeper { @@ -173,7 +173,7 @@ typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap; typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap; - UniquePtr<uint8_t[]> key_; + std::unique_ptr<uint8_t[]> key_; FailureRecordMap failure_map_; FastHashMap fast_hash_map_; };
diff --git a/gatekeeperd/SoftGateKeeperDevice.h b/gatekeeperd/SoftGateKeeperDevice.h index 3463c29..e3dc068 100644 --- a/gatekeeperd/SoftGateKeeperDevice.h +++ b/gatekeeperd/SoftGateKeeperDevice.h
@@ -19,7 +19,7 @@ #include "SoftGateKeeper.h" -#include <UniquePtr.h> +#include <memory> using namespace gatekeeper; @@ -68,7 +68,7 @@ const uint8_t *provided_password, uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll); private: - UniquePtr<SoftGateKeeper> impl_; + std::unique_ptr<SoftGateKeeper> impl_; }; } // namespace gatekeeper
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp index c6369f9..61c8804 100644 --- a/gatekeeperd/gatekeeperd.cpp +++ b/gatekeeperd/gatekeeperd.cpp
@@ -23,6 +23,7 @@ #include <inttypes.h> #include <stdint.h> #include <unistd.h> +#include <memory> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> @@ -37,7 +38,6 @@ #include <utils/String16.h> #include "SoftGateKeeperDevice.h" -#include "IUserManager.h" #include <hidl/HidlSupport.h> #include <android/hardware/gatekeeper/1.0/IGatekeeper.h> @@ -334,23 +334,7 @@ return ret; } - virtual uint64_t getSecureUserId(uint32_t uid) { - uint64_t sid = read_sid(uid); - if (sid == 0) { - // might be a work profile, look up the parent - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> binder = sm->getService(String16("user")); - sp<IUserManager> um = interface_cast<IUserManager>(binder); - int32_t parent = um->getCredentialOwnerProfile(uid); - if (parent < 0) { - return 0; - } else if (parent != (int32_t) uid) { - return read_sid(parent); - } - } - return sid; - - } + virtual uint64_t getSecureUserId(uint32_t uid) { return read_sid(uid); } virtual void clearSecureUserId(uint32_t uid) { IPCThreadState* ipc = IPCThreadState::self(); @@ -400,7 +384,7 @@ private: sp<IGatekeeper> hw_device; - UniquePtr<SoftGateKeeperDevice> soft_device; + std::unique_ptr<SoftGateKeeperDevice> soft_device; bool clear_state_if_needed_done; };
diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp index 47a8bfa..100375f 100644 --- a/gatekeeperd/tests/gatekeeper_test.cpp +++ b/gatekeeperd/tests/gatekeeper_test.cpp
@@ -19,7 +19,6 @@ #include <gtest/gtest.h> #include <hardware/hw_auth_token.h> -#include <UniquePtr.h> #include "../SoftGateKeeper.h"
diff --git a/healthd/Android.bp b/healthd/Android.bp new file mode 100644 index 0000000..56f5148 --- /dev/null +++ b/healthd/Android.bp
@@ -0,0 +1,7 @@ +cc_library_headers { + name: "libhealthd_headers", + vendor_available: true, + export_include_dirs: ["include"], + header_libs: ["libbatteryservice_headers"], + export_header_lib_headers: ["libbatteryservice_headers"], +}
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp index d26530b..676ee41 100644 --- a/healthd/BatteryMonitor.cpp +++ b/healthd/BatteryMonitor.cpp
@@ -116,6 +116,10 @@ { "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE }, { "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE }, { "Cold", BATTERY_HEALTH_COLD }, + // battery health values from JEITA spec + { "Warm", BATTERY_HEALTH_GOOD }, + { "Cool", BATTERY_HEALTH_GOOD }, + { "Hot", BATTERY_HEALTH_OVERHEAT }, { NULL, 0 }, };
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp index e7f3eaf..6c6d738 100644 --- a/healthd/healthd_mode_charger.cpp +++ b/healthd/healthd_mode_charger.cpp
@@ -32,6 +32,7 @@ #include <functional> #include <android-base/file.h> +#include <android-base/macros.h> #include <linux/netlink.h> #include <sys/socket.h> @@ -72,8 +73,6 @@ #define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC) #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC) -#define LAST_KMSG_PATH "/proc/last_kmsg" -#define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops" #define LAST_KMSG_MAX_SZ (32 * 1024) #define LOGE(x...) KLOG_ERROR("charger", x); @@ -205,14 +204,21 @@ LOGW("\n"); LOGW("*************** LAST KMSG ***************\n"); LOGW("\n"); - buf = (char*)load_file(LAST_KMSG_PSTORE_PATH, &sz); + const char* kmsg[] = { + // clang-format off + "/sys/fs/pstore/console-ramoops-0", + "/sys/fs/pstore/console-ramoops", + "/proc/last_kmsg", + // clang-format on + }; + for (size_t i = 0; i < arraysize(kmsg); ++i) { + buf = (char*)load_file(kmsg[i], &sz); + if (buf && sz) break; + } if (!buf || !sz) { - buf = (char*)load_file(LAST_KMSG_PATH, &sz); - if (!buf || !sz) { - LOGW("last_kmsg not found. Cold reset?\n"); - goto out; - } + LOGW("last_kmsg not found. Cold reset?\n"); + goto out; } len = min(sz, LAST_KMSG_MAX_SZ);
diff --git a/include/private/fs_config.h b/include/private/fs_config.h new file mode 100644 index 0000000..e9868a4 --- /dev/null +++ b/include/private/fs_config.h
@@ -0,0 +1,4 @@ +// TODO(b/63135587) remove this file after the transitive dependency +// from private/android_filesystem_config.h is resolved. All files that use +// libcutils/include/private/fs_config.h should include the file directly, not +// indirectly via private/android_filesystem_config.h.
diff --git a/init/Android.bp b/init/Android.bp index 5d6d979..aaef7e9 100644 --- a/init/Android.bp +++ b/init/Android.bp
@@ -18,9 +18,8 @@ name: "init_defaults", cpp_std: "experimental", sanitize: { - misc_undefined: ["integer"], + misc_undefined: ["signed-integer-overflow"], }, - tidy_checks: ["-misc-forwarding-reference-overload"], cppflags: [ "-DLOG_UEVENTS=0", "-Wall", @@ -158,6 +157,7 @@ "init_test.cpp", "property_service_test.cpp", "service_test.cpp", + "ueventd_test.cpp", "util_test.cpp", ], shared_libs: [
diff --git a/init/Android.mk b/init/Android.mk index bf75f5a..161256e 100644 --- a/init/Android.mk +++ b/init/Android.mk
@@ -28,6 +28,10 @@ init_options += -DLOG_UEVENTS=0 +ifeq ($(TARGET_USER_MODE_LINUX), true) + init_cflags += -DUSER_MODE_LINUX +endif + init_cflags += \ $(init_options) \ -Wall -Wextra \ @@ -38,8 +42,6 @@ # -- include $(CLEAR_VARS) -# b/38002385, work around clang-tidy segmentation fault. -LOCAL_TIDY_CHECKS := -misc-forwarding-reference-overload LOCAL_CPPFLAGS := $(init_cflags) LOCAL_SRC_FILES:= \ bootchart.cpp \ @@ -94,6 +96,6 @@ ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \ ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd -LOCAL_SANITIZE := integer +LOCAL_SANITIZE := signed-integer-overflow LOCAL_CLANG := true include $(BUILD_EXECUTABLE)
diff --git a/init/README.md b/init/README.md index 422fdad..f3b57bc 100644 --- a/init/README.md +++ b/init/README.md
@@ -260,6 +260,18 @@ > Sets the child's /proc/self/oom\_score\_adj to the specified value, which must range from -1000 to 1000. +`memcg.swappiness <value>` +> Sets the child's memory.swappiness to the specified value (only if memcg is mounted), + which must be equal or greater than 0. + +`memcg.soft_limit_in_bytes <value>` +> Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted), + which must be equal or greater than 0. + +`memcg.limit_in_bytes <value>` +> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted), + which must be equal or greater than 0. + `shutdown <shutdown_behavior>` > Set shutdown behavior of the service process. When this is not specified, the service is killed during shutdown process by using SIGTERM and SIGKILL.
diff --git a/init/action.cpp b/init/action.cpp index 41bd061..4ec5f17 100644 --- a/init/action.cpp +++ b/init/action.cpp
@@ -16,15 +16,17 @@ #include "action.h" +#include <android-base/chrono_utils.h> #include <android-base/logging.h> #include <android-base/properties.h> -#include <android-base/stringprintf.h> #include <android-base/strings.h> #include "util.h" using android::base::Join; -using android::base::StringPrintf; + +namespace android { +namespace init { Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line) : func_(f), args_(args), line_(line) {} @@ -89,19 +91,18 @@ } void Action::ExecuteCommand(const Command& command) const { - Timer t; + android::base::Timer t; int result = command.InvokeFunc(); - double duration_ms = t.duration_s() * 1000; + auto duration = t.duration(); // Any action longer than 50ms will be warned to user as slow operation - if (duration_ms > 50.0 || - android::base::GetMinimumLogSeverity() <= android::base::DEBUG) { + if (duration > 50ms || android::base::GetMinimumLogSeverity() <= android::base::DEBUG) { std::string trigger_name = BuildTriggersString(); std::string cmd_str = command.BuildCommandString(); - 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."; + LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_ + << ":" << command.line() << ") returned " << result << " took " + << duration.count() << "ms."; } } @@ -358,3 +359,6 @@ action_manager_->AddAction(std::move(action_)); } } + +} // namespace init +} // namespace android
diff --git a/init/action.h b/init/action.h index c04076a..ad15f3f 100644 --- a/init/action.h +++ b/init/action.h
@@ -27,6 +27,9 @@ #include "init_parser.h" #include "keyword_map.h" +namespace android { +namespace init { + class Command { public: Command(BuiltinFunction f, const std::vector<std::string>& args, int line); @@ -127,4 +130,7 @@ std::unique_ptr<Action> action_; }; +} // namespace init +} // namespace android + #endif
diff --git a/init/bootchart.cpp b/init/bootchart.cpp index 825603a..4727f92 100644 --- a/init/bootchart.cpp +++ b/init/bootchart.cpp
@@ -40,6 +40,9 @@ using android::base::StringPrintf; using namespace std::chrono_literals; +namespace android { +namespace init { + static std::thread* g_bootcharting_thread; static std::mutex g_bootcharting_finished_mutex; @@ -192,3 +195,6 @@ if (args[1] == "start") return do_bootchart_start(); return do_bootchart_stop(); } + +} // namespace init +} // namespace android
diff --git a/init/bootchart.h b/init/bootchart.h index 0e3593d..e4f7b59 100644 --- a/init/bootchart.h +++ b/init/bootchart.h
@@ -20,6 +20,12 @@ #include <string> #include <vector> +namespace android { +namespace init { + int do_bootchart(const std::vector<std::string>& args); +} // namespace init +} // namespace android + #endif /* _BOOTCHART_H */
diff --git a/init/builtins.cpp b/init/builtins.cpp index fcc6092..9e2efe2 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp
@@ -39,11 +39,11 @@ #include <sys/wait.h> #include <unistd.h> +#include <android-base/chrono_utils.h> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/parseint.h> #include <android-base/properties.h> -#include <android-base/stringprintf.h> #include <android-base/strings.h> #include <bootloader_message/bootloader_message.h> #include <cutils/android_reboot.h> @@ -68,6 +68,9 @@ #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW +namespace android { +namespace init { + static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s; static int insmod(const char *filename, const char *options, int flags) { @@ -259,7 +262,7 @@ "--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]}; reboot_into_recovery(options); - return -1; + return 0; } } return 0; @@ -487,7 +490,8 @@ /* Setup a wipe via recovery, and reboot into recovery */ PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery."; const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" }; - ret = reboot_into_recovery(options); + reboot_into_recovery(options); + return 0; /* If reboot worked, there is no return. */ } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) { if (e4crypt_install_keyring()) { @@ -552,11 +556,10 @@ } } - std::string prop_name = android::base::StringPrintf("ro.boottime.init.mount_all.%s", - prop_post_fix); - Timer t; + std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix; + android::base::Timer t; int ret = mount_fstab(fstabfile, mount_mode); - property_set(prop_name.c_str(), std::to_string(t.duration_ms()).c_str()); + property_set(prop_name, std::to_string(t.duration().count())); if (import_rc) { /* Paths of .rc files are specified at the 2nd argument and beyond */ @@ -584,9 +587,7 @@ } static int do_setprop(const std::vector<std::string>& args) { - const char* name = args[1].c_str(); - const char* value = args[2].c_str(); - property_set(name, value); + property_set(args[1], args[2]); return 0; } @@ -669,8 +670,7 @@ static void verity_update_property(fstab_rec *fstab, const char *mount_point, int mode, int status) { - property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(), - android::base::StringPrintf("%d", mode).c_str()); + property_set("partition."s + mount_point + ".verified", std::to_string(mode)); } static int do_verity_update_state(const std::vector<std::string>& args) { @@ -946,3 +946,6 @@ // clang-format on return builtin_functions; } + +} // namespace init +} // namespace android
diff --git a/init/builtins.h b/init/builtins.h index e1f0567..b110f61 100644 --- a/init/builtins.h +++ b/init/builtins.h
@@ -24,6 +24,9 @@ #include "keyword_map.h" +namespace android { +namespace init { + using BuiltinFunction = std::function<int(const std::vector<std::string>&)>; class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> { public: @@ -33,4 +36,7 @@ const Map& map() const override; }; +} // namespace init +} // namespace android + #endif
diff --git a/init/capabilities.cpp b/init/capabilities.cpp index b8a9ec0..642a364 100644 --- a/init/capabilities.cpp +++ b/init/capabilities.cpp
@@ -25,6 +25,9 @@ #define CAP_MAP_ENTRY(cap) { #cap, CAP_##cap } +namespace android { +namespace init { + static const std::map<std::string, int> cap_map = { CAP_MAP_ENTRY(CHOWN), CAP_MAP_ENTRY(DAC_OVERRIDE), @@ -104,17 +107,15 @@ } static bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) { - cap_t caps = cap_init(); - auto deleter = [](cap_t* p) { cap_free(*p); }; - std::unique_ptr<cap_t, decltype(deleter)> ptr_caps(&caps, deleter); + ScopedCaps caps(cap_init()); - cap_clear(caps); + cap_clear(caps.get()); cap_value_t value[1]; for (size_t cap = 0; cap < to_keep.size(); ++cap) { if (to_keep.test(cap)) { value[0] = cap; - if (cap_set_flag(caps, CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 || - cap_set_flag(caps, CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0) { + if (cap_set_flag(caps.get(), CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 || + cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0) { PLOG(ERROR) << "cap_set_flag(INHERITABLE|PERMITTED, " << cap << ") failed"; return false; } @@ -123,14 +124,14 @@ if (add_setpcap) { value[0] = CAP_SETPCAP; - if (cap_set_flag(caps, CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0 || - cap_set_flag(caps, CAP_EFFECTIVE, arraysize(value), value, CAP_SET) != 0) { + if (cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0 || + cap_set_flag(caps.get(), CAP_EFFECTIVE, arraysize(value), value, CAP_SET) != 0) { PLOG(ERROR) << "cap_set_flag(PERMITTED|EFFECTIVE, " << CAP_SETPCAP << ") failed"; return false; } } - if (cap_set_proc(caps) != 0) { + if (cap_set_proc(caps.get()) != 0) { PLOG(ERROR) << "cap_set_proc(" << to_keep.to_ulong() << ") failed"; return false; } @@ -192,3 +193,6 @@ // See http://man7.org/linux/man-pages/man7/capabilities.7.html. return SetAmbientCaps(to_keep); } + +} // namespace init +} // namespace android
diff --git a/init/capabilities.h b/init/capabilities.h index abd7fb2..ede85c3 100644 --- a/init/capabilities.h +++ b/init/capabilities.h
@@ -15,16 +15,28 @@ #ifndef _INIT_CAPABILITIES_H #define _INIT_CAPABILITIES_H -#include <linux/capability.h> +#include <sys/capability.h> #include <bitset> #include <string> +#include <type_traits> + +namespace android { +namespace init { + +struct CapDeleter { + void operator()(cap_t caps) const { cap_free(caps); } +}; using CapSet = std::bitset<CAP_LAST_CAP + 1>; +using ScopedCaps = std::unique_ptr<std::remove_pointer<cap_t>::type, CapDeleter>; int LookupCap(const std::string& cap_name); bool CapAmbientSupported(); unsigned int GetLastValidCap(); bool SetCapsForExec(const CapSet& to_keep); +} // namespace init +} // namespace android + #endif // _INIT_CAPABILITIES_H
diff --git a/init/descriptors.cpp b/init/descriptors.cpp index 6f729a3..0cb639a 100644 --- a/init/descriptors.cpp +++ b/init/descriptors.cpp
@@ -31,6 +31,9 @@ #include "init.h" #include "util.h" +namespace android { +namespace init { + DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid, gid_t gid, int perm, const std::string& context) : name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) { @@ -58,7 +61,7 @@ std::for_each(publishedName.begin(), publishedName.end(), [] (char& c) { c = isalnum(c) ? c : '_'; }); - std::string val = android::base::StringPrintf("%d", fd); + std::string val = std::to_string(fd); add_environment(publishedName.c_str(), val.c_str()); // make sure we don't close on exec @@ -74,7 +77,8 @@ } void SocketInfo::Clean() const { - unlink(android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s", name().c_str()).c_str()); + std::string path = android::base::StringPrintf("%s/%s", ANDROID_SOCKET_DIR, name().c_str()); + unlink(path.c_str()); } int SocketInfo::Create(const std::string& context) const { @@ -125,3 +129,6 @@ const std::string FileInfo::key() const { return ANDROID_FILE_ENV_PREFIX; } + +} // namespace init +} // namespace android
diff --git a/init/descriptors.h b/init/descriptors.h index ff276fb..3bdddfe 100644 --- a/init/descriptors.h +++ b/init/descriptors.h
@@ -22,6 +22,9 @@ #include <string> +namespace android { +namespace init { + class DescriptorInfo { public: DescriptorInfo(const std::string& name, const std::string& type, uid_t uid, @@ -75,4 +78,7 @@ virtual const std::string key() const override; }; +} // namespace init +} // namespace android + #endif
diff --git a/init/devices.cpp b/init/devices.cpp index 2943fb7..13cf991 100644 --- a/init/devices.cpp +++ b/init/devices.cpp
@@ -37,13 +37,23 @@ #error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals" #endif +using android::base::Basename; +using android::base::Dirname; +using android::base::Readlink; +using android::base::Realpath; +using android::base::StartsWith; +using android::base::StringPrintf; + +namespace android { +namespace init { + /* Given a path that may start with a PCI device, populate the supplied buffer * with the PCI domain/bus number and the peripheral ID and return 0. * If it doesn't start with a PCI device, or there is some error, return -1 */ static bool FindPciDevicePrefix(const std::string& path, std::string* result) { result->clear(); - if (!android::base::StartsWith(path, "/devices/pci")) return false; + if (!StartsWith(path, "/devices/pci")) return false; /* Beginning of the prefix is the initial "pci" after "/devices/" */ std::string::size_type start = 9; @@ -74,7 +84,7 @@ static bool FindVbdDevicePrefix(const std::string& path, std::string* result) { result->clear(); - if (!android::base::StartsWith(path, "/devices/vbd-")) return false; + if (!StartsWith(path, "/devices/vbd-")) return false; /* Beginning of the prefix is the initial "vbd-" after "/devices/" */ std::string::size_type start = 13; @@ -116,14 +126,14 @@ } bool Permissions::Match(const std::string& path) const { - if (prefix_) return android::base::StartsWith(path, name_.c_str()); + if (prefix_) return StartsWith(path, name_.c_str()); if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0; return path == name_; } bool SysfsPermissions::MatchWithSubsystem(const std::string& path, const std::string& subsystem) const { - std::string path_basename = android::base::Basename(path); + std::string path_basename = Basename(path); if (name().find(subsystem) != std::string::npos) { if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true; if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true; @@ -156,11 +166,11 @@ // Uevents don't contain the mount point, so we need to add it here. path.insert(0, sysfs_mount_point_); - std::string directory = android::base::Dirname(path); + std::string directory = Dirname(path); while (directory != "/" && directory != ".") { std::string subsystem_link_path; - if (android::base::Realpath(directory + "/subsystem", &subsystem_link_path) && + if (Realpath(directory + "/subsystem", &subsystem_link_path) && subsystem_link_path == sysfs_mount_point_ + "/bus/platform") { // We need to remove the mount point that we added above before returning. directory.erase(0, sysfs_mount_point_.size()); @@ -172,7 +182,7 @@ if (last_slash == std::string::npos) return false; path.erase(last_slash); - directory = android::base::Dirname(path); + directory = Dirname(path); } return false; @@ -209,7 +219,7 @@ return {0600, 0, 0}; } -void DeviceHandler::MakeDevice(const std::string& path, int block, int major, int minor, +void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, int minor, const std::vector<std::string>& links) const { auto[mode, uid, gid] = GetDevicePermissions(path, links); mode |= (block ? S_IFBLK : S_IFCHR); @@ -269,45 +279,6 @@ } } -std::vector<std::string> DeviceHandler::GetCharacterDeviceSymlinks(const Uevent& uevent) const { - std::string parent_device; - if (!FindPlatformDevice(uevent.path, &parent_device)) return {}; - - // skip path to the parent driver - std::string path = uevent.path.substr(parent_device.length()); - - if (!android::base::StartsWith(path, "/usb")) return {}; - - // 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" - - std::string::size_type start = 0; - start = path.find('/', start + 1); - if (start == std::string::npos) return {}; - - 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; -} - // replaces any unacceptable characters with '_', the // length of the resulting string is equal to the input string void SanitizePartitionName(std::string* string) { @@ -334,9 +305,9 @@ 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())) { + if (StartsWith(device, devices_platform_prefix.c_str())) { device = device.substr(devices_platform_prefix.length()); - } else if (android::base::StartsWith(device, devices_prefix.c_str())) { + } else if (StartsWith(device, devices_prefix.c_str())) { device = device.substr(devices_prefix.length()); } @@ -375,13 +346,13 @@ return links; } -void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, int block, +void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block, int major, int minor, const std::vector<std::string>& links) const { if (action == "add") { MakeDevice(devpath, block, major, minor, links); for (const auto& link : links) { - if (mkdir_recursive(android::base::Dirname(link), 0755, sehandle_)) { - PLOG(ERROR) << "Failed to create directory " << android::base::Dirname(link); + if (mkdir_recursive(Dirname(link), 0755, sehandle_)) { + PLOG(ERROR) << "Failed to create directory " << Dirname(link); } if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) { @@ -393,7 +364,7 @@ if (action == "remove") { for (const auto& link : links) { std::string link_path; - if (android::base::Readlink(link, &link_path) && link_path == devpath) { + if (Readlink(link, &link_path) && link_path == devpath) { unlink(link.c_str()); } } @@ -401,31 +372,26 @@ } } -void DeviceHandler::HandleBlockDeviceEvent(const Uevent& uevent) const { - // if it's not a /dev device, nothing to do - if (uevent.major < 0 || uevent.minor < 0) return; - - const char* base = "/dev/block/"; - make_dir(base, 0755, sehandle_); - - 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 = GetBlockDeviceSymlinks(uevent); +void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) { + if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") { + FixupSysPermissions(uevent.path, uevent.subsystem); } - HandleDevice(uevent.action, devpath, 1, uevent.major, uevent.minor, links); -} - -void DeviceHandler::HandleGenericDeviceEvent(const Uevent& uevent) const { // if it's not a /dev device, nothing to do if (uevent.major < 0 || uevent.minor < 0) return; std::string devpath; + std::vector<std::string> links; + bool block = false; - if (android::base::StartsWith(uevent.subsystem, "usb")) { + if (uevent.subsystem == "block") { + block = true; + devpath = "/dev/block/" + Basename(uevent.path); + + if (StartsWith(uevent.path, "/devices")) { + links = GetBlockDeviceSymlinks(uevent); + } + } else if (StartsWith(uevent.subsystem, "usb")) { if (uevent.subsystem == "usb") { if (!uevent.device_name.empty()) { devpath = "/dev/" + uevent.device_name; @@ -435,7 +401,7 @@ // 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); + devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id); } } else { // ignore other USB events @@ -446,26 +412,12 @@ subsystem != subsystems_.cend()) { devpath = subsystem->ParseDevPath(uevent); } else { - devpath = "/dev/" + android::base::Basename(uevent.path); + devpath = "/dev/" + Basename(uevent.path); } - mkdir_recursive(android::base::Dirname(devpath), 0755, sehandle_); + mkdir_recursive(Dirname(devpath), 0755, sehandle_); - auto links = GetCharacterDeviceSymlinks(uevent); - - HandleDevice(uevent.action, devpath, 0, uevent.major, uevent.minor, links); -} - -void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) { - if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") { - FixupSysPermissions(uevent.path, uevent.subsystem); - } - - if (uevent.subsystem == "block") { - HandleBlockDeviceEvent(uevent); - } else { - HandleGenericDeviceEvent(uevent); - } + HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links); } DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions, @@ -481,3 +433,6 @@ DeviceHandler::DeviceHandler() : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{}, false) {} + +} // namespace init +} // namespace android
diff --git a/init/devices.h b/init/devices.h index 362c38c..c64f5fb 100644 --- a/init/devices.h +++ b/init/devices.h
@@ -29,6 +29,9 @@ #include "uevent.h" +namespace android { +namespace init { + class Permissions { public: Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid); @@ -112,16 +115,12 @@ bool FindPlatformDevice(std::string path, std::string* platform_device_path) const; std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions( const std::string& path, const std::vector<std::string>& links) const; - void MakeDevice(const std::string& path, int block, int major, int minor, + void MakeDevice(const std::string& path, bool block, int major, int minor, const std::vector<std::string>& links) const; - std::vector<std::string> GetCharacterDeviceSymlinks(const Uevent& uevent) const; - void HandleDevice(const std::string& action, const std::string& devpath, int block, int major, + void HandleDevice(const std::string& action, const std::string& devpath, bool block, int major, int minor, const std::vector<std::string>& links) const; void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const; - void HandleBlockDeviceEvent(const Uevent& uevent) const; - void HandleGenericDeviceEvent(const Uevent& uevent) const; - std::vector<Permissions> dev_permissions_; std::vector<SysfsPermissions> sysfs_permissions_; std::vector<Subsystem> subsystems_; @@ -133,4 +132,7 @@ // Exposed for testing void SanitizePartitionName(std::string* string); +} // namespace init +} // namespace android + #endif
diff --git a/init/devices_test.cpp b/init/devices_test.cpp index e1e4e49..ac4ab9b 100644 --- a/init/devices_test.cpp +++ b/init/devices_test.cpp
@@ -24,10 +24,13 @@ using namespace std::string_literals; +namespace android { +namespace init { + class DeviceHandlerTester { public: void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent, - const std::vector<std::string> expected_links, bool block) { + const std::vector<std::string> expected_links) { TemporaryDir fake_sys_root; device_handler_.sysfs_mount_point_ = fake_sys_root.path; @@ -41,11 +44,7 @@ mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777, nullptr); std::vector<std::string> result; - if (block) { - result = device_handler_.GetBlockDeviceSymlinks(uevent); - } else { - result = device_handler_.GetCharacterDeviceSymlinks(uevent); - } + result = device_handler_.GetBlockDeviceSymlinks(uevent); auto expected_size = expected_links.size(); ASSERT_EQ(expected_size, result.size()); @@ -61,95 +60,6 @@ DeviceHandler device_handler_; }; -TEST(device_handler, 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"}; - - DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false); -} - -TEST(device_handler, 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; - - DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false); -} - -TEST(device_handler, 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; - - DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false); -} - -TEST(device_handler, 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; - - DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false); -} - -TEST(device_handler, 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; - - DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false); -} - -TEST(device_handler, 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; - - DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false); -} - -TEST(device_handler, 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; - - DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false); -} - -TEST(device_handler, 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; - - DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false); -} - TEST(device_handler, get_block_device_symlinks_success_platform) { // These are actual paths from bullhead const char* platform_device = "/devices/soc.0/f9824900.sdhci"; @@ -161,7 +71,7 @@ std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"}; DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true); + device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result); } TEST(device_handler, get_block_device_symlinks_success_platform_with_partition) { @@ -179,7 +89,7 @@ }; DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true); + device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result); } TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_num) { @@ -195,7 +105,7 @@ }; DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true); + device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result); } TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_name) { @@ -211,7 +121,7 @@ }; DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true); + device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result); } TEST(device_handler, get_block_device_symlinks_success_pci) { @@ -222,7 +132,7 @@ std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"}; DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true); + device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result); } TEST(device_handler, get_block_device_symlinks_pci_bad_format) { @@ -233,7 +143,7 @@ std::vector<std::string> expected_result{}; DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true); + device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result); } TEST(device_handler, get_block_device_symlinks_success_vbd) { @@ -244,7 +154,7 @@ std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"}; DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true); + device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result); } TEST(device_handler, get_block_device_symlinks_vbd_bad_format) { @@ -255,7 +165,7 @@ std::vector<std::string> expected_result{}; DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true); + device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result); } TEST(device_handler, get_block_device_symlinks_no_matches) { @@ -268,7 +178,7 @@ std::vector<std::string> expected_result; DeviceHandlerTester device_handler_tester_; - device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true); + device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result); } TEST(device_handler, sanitize_null) { @@ -400,3 +310,6 @@ EXPECT_EQ(0U, permissions.uid()); EXPECT_EQ(1001U, permissions.gid()); } + +} // namespace init +} // namespace android
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp index 844c605..b686885 100644 --- a/init/firmware_handler.cpp +++ b/init/firmware_handler.cpp
@@ -24,16 +24,22 @@ #include <string> #include <thread> +#include <android-base/chrono_utils.h> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/unique_fd.h> -#include "util.h" +using android::base::Timer; +using android::base::unique_fd; +using android::base::WriteFully; + +namespace android { +namespace init { static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size, int loading_fd, int data_fd) { // Start transfer. - android::base::WriteFully(loading_fd, "1", 1); + WriteFully(loading_fd, "1", 1); // Copy the firmware. int rc = sendfile(data_fd, fw_fd, nullptr, fw_size); @@ -44,7 +50,7 @@ // Tell the firmware whether to abort or commit. const char* response = (rc != -1) ? "0" : "-1"; - android::base::WriteFully(loading_fd, response, strlen(response)); + WriteFully(loading_fd, response, strlen(response)); } static bool IsBooting() { @@ -60,13 +66,13 @@ std::string loading = root + "/loading"; std::string data = root + "/data"; - android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC)); + unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC)); if (loading_fd == -1) { PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware; return; } - android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC)); + unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC)); if (data_fd == -1) { PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware; return; @@ -78,7 +84,7 @@ try_loading_again: for (size_t i = 0; i < arraysize(firmware_dirs); i++) { std::string file = firmware_dirs[i] + uevent.firmware; - android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC)); + unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC)); struct stat sb; if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) { LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd); @@ -104,29 +110,17 @@ if (uevent.subsystem != "firmware" || uevent.action != "add") return; // Loading the firmware in a child means we can do that in parallel... - // We double fork instead of waiting for these processes. - pid_t pid = fork(); + auto pid = fork(); if (pid == -1) { PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware; - return; } - if (pid == 0) { - pid = fork(); - if (pid == -1) { - PLOG(ERROR) << "could not fork a sceond time to process firmware event for " - << uevent.firmware; - _exit(EXIT_FAILURE); - } - if (pid == 0) { - Timer t; - ProcessFirmwareEvent(uevent); - LOG(INFO) << "loading " << uevent.path << " took " << t; - _exit(EXIT_SUCCESS); - } - + Timer t; + ProcessFirmwareEvent(uevent); + LOG(INFO) << "loading " << uevent.path << " took " << t; _exit(EXIT_SUCCESS); } - - waitpid(pid, nullptr, 0); } + +} // namespace init +} // namespace android
diff --git a/init/firmware_handler.h b/init/firmware_handler.h index be9daae..e456ac4 100644 --- a/init/firmware_handler.h +++ b/init/firmware_handler.h
@@ -19,6 +19,12 @@ #include "uevent.h" +namespace android { +namespace init { + void HandleFirmwareEvent(const Uevent& uevent); +} // namespace init +} // namespace android + #endif
diff --git a/init/import_parser.cpp b/init/import_parser.cpp index 99275e5..b9fa2ce 100644 --- a/init/import_parser.cpp +++ b/init/import_parser.cpp
@@ -20,6 +20,9 @@ #include "util.h" +namespace android { +namespace init { + bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename, int line, std::string* err) { if (args.size() != 2) { @@ -50,3 +53,6 @@ } } } + +} // namespace init +} // namespace android
diff --git a/init/import_parser.h b/init/import_parser.h index 45cbfad..b774c57 100644 --- a/init/import_parser.h +++ b/init/import_parser.h
@@ -22,6 +22,9 @@ #include <string> #include <vector> +namespace android { +namespace init { + class ImportParser : public SectionParser { public: ImportParser(Parser* parser) : parser_(parser) {} @@ -37,4 +40,7 @@ std::vector<std::pair<std::string, int>> imports_; }; +} // namespace init +} // namespace android + #endif
diff --git a/init/init.cpp b/init/init.cpp index 9652efa..f65bfe0 100644 --- a/init/init.cpp +++ b/init/init.cpp
@@ -42,14 +42,12 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/properties.h> -#include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <keyutils.h> #include <libavb/libavb.h> #include <private/android_filesystem_config.h> #include <selinux/android.h> -#include <selinux/label.h> #include <selinux/selinux.h> #include <fstream> @@ -71,9 +69,14 @@ #include "util.h" #include "watchdogd.h" +using namespace std::string_literals; + using android::base::boot_clock; using android::base::GetProperty; -using android::base::StringPrintf; +using android::base::Timer; + +namespace android { +namespace init { struct selabel_handle *sehandle; struct selabel_handle *sehandle_prop; @@ -93,6 +96,8 @@ static std::string wait_prop_name; static std::string wait_prop_value; static bool shutting_down; +static std::string shutdown_command; +static bool do_shutdown = false; void DumpState() { ServiceManager::GetInstance().DumpState(); @@ -171,9 +176,16 @@ // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific // commands to be executed. if (name == "sys.powerctl") { - if (HandlePowerctlMessage(value)) { - shutting_down = true; - } + // Despite the above comment, we can't call HandlePowerctlMessage() in this function, + // because it modifies the contents of the action queue, which can cause the action queue + // to get into a bad state if this function is called from a command being executed by the + // action queue. Instead we set this flag and ensure that shutdown happens before the next + // command is run in the main init loop. + // TODO: once property service is removed from init, this will never happen from a builtin, + // but rather from a callback from the property service socket, in which case this hack can + // go away. + shutdown_command = value; + do_shutdown = true; } if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value); @@ -230,7 +242,7 @@ panic(); } - property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration_ms()).c_str()); + property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count())); return 0; } @@ -388,7 +400,10 @@ int ret = -1; /* values are arch-dependent */ -#if defined(__aarch64__) +#if defined(USER_MODE_LINUX) + /* uml does not support mmap_rnd_bits */ + ret = 0; +#elif defined(__aarch64__) /* arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE */ if (set_mmap_rnd_bits_min(33, 24, false) && set_mmap_rnd_bits_min(16, 16, true)) { @@ -460,14 +475,14 @@ if (for_emulator) { // In the emulator, export any kernel option with the "ro.kernel." prefix. - property_set(StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str()); + property_set("ro.kernel." + key, value); return; } if (key == "qemu") { strlcpy(qemu, value.c_str(), sizeof(qemu)); } else if (android::base::StartsWith(key, "androidboot.")) { - property_set(StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(), value.c_str()); + property_set("ro.boot." + key.substr(12), value); } } @@ -498,7 +513,7 @@ }; for (size_t i = 0; i < arraysize(prop_map); i++) { std::string value = GetProperty(prop_map[i].src_prop, ""); - property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value); + property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value); } } @@ -507,7 +522,7 @@ return; } - std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(kAndroidDtDir.c_str()), closedir); + std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir); if (!dir) return; std::string dt_file; @@ -517,13 +532,12 @@ continue; } - std::string file_name = kAndroidDtDir + dp->d_name; + std::string file_name = get_android_dt_dir() + dp->d_name; android::base::ReadFileToString(file_name, &dt_file); std::replace(dt_file.begin(), dt_file.end(), ',', '.'); - std::string property_name = StringPrintf("ro.boot.%s", dp->d_name); - property_set(property_name.c_str(), dt_file.c_str()); + property_set("ro.boot."s + dp->d_name, dt_file); } } @@ -802,13 +816,15 @@ return false; } std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil"); + const std::string version_as_string = std::to_string(max_policy_version); + // clang-format off const char* compile_args[] = { "/system/bin/secilc", plat_policy_cil_file, "-M", "true", "-G", "-N", // Target the highest policy language version supported by the kernel - "-c", std::to_string(max_policy_version).c_str(), + "-c", version_as_string.c_str(), mapping_file.c_str(), "/vendor/etc/selinux/nonplat_sepolicy.cil", "-o", compiled_sepolicy, @@ -887,7 +903,7 @@ } // init's first stage can't set properties, so pass the time to the second stage. - setenv("INIT_SELINUX_TOOK", std::to_string(t.duration_ms()).c_str(), 1); + setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1); } else { selinux_init_all_handles(); } @@ -905,7 +921,6 @@ selinux_android_restorecon("/dev/urandom", 0); selinux_android_restorecon("/dev/__properties__", 0); - selinux_android_restorecon("/file_contexts.bin", 0); selinux_android_restorecon("/plat_file_contexts", 0); selinux_android_restorecon("/nonplat_file_contexts", 0); selinux_android_restorecon("/plat_property_contexts", 0); @@ -919,7 +934,6 @@ selinux_android_restorecon("/sepolicy", 0); selinux_android_restorecon("/vndservice_contexts", 0); - selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE); selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE); selinux_android_restorecon("/dev/device-mapper", 0); @@ -943,7 +957,7 @@ } } -static void install_reboot_signal_handlers() { +static void InstallRebootSignalHandlers() { // Instead of panic'ing the kernel as is the default behavior when init crashes, // we prefer to reboot to bootloader on development builds, as this will prevent // boot looping bad configurations and allow both developers and test farms to easily @@ -951,7 +965,13 @@ struct sigaction action; memset(&action, 0, sizeof(action)); sigfillset(&action.sa_mask); - action.sa_handler = [](int) { + action.sa_handler = [](int signal) { + // These signal handlers are also caught for processes forked from init, however we do not + // want them to trigger reboot, so we directly call _exit() for children processes here. + if (getpid() != 1) { + _exit(signal); + } + // panic() reboots to bootloader panic(); }; @@ -978,7 +998,7 @@ } if (REBOOT_BOOTLOADER_ON_PANIC) { - install_reboot_signal_handlers(); + InstallRebootSignalHandlers(); } add_environment("PATH", _PATH_DEFPATH); @@ -1036,7 +1056,7 @@ static constexpr uint32_t kNanosecondsPerMillisecond = 1e6; uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond; - setenv("INIT_STARTED_AT", StringPrintf("%" PRIu64, start_ms).c_str(), 1); + setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1); char* path = argv[0]; char* args[] = { path, nullptr }; @@ -1164,6 +1184,13 @@ // By default, sleep until something happens. int epoll_timeout_ms = -1; + if (do_shutdown && !shutting_down) { + do_shutdown = false; + if (HandlePowerctlMessage(shutdown_command)) { + shutting_down = true; + } + } + if (!(waiting_for_prop || sm.IsWaitingForExec())) { am.ExecuteOneCommand(); } @@ -1191,3 +1218,10 @@ return 0; } + +} // namespace init +} // namespace android + +int main(int argc, char** argv) { + android::init::main(argc, argv); +}
diff --git a/init/init.h b/init/init.h index 4024cfe..aaab523 100644 --- a/init/init.h +++ b/init/init.h
@@ -19,6 +19,11 @@ #include <string> +#include <selinux/label.h> + +namespace android { +namespace init { + // Note: These globals are *only* valid in init, so they should not be used in ueventd, // watchdogd, or any files that may be included in those, such as devices.cpp and util.cpp. // TODO: Have an Init class and remove all globals. @@ -41,4 +46,7 @@ void ResetWaitForProp(); +} // namespace init +} // namespace android + #endif /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp index d72c3ac..0f7e38f 100644 --- a/init/init_first_stage.cpp +++ b/init/init_first_stage.cpp
@@ -25,6 +25,7 @@ #include <string> #include <vector> +#include <android-base/chrono_utils.h> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/strings.h> @@ -36,7 +37,10 @@ #include "uevent_listener.h" #include "util.h" -using namespace std::chrono_literals; +using android::base::Timer; + +namespace android { +namespace init { // Class Declarations // ------------------ @@ -508,3 +512,6 @@ } setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1); } + +} // namespace init +} // namespace android
diff --git a/init/init_first_stage.h b/init/init_first_stage.h index 170a24c..c7a3867 100644 --- a/init/init_first_stage.h +++ b/init/init_first_stage.h
@@ -17,7 +17,13 @@ #ifndef _INIT_FIRST_STAGE_H #define _INIT_FIRST_STAGE_H +namespace android { +namespace init { + bool DoFirstStageMount(); void SetInitAvbVersionInRecovery(); +} // namespace init +} // namespace android + #endif
diff --git a/init/init_parser.cpp b/init/init_parser.cpp index 1b31cf2..9f7089b 100644 --- a/init/init_parser.cpp +++ b/init/init_parser.cpp
@@ -18,6 +18,7 @@ #include <dirent.h> +#include <android-base/chrono_utils.h> #include <android-base/logging.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> @@ -25,6 +26,9 @@ #include "parser.h" #include "util.h" +namespace android { +namespace init { + Parser::Parser() { } @@ -108,7 +112,7 @@ bool Parser::ParseConfigFile(const std::string& path) { LOG(INFO) << "Parsing file " << path << "..."; - Timer t; + android::base::Timer t; std::string data; std::string err; if (!ReadFile(path, &data, &err)) { @@ -159,3 +163,6 @@ } return ParseConfigFile(path); } + +} // namespace init +} // namespace android
diff --git a/init/init_parser.h b/init/init_parser.h index 722ebb2..c07a699 100644 --- a/init/init_parser.h +++ b/init/init_parser.h
@@ -45,6 +45,9 @@ // This function is called at the end of the file. // It indicates that the parsing has completed and any relevant objects should be committed. +namespace android { +namespace init { + class SectionParser { public: virtual ~SectionParser() {} @@ -93,4 +96,7 @@ bool is_odm_etc_init_loaded_ = false; }; +} // namespace init +} // namespace android + #endif
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp index 86d60d0..95f269a 100644 --- a/init/init_parser_test.cpp +++ b/init/init_parser_test.cpp
@@ -16,13 +16,17 @@ #include "init_parser.h" -#include "init.h" -#include "service.h" +#include <string> +#include <vector> #include <gtest/gtest.h> -#include <string> -#include <vector> +#include "init.h" +#include "service.h" +#include "util.h" + +namespace android { +namespace init { TEST(init_parser, make_exec_oneshot_service_invalid_syntax) { ServiceManager& sm = ServiceManager::GetInstance(); @@ -141,3 +145,6 @@ TEST(init_parser, make_exec_oneshot_service_with_just_command_no_dash) { Test_make_exec_oneshot_service(false, false, false, false, false); } + +} // namespace init +} // namespace android
diff --git a/init/init_test.cpp b/init/init_test.cpp index 7093ba9..0a4071b 100644 --- a/init/init_test.cpp +++ b/init/init_test.cpp
@@ -27,6 +27,9 @@ #include "keyword_map.h" #include "util.h" +namespace android { +namespace init { + class TestFunctionMap : public KeywordMap<BuiltinFunction> { public: // Helper for argument-less functions @@ -185,3 +188,6 @@ EXPECT_EQ(6, num_executed); } + +} // namespace init +} // namespace android
diff --git a/init/keychords.cpp b/init/keychords.cpp index c572cee..a0d7cc5 100644 --- a/init/keychords.cpp +++ b/init/keychords.cpp
@@ -14,6 +14,8 @@ * limitations under the License. */ +#include "keychords.h" + #include <fcntl.h> #include <stdlib.h> #include <sys/stat.h> @@ -25,7 +27,9 @@ #include <android-base/properties.h> #include "init.h" -#include "service.h" + +namespace android { +namespace init { static struct input_keychord *keychords = 0; static int keychords_count = 0; @@ -112,3 +116,6 @@ register_epoll_handler(keychord_fd, handle_keychord); } + +} // namespace init +} // namespace android
diff --git a/init/keychords.h b/init/keychords.h index d2723b7..1c34098 100644 --- a/init/keychords.h +++ b/init/keychords.h
@@ -17,9 +17,15 @@ #ifndef _INIT_KEYCHORDS_H_ #define _INIT_KEYCHORDS_H_ -struct service; +#include "service.h" -void add_service_keycodes(service*); +namespace android { +namespace init { + +void add_service_keycodes(Service* svc); void keychord_init(); +} // namespace init +} // namespace android + #endif
diff --git a/init/keyword_map.h b/init/keyword_map.h index 88bad01..481d637 100644 --- a/init/keyword_map.h +++ b/init/keyword_map.h
@@ -22,6 +22,9 @@ #include <android-base/stringprintf.h> +namespace android { +namespace init { + template <typename Function> class KeywordMap { public: @@ -79,4 +82,7 @@ virtual const Map& map() const = 0; }; +} // namespace init +} // namespace android + #endif
diff --git a/init/log.cpp b/init/log.cpp index 0615730..1830077 100644 --- a/init/log.cpp +++ b/init/log.cpp
@@ -23,6 +23,9 @@ #include <android-base/logging.h> #include <selinux/selinux.h> +namespace android { +namespace init { + void InitKernelLogging(char* argv[]) { // Make stdin/stdout/stderr all point to /dev/null. int fd = open("/sys/fs/selinux/null", O_RDWR); @@ -55,3 +58,6 @@ android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf); return 0; } + +} // namespace init +} // namespace android
diff --git a/init/log.h b/init/log.h index 29a27af..5a4eba6 100644 --- a/init/log.h +++ b/init/log.h
@@ -19,8 +19,14 @@ #include <sys/cdefs.h> +namespace android { +namespace init { + void InitKernelLogging(char* argv[]); int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3); +} // namespace init +} // namespace android + #endif
diff --git a/init/parser.cpp b/init/parser.cpp index 0d13cfe..c0fa6d9 100644 --- a/init/parser.cpp +++ b/init/parser.cpp
@@ -1,5 +1,8 @@ #include "parser.h" +namespace android { +namespace init { + int next_token(struct parse_state *state) { char *x = state->ptr; @@ -116,3 +119,6 @@ } return T_EOF; } + +} // namespace init +} // namespace android
diff --git a/init/parser.h b/init/parser.h index 3dcc566..86e4c57 100644 --- a/init/parser.h +++ b/init/parser.h
@@ -21,6 +21,9 @@ #define T_TEXT 1 #define T_NEWLINE 2 +namespace android { +namespace init { + struct parse_state { char *ptr; @@ -31,4 +34,7 @@ int next_token(struct parse_state *state); +} // namespace init +} // namespace android + #endif /* PARSER_H_ */
diff --git a/init/property_service.cpp b/init/property_service.cpp index 20f21a9..fd14bd6 100644 --- a/init/property_service.cpp +++ b/init/property_service.cpp
@@ -39,12 +39,13 @@ #include <sys/_system_properties.h> #include <memory> +#include <queue> #include <vector> +#include <android-base/chrono_utils.h> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/properties.h> -#include <android-base/stringprintf.h> #include <android-base/strings.h> #include <bootimg.h> #include <fs_mgr.h> @@ -55,11 +56,14 @@ #include "init.h" #include "util.h" -using android::base::StringPrintf; +using android::base::Timer; #define PERSISTENT_PROPERTY_DIR "/data/property" #define RECOVERY_MOUNT_POINT "/recovery" +namespace android { +namespace init { + static int persistent_properties_loaded = 0; static int property_set_fd = -1; @@ -162,7 +166,7 @@ return true; } -uint32_t property_set(const std::string& name, const std::string& value) { +static uint32_t PropertySetImpl(const std::string& name, const std::string& value) { size_t valuelen = value.size(); if (!is_legal_property_name(name)) { @@ -176,12 +180,6 @@ return PROP_ERROR_INVALID_VALUE; } - if (name == "selinux.restorecon_recursive" && valuelen > 0) { - if (selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) { - LOG(ERROR) << "Failed to restorecon_recursive " << value; - } - } - prop_info* pi = (prop_info*) __system_property_find(name.c_str()); if (pi != nullptr) { // ro.* properties are actually "write-once". @@ -210,6 +208,85 @@ return PROP_SUCCESS; } +typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&); + +struct PropertyChildInfo { + pid_t pid; + PropertyAsyncFunc func; + std::string name; + std::string value; +}; + +static std::queue<PropertyChildInfo> property_children; + +static void PropertyChildLaunch() { + auto& info = property_children.front(); + pid_t pid = fork(); + if (pid < 0) { + LOG(ERROR) << "Failed to fork for property_set_async"; + while (!property_children.empty()) { + property_children.pop(); + } + return; + } + if (pid != 0) { + info.pid = pid; + } else { + if (info.func(info.name, info.value) != 0) { + LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value + << "\") failed"; + } + exit(0); + } +} + +bool PropertyChildReap(pid_t pid) { + if (property_children.empty()) { + return false; + } + auto& info = property_children.front(); + if (info.pid != pid) { + return false; + } + if (PropertySetImpl(info.name, info.value) != PROP_SUCCESS) { + LOG(ERROR) << "Failed to set async property " << info.name; + } + property_children.pop(); + if (!property_children.empty()) { + PropertyChildLaunch(); + } + return true; +} + +static uint32_t PropertySetAsync(const std::string& name, const std::string& value, + PropertyAsyncFunc func) { + if (value.empty()) { + return PropertySetImpl(name, value); + } + + PropertyChildInfo info; + info.func = func; + info.name = name; + info.value = value; + property_children.push(info); + if (property_children.size() == 1) { + PropertyChildLaunch(); + } + return PROP_SUCCESS; +} + +static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) { + return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE); +} + +uint32_t property_set(const std::string& name, const std::string& value) { + if (name == "selinux.restorecon_recursive") { + return PropertySetAsync(name, value, RestoreconRecursiveAsync); + } + + return PropertySetImpl(name, value); +} + class SocketConnection { public: SocketConnection(int socket, const struct ucred& cred) @@ -276,7 +353,7 @@ while (*timeout_ms > 0) { Timer timer; int nr = poll(ufds, 1, *timeout_ms); - uint64_t millis = timer.duration_ms(); + uint64_t millis = timer.duration().count(); *timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis; if (nr > 0) { @@ -647,7 +724,7 @@ boot_img_hdr hdr; if (android::base::ReadFully(fd, &hdr, sizeof(hdr))) { std::string hex = bytes_to_hex(reinterpret_cast<uint8_t*>(hdr.id), sizeof(hdr.id)); - property_set("ro.recovery_id", hex.c_str()); + property_set("ro.recovery_id", hex); } else { PLOG(ERROR) << "error reading /recovery"; } @@ -677,3 +754,6 @@ register_epoll_handler(property_set_fd, handle_property_set_fd); } + +} // namespace init +} // namespace android
diff --git a/init/property_service.h b/init/property_service.h index 9a5b6f6..a55e79c 100644 --- a/init/property_service.h +++ b/init/property_service.h
@@ -21,11 +21,16 @@ #include <string> +namespace android { +namespace init { + struct property_audit_data { ucred *cr; const char* name; }; +extern bool PropertyChildReap(pid_t pid); + void property_init(void); void property_load_boot_defaults(void); void load_persist_props(void); @@ -34,5 +39,7 @@ uint32_t property_set(const std::string& name, const std::string& value); bool is_legal_property_name(const std::string& name); +} // namespace init +} // namespace android #endif /* _INIT_PROPERTY_H */
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp index 4d784aa..3a64e02 100644 --- a/init/property_service_test.cpp +++ b/init/property_service_test.cpp
@@ -23,6 +23,9 @@ #include <gtest/gtest.h> +namespace android { +namespace init { + TEST(property_service, very_long_name_35166374) { // Connect to the property service directly... int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); @@ -46,3 +49,6 @@ ASSERT_EQ(static_cast<ssize_t>(sizeof(data)), send(fd, &data, sizeof(data), 0)); ASSERT_EQ(0, close(fd)); } + +} // namespace init +} // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp index cee5a72..5c7ddf1 100644 --- a/init/reboot.cpp +++ b/init/reboot.cpp
@@ -20,7 +20,7 @@ #include <fcntl.h> #include <linux/fs.h> #include <mntent.h> -#include <selinux/selinux.h> +#include <sys/capability.h> #include <sys/cdefs.h> #include <sys/ioctl.h> #include <sys/mount.h> @@ -35,6 +35,7 @@ #include <thread> #include <vector> +#include <android-base/chrono_utils.h> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/macros.h> @@ -48,11 +49,16 @@ #include <logwrap/logwrap.h> #include <private/android_filesystem_config.h> +#include "capabilities.h" #include "init.h" #include "property_service.h" #include "service.h" using android::base::StringPrintf; +using android::base::Timer; + +namespace android { +namespace init { // represents umount status during reboot / shutdown. enum UmountStat { @@ -78,8 +84,8 @@ mnt_type_(entry.mnt_type), mnt_opts_(entry.mnt_opts) {} - bool Umount() { - int r = umount2(mnt_dir_.c_str(), 0); + bool Umount(bool force) { + int r = umount2(mnt_dir_.c_str(), force ? MNT_FORCE : 0); if (r == 0) { LOG(INFO) << "umounted " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_; return true; @@ -157,12 +163,42 @@ } static void LogShutdownTime(UmountStat stat, Timer* t) { - LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration_ms()) << ":" << stat; + LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration().count()) << ":" + << stat; +} + +// Determines whether the system is capable of rebooting. This is conservative, +// so if any of the attempts to determine this fail, it will still return true. +static bool IsRebootCapable() { + if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) { + PLOG(WARNING) << "CAP_SYS_BOOT is not supported"; + return true; + } + + ScopedCaps caps(cap_get_proc()); + if (!caps) { + PLOG(WARNING) << "cap_get_proc() failed"; + return true; + } + + cap_flag_value_t value = CAP_SET; + if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) { + PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed"; + return true; + } + return value == CAP_SET; } static void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) { LOG(INFO) << "Reboot ending, jumping to kernel"; + + if (!IsRebootCapable()) { + // On systems where init does not have the capability of rebooting the + // device, just exit cleanly. + exit(0); + } + switch (cmd) { case ANDROID_RB_POWEROFF: reboot(RB_POWER_OFF); @@ -227,7 +263,7 @@ } } -static UmountStat UmountPartitions(int timeoutMs) { +static UmountStat UmountPartitions(std::chrono::milliseconds timeout) { Timer t; UmountStat stat = UMOUNT_STAT_TIMEOUT; int retry = 0; @@ -245,17 +281,17 @@ stat = UMOUNT_STAT_SUCCESS; break; } - if ((timeoutMs < t.duration_ms()) && retry > 0) { // try umount at least once + if ((timeout < t.duration()) && retry > 0) { // try umount at least once stat = UMOUNT_STAT_TIMEOUT; break; } if (emulated_devices.size() > 0 && std::all_of(emulated_devices.begin(), emulated_devices.end(), - [](auto& entry) { return entry.Umount(); })) { + [](auto& entry) { return entry.Umount(false); })) { sync(); } for (auto& entry : block_devices) { - entry.Umount(); + entry.Umount(timeout == 0ms); } retry++; std::this_thread::sleep_for(100ms); @@ -274,7 +310,7 @@ * * return true when umount was successful. false when timed out. */ -static UmountStat TryUmountAndFsck(bool runFsck, int timeoutMs) { +static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeout) { Timer t; std::vector<MountEntry> block_devices; std::vector<MountEntry> emulated_devices; @@ -285,13 +321,13 @@ return UMOUNT_STAT_ERROR; } - UmountStat stat = UmountPartitions(timeoutMs - t.duration_ms()); + UmountStat stat = UmountPartitions(timeout - t.duration()); if (stat != UMOUNT_STAT_SUCCESS) { LOG(INFO) << "umount timeout, last resort, kill all and try"; if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true); KillAllProcesses(); // even if it succeeds, still it is timeout and do not run fsck with all processes killed - UmountStat st = UmountPartitions(0); + UmountStat st = UmountPartitions(0ms); if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false); } @@ -319,19 +355,19 @@ runFsck = false; } - unsigned int shutdown_timeout = 0; // ms + auto shutdown_timeout = 0ms; if (!SHUTDOWN_ZERO_TIMEOUT) { if (is_thermal_shutdown) { - constexpr unsigned int thermal_shutdown_timeout = 1; // sec - shutdown_timeout = thermal_shutdown_timeout * 1000; + constexpr unsigned int thermal_shutdown_timeout = 1; + shutdown_timeout = std::chrono::seconds(thermal_shutdown_timeout); } else { - constexpr unsigned int shutdown_timeout_default = 6; // sec + constexpr unsigned int shutdown_timeout_default = 6; auto shutdown_timeout_property = android::base::GetUintProperty( "ro.build.shutdown_timeout", shutdown_timeout_default); - shutdown_timeout = shutdown_timeout_property * 1000; + shutdown_timeout = std::chrono::seconds(shutdown_timeout_property); } } - LOG(INFO) << "Shutdown timeout: " << shutdown_timeout << " ms"; + LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms"; // keep debugging tools until non critical ones are all gone. const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"}; @@ -358,7 +394,7 @@ // optional shutdown step // 1. terminate all services except shutdown critical ones. wait for delay to finish - if (shutdown_timeout > 0) { + if (shutdown_timeout > 0ms) { LOG(INFO) << "terminating init services"; // Ask all services to terminate except shutdown critical ones. @@ -369,7 +405,7 @@ int service_count = 0; // Only wait up to half of timeout here auto termination_wait_timeout = shutdown_timeout / 2; - while (t.duration_ms() < termination_wait_timeout) { + while (t.duration() < termination_wait_timeout) { ServiceManager::GetInstance().ReapAnyOutstandingChildren(); service_count = 0; @@ -417,7 +453,7 @@ }); // 4. sync, try umount, and optionally run fsck for user shutdown sync(); - UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration_ms()); + UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration()); // Follow what linux shutdown is doing: one more sync with little bit delay sync(); if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms); @@ -495,3 +531,6 @@ return true; } + +} // namespace init +} // namespace android
diff --git a/init/reboot.h b/init/reboot.h index b304b3c..e559540 100644 --- a/init/reboot.h +++ b/init/reboot.h
@@ -19,6 +19,9 @@ #include <string> +namespace android { +namespace init { + /* Reboot / shutdown the system. * cmd ANDROID_RB_* as defined in android_reboot.h * reason Reason string like "reboot", "userrequested" @@ -32,4 +35,7 @@ // Parses and handles a setprop sys.powerctl message. bool HandlePowerctlMessage(const std::string& command); +} // namespace init +} // namespace android + #endif
diff --git a/init/service.cpp b/init/service.cpp index 09ebc31..f5e54df 100644 --- a/init/service.cpp +++ b/init/service.cpp
@@ -34,6 +34,7 @@ #include <android-base/logging.h> #include <android-base/parseint.h> #include <android-base/properties.h> +#include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <processgroup/processgroup.h> @@ -45,10 +46,17 @@ #include "util.h" using android::base::boot_clock; +using android::base::GetProperty; +using android::base::Join; +using android::base::make_scope_guard; using android::base::ParseInt; +using android::base::StartsWith; using android::base::StringPrintf; using android::base::WriteStringToFile; +namespace android { +namespace init { + static std::string ComputeContextFromExecutable(std::string& service_name, const std::string& service_path) { std::string computed_context; @@ -126,17 +134,21 @@ } } -static void ExpandArgs(const std::vector<std::string>& args, std::vector<char*>* strs) { +static bool ExpandArgsAndExecve(const std::vector<std::string>& args) { std::vector<std::string> expanded_args; + std::vector<char*> c_strings; + expanded_args.resize(args.size()); - strs->push_back(const_cast<char*>(args[0].c_str())); + c_strings.push_back(const_cast<char*>(args[0].data())); for (std::size_t i = 1; i < args.size(); ++i) { if (!expand_props(args[i], &expanded_args[i])) { LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'"; } - strs->push_back(const_cast<char*>(expanded_args[i].c_str())); + c_strings.push_back(expanded_args[i].data()); } - strs->push_back(nullptr); + c_strings.push_back(nullptr); + + return execve(c_strings[0], c_strings.data(), (char**)ENV) == 0; } ServiceEnvironmentInfo::ServiceEnvironmentInfo() { @@ -163,6 +175,9 @@ ioprio_pri_(0), priority_(0), oom_score_adjust_(-1000), + swappiness_(-1), + soft_limit_in_bytes_(-1), + limit_in_bytes_(-1), args_(args) { onrestart_.InitSingleTrigger("onrestart"); } @@ -188,6 +203,9 @@ ioprio_pri_(0), priority_(0), oom_score_adjust_(-1000), + swappiness_(-1), + soft_limit_in_bytes_(-1), + limit_in_bytes_(-1), args_(args) { onrestart_.InitSingleTrigger("onrestart"); } @@ -198,13 +216,12 @@ return; } - std::string prop_name = StringPrintf("init.svc.%s", name_.c_str()); - property_set(prop_name.c_str(), new_state.c_str()); + std::string prop_name = "init.svc." + name_; + property_set(prop_name, new_state); if (new_state == "running") { uint64_t start_ns = time_started_.time_since_epoch().count(); - property_set(StringPrintf("ro.boottime.%s", name_.c_str()).c_str(), - StringPrintf("%" PRIu64, start_ns).c_str()); + property_set("ro.boottime." + name_, std::to_string(start_ns)); } } @@ -230,8 +247,15 @@ void Service::SetProcessAttributes() { // Keep capabilites on uid change. if (capabilities_.any() && uid_) { - if (prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS | SECBIT_KEEP_CAPS_LOCKED) != 0) { - PLOG(FATAL) << "prtcl(PR_SET_KEEPCAPS) failed for " << name_; + // If Android is running in a container, some securebits might already + // be locked, so don't change those. + unsigned long securebits = prctl(PR_GET_SECUREBITS); + if (securebits == -1UL) { + PLOG(FATAL) << "prctl(PR_GET_SECUREBITS) failed for " << name_; + } + securebits |= SECBIT_KEEP_CAPS | SECBIT_KEEP_CAPS_LOCKED; + if (prctl(PR_SET_SECUREBITS, securebits) != 0) { + PLOG(FATAL) << "prctl(PR_SET_SECUREBITS) failed for " << name_; } } @@ -322,8 +346,8 @@ void Service::DumpState() const { LOG(INFO) << "service " << name_; - LOG(INFO) << " class '" << android::base::Join(classnames_, " ") << "'"; - LOG(INFO) << " exec "<< android::base::Join(args_, " "); + LOG(INFO) << " class '" << Join(classnames_, " ") << "'"; + LOG(INFO) << " exec " << Join(args_, " "); std::for_each(descriptors_.begin(), descriptors_.end(), [] (const auto& info) { LOG(INFO) << *info; }); } @@ -477,6 +501,30 @@ return true; } +bool Service::ParseMemcgSwappiness(const std::vector<std::string>& args, std::string* err) { + if (!ParseInt(args[1], &swappiness_, 0)) { + *err = "swappiness value must be equal or greater than 0"; + return false; + } + return true; +} + +bool Service::ParseMemcgLimitInBytes(const std::vector<std::string>& args, std::string* err) { + if (!ParseInt(args[1], &limit_in_bytes_, 0)) { + *err = "limit_in_bytes value must be equal or greater than 0"; + return false; + } + return true; +} + +bool Service::ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args, std::string* err) { + if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) { + *err = "soft_limit_in_bytes value must be equal or greater than 0"; + return false; + } + return true; +} + bool Service::ParseSeclabel(const std::vector<std::string>& args, std::string* err) { seclabel_ = args[1]; return true; @@ -534,9 +582,8 @@ // name type perm [ uid gid context ] bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) { - if (!android::base::StartsWith(args[2], "dgram") && - !android::base::StartsWith(args[2], "stream") && - !android::base::StartsWith(args[2], "seqpacket")) { + if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") && + !StartsWith(args[2], "seqpacket")) { *err = "socket type must be 'dgram', 'stream' or 'seqpacket'"; return false; } @@ -596,6 +643,12 @@ {"onrestart", {1, kMax, &Service::ParseOnrestart}}, {"oom_score_adjust", {1, 1, &Service::ParseOomScoreAdjust}}, + {"memcg.swappiness", + {1, 1, &Service::ParseMemcgSwappiness}}, + {"memcg.soft_limit_in_bytes", + {1, 1, &Service::ParseMemcgSoftLimitInBytes}}, + {"memcg.limit_in_bytes", + {1, 1, &Service::ParseMemcgLimitInBytes}}, {"namespace", {1, 2, &Service::ParseNamespace}}, {"seclabel", {1, 1, &Service::ParseSeclabel}}, {"setenv", {2, 2, &Service::ParseSetenv}}, @@ -620,10 +673,10 @@ return (this->*parser)(args, err); } -bool Service::ExecStart(std::unique_ptr<Timer>* exec_waiter) { +bool Service::ExecStart(std::unique_ptr<android::base::Timer>* exec_waiter) { flags_ |= SVC_EXEC | SVC_ONESHOT; - exec_waiter->reset(new Timer); + exec_waiter->reset(new android::base::Timer); if (!Start()) { exec_waiter->reset(); @@ -705,13 +758,13 @@ // See if there were "writepid" instructions to write to files under /dev/cpuset/. auto cpuset_predicate = [](const std::string& path) { - return android::base::StartsWith(path, "/dev/cpuset/"); + return StartsWith(path, "/dev/cpuset/"); }; auto iter = std::find_if(writepid_files_.begin(), writepid_files_.end(), cpuset_predicate); if (iter == writepid_files_.end()) { // There were no "writepid" instructions for cpusets, check if the system default // cpuset is specified to be used for the process. - std::string default_cpuset = android::base::GetProperty("ro.cpuset.default", ""); + std::string default_cpuset = GetProperty("ro.cpuset.default", ""); if (!default_cpuset.empty()) { // Make sure the cpuset name starts and ends with '/'. // A single '/' means the 'root' cpuset. @@ -725,7 +778,7 @@ StringPrintf("/dev/cpuset%stasks", default_cpuset.c_str())); } } - std::string pid_str = StringPrintf("%d", getpid()); + std::string pid_str = std::to_string(getpid()); for (const auto& file : writepid_files_) { if (!WriteStringToFile(pid_str, file)) { PLOG(ERROR) << "couldn't write " << pid_str << " to " << file; @@ -750,10 +803,8 @@ // priority. Aborts on failure. SetProcessAttributes(); - std::vector<char*> strs; - ExpandArgs(args_, &strs); - if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) { - PLOG(ERROR) << "cannot execve('" << strs[0] << "')"; + if (!ExpandArgsAndExecve(args_)) { + PLOG(ERROR) << "cannot execve('" << args_[0] << "')"; } _exit(127); @@ -766,7 +817,7 @@ } if (oom_score_adjust_ != -1000) { - std::string oom_str = StringPrintf("%d", oom_score_adjust_); + std::string oom_str = std::to_string(oom_score_adjust_); std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid); if (!WriteStringToFile(oom_str, oom_file)) { PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno); @@ -782,12 +833,30 @@ if (errno != 0) { PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '" << name_ << "'"; + } else { + if (swappiness_ != -1) { + if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) { + PLOG(ERROR) << "setProcessGroupSwappiness failed"; + } + } + + if (soft_limit_in_bytes_ != -1) { + if (!setProcessGroupSoftLimit(uid_, pid_, soft_limit_in_bytes_)) { + PLOG(ERROR) << "setProcessGroupSoftLimit failed"; + } + } + + if (limit_in_bytes_ != -1) { + if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) { + PLOG(ERROR) << "setProcessGroupLimit failed"; + } + } } if ((flags_ & SVC_EXEC) != 0) { - LOG(INFO) << android::base::StringPrintf( - "SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...", pid_, uid_, gid_, - supp_gids_.size(), !seclabel_.empty() ? seclabel_.c_str() : "default"); + LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+" + << supp_gids_.size() << " context " + << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting..."; } NotifyStateChange("running"); @@ -964,8 +1033,7 @@ std::vector<std::string> str_args(args.begin() + command_arg, args.end()); exec_count_++; - std::string name = - "exec " + std::to_string(exec_count_) + " (" + android::base::Join(str_args, " ") + ")"; + std::string name = "exec " + std::to_string(exec_count_) + " (" + Join(str_args, " ") + ")"; unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY; CapSet no_capabilities; @@ -1087,13 +1155,25 @@ } bool ServiceManager::ReapOneProcess() { - int status; - pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG)); - if (pid == 0) { + siginfo_t siginfo = {}; + // This returns a zombie pid or informs us that there are no zombies left to be reaped. + // It does NOT reap the pid; that is done below. + if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) { + PLOG(ERROR) << "waitid failed"; return false; - } else if (pid == -1) { - PLOG(ERROR) << "waitpid failed"; - return false; + } + + auto pid = siginfo.si_pid; + if (pid == 0) return false; + + // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid + // whenever the function returns from this point forward. + // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we + // want the pid to remain valid throughout that (and potentially future) usages. + auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); }); + + if (PropertyChildReap(pid)) { + return true; } Service* svc = FindServiceByPid(pid); @@ -1101,24 +1181,20 @@ std::string name; std::string wait_string; if (svc) { - name = android::base::StringPrintf("Service '%s' (pid %d)", - svc->name().c_str(), pid); + name = StringPrintf("Service '%s' (pid %d)", svc->name().c_str(), pid); if (svc->flags() & SVC_EXEC) { - wait_string = - android::base::StringPrintf(" waiting took %f seconds", exec_waiter_->duration_s()); + wait_string = StringPrintf(" waiting took %f seconds", + exec_waiter_->duration().count() / 1000.0f); } } else { - name = android::base::StringPrintf("Untracked pid %d", pid); + name = StringPrintf("Untracked pid %d", pid); } + auto status = siginfo.si_status; if (WIFEXITED(status)) { LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string; } else if (WIFSIGNALED(status)) { LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string; - } else if (WIFSTOPPED(status)) { - LOG(INFO) << name << " stopped by signal " << WSTOPSIG(status) << wait_string; - } else { - LOG(INFO) << name << " state changed" << wait_string; } if (!svc) { @@ -1192,3 +1268,6 @@ // the service name to the "ctl.start" and "ctl.stop" properties.) return is_legal_property_name("init.svc." + name) && name.size() <= PROP_VALUE_MAX; } + +} // namespace init +} // namespace android
diff --git a/init/service.h b/init/service.h index 25d1975..62a3299 100644 --- a/init/service.h +++ b/init/service.h
@@ -32,7 +32,6 @@ #include "descriptors.h" #include "init_parser.h" #include "keyword_map.h" -#include "util.h" #define SVC_DISABLED 0x001 // do not autostart with class #define SVC_ONESHOT 0x002 // do not restart on exit @@ -55,8 +54,8 @@ #define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups -class Action; -class ServiceManager; +namespace android { +namespace init { struct ServiceEnvironmentInfo { ServiceEnvironmentInfo(); @@ -76,7 +75,7 @@ bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; } bool ParseLine(const std::vector<std::string>& args, std::string* err); - bool ExecStart(std::unique_ptr<Timer>* exec_waiter); + bool ExecStart(std::unique_ptr<android::base::Timer>* exec_waiter); bool Start(); bool StartIfNotDisabled(); bool Enable(); @@ -135,6 +134,9 @@ bool ParseOneshot(const std::vector<std::string>& args, std::string* err); bool ParseOnrestart(const std::vector<std::string>& args, std::string* err); bool ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err); + bool ParseMemcgLimitInBytes(const std::vector<std::string>& args, std::string* err); + bool ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args, std::string* err); + bool ParseMemcgSwappiness(const std::vector<std::string>& args, std::string* err); bool ParseNamespace(const std::vector<std::string>& args, std::string* err); bool ParseSeclabel(const std::vector<std::string>& args, std::string* err); bool ParseSetenv(const std::vector<std::string>& args, std::string* err); @@ -182,6 +184,10 @@ int oom_score_adjust_; + int swappiness_; + int soft_limit_in_bytes_; + int limit_in_bytes_; + bool process_cgroup_empty_ = false; std::vector<std::string> args_; @@ -218,7 +224,7 @@ bool ReapOneProcess(); static int exec_count_; // Every service needs a unique name. - std::unique_ptr<Timer> exec_waiter_; + std::unique_ptr<android::base::Timer> exec_waiter_; std::vector<std::unique_ptr<Service>> services_; }; @@ -239,4 +245,7 @@ std::unique_ptr<Service> service_; }; +} // namespace init +} // namespace android + #endif
diff --git a/init/service_test.cpp b/init/service_test.cpp index b9c4627..44f28a3 100644 --- a/init/service_test.cpp +++ b/init/service_test.cpp
@@ -23,6 +23,9 @@ #include <gtest/gtest.h> +namespace android { +namespace init { + TEST(service, pod_initialized) { constexpr auto memory_size = sizeof(Service); alignas(alignof(Service)) char old_memory[memory_size]; @@ -67,3 +70,6 @@ EXPECT_EQ(-1000, service_in_old_memory2->oom_score_adjust()); EXPECT_FALSE(service_in_old_memory->process_cgroup_empty()); } + +} // namespace init +} // namespace android
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp index 4d56d84..db1bfcf 100644 --- a/init/signal_handler.cpp +++ b/init/signal_handler.cpp
@@ -21,11 +21,13 @@ #include <unistd.h> #include <android-base/logging.h> -#include <android-base/stringprintf.h> #include "init.h" #include "service.h" +namespace android { +namespace init { + static int signal_write_fd = -1; static int signal_read_fd = -1; @@ -65,3 +67,6 @@ register_epoll_handler(signal_read_fd, handle_signal); } + +} // namespace init +} // namespace android
diff --git a/init/signal_handler.h b/init/signal_handler.h index 449b4af..f7881ab 100644 --- a/init/signal_handler.h +++ b/init/signal_handler.h
@@ -17,6 +17,12 @@ #ifndef _INIT_SIGNAL_HANDLER_H_ #define _INIT_SIGNAL_HANDLER_H_ +namespace android { +namespace init { + void signal_handler_init(void); +} // namespace init +} // namespace android + #endif
diff --git a/init/uevent.h b/init/uevent.h index 1095665..c4fd945 100644 --- a/init/uevent.h +++ b/init/uevent.h
@@ -19,6 +19,9 @@ #include <string> +namespace android { +namespace init { + struct Uevent { std::string action; std::string path; @@ -31,4 +34,7 @@ int minor; }; +} // namespace init +} // namespace android + #endif
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp index 923fa8e..ac1d7c7 100644 --- a/init/uevent_listener.cpp +++ b/init/uevent_listener.cpp
@@ -26,6 +26,9 @@ #include <android-base/logging.h> #include <cutils/uevent.h> +namespace android { +namespace init { + static void ParseEvent(const char* msg, Uevent* uevent) { uevent->partition_num = -1; uevent->major = -1; @@ -165,7 +168,7 @@ return RegenerateUeventsForDir(d.get(), callback); } -const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"}; +static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"}; void UeventListener::RegenerateUevents(const ListenerCallback& callback) const { for (const auto path : kRegenerationPaths) { @@ -212,3 +215,6 @@ } } } + +} // namespace init +} // namespace android
diff --git a/init/uevent_listener.h b/init/uevent_listener.h index 1964688..5b453fe 100644 --- a/init/uevent_listener.h +++ b/init/uevent_listener.h
@@ -29,6 +29,9 @@ #define UEVENT_MSG_LEN 2048 +namespace android { +namespace init { + enum class ListenerAction { kStop = 0, // Stop regenerating uevents as we've handled the one(s) we're interested in. kContinue, // Continue regenerating uevents as we haven't seen the one(s) we're interested in. @@ -36,8 +39,6 @@ using ListenerCallback = std::function<ListenerAction(const Uevent&)>; -extern const char* kRegenerationPaths[3]; - class UeventListener { public: UeventListener(); @@ -55,4 +56,7 @@ android::base::unique_fd device_fd_; }; +} // namespace init +} // namespace android + #endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp index ff64e8e..c0eae1e 100644 --- a/init/ueventd.cpp +++ b/init/ueventd.cpp
@@ -27,9 +27,9 @@ #include <set> #include <thread> +#include <android-base/chrono_utils.h> #include <android-base/logging.h> #include <android-base/properties.h> -#include <android-base/stringprintf.h> #include <selinux/android.h> #include <selinux/selinux.h> @@ -100,6 +100,9 @@ // the uevent listener resumes in polling mode and will handle the uevents that occurred during // coldboot. +namespace android { +namespace init { + class ColdBoot { public: ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler) @@ -158,9 +161,7 @@ } void ColdBoot::DoRestoreCon() { - for (const char* path : kRegenerationPaths) { - selinux_android_restorecon(path, SELINUX_ANDROID_RESTORECON_RECURSE); - } + selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE); device_handler_.set_skip_restorecon(false); } @@ -198,7 +199,7 @@ } void ColdBoot::Run() { - Timer cold_boot_timer; + android::base::Timer cold_boot_timer; RegenerateUevents(); @@ -209,7 +210,7 @@ WaitForSubProcesses(); close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); - LOG(INFO) << "Coldboot took " << cold_boot_timer; + LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds"; } DeviceHandler CreateDeviceHandler() { @@ -268,6 +269,13 @@ cold_boot.Run(); } + // We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now. + signal(SIGCHLD, SIG_IGN); + // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN + // for SIGCHLD above. + while (waitpid(-1, nullptr, WNOHANG) > 0) { + } + uevent_listener.Poll([&device_handler](const Uevent& uevent) { HandleFirmwareEvent(uevent); device_handler.HandleDeviceEvent(uevent); @@ -276,3 +284,6 @@ return 0; } + +} // namespace init +} // namespace android
diff --git a/init/ueventd.h b/init/ueventd.h index 1f424d3..51775ec 100644 --- a/init/ueventd.h +++ b/init/ueventd.h
@@ -17,6 +17,12 @@ #ifndef _INIT_UEVENTD_H_ #define _INIT_UEVENTD_H_ +namespace android { +namespace init { + int ueventd_main(int argc, char** argv); +} // namespace init +} // namespace android + #endif
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp index 7156e76..02e0d42 100644 --- a/init/ueventd_parser.cpp +++ b/init/ueventd_parser.cpp
@@ -21,6 +21,9 @@ #include "keyword_map.h" +namespace android { +namespace init { + bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, std::vector<SysfsPermissions>* out_sysfs_permissions, std::vector<Permissions>* out_dev_permissions) { @@ -143,3 +146,6 @@ void SubsystemParser::EndSection() { subsystems_->emplace_back(std::move(subsystem_)); } + +} // namespace init +} // namespace android
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h index c1ce976..592df63 100644 --- a/init/ueventd_parser.h +++ b/init/ueventd_parser.h
@@ -23,6 +23,9 @@ #include "devices.h" #include "init_parser.h" +namespace android { +namespace init { + class SubsystemParser : public SectionParser { public: SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {} @@ -43,4 +46,7 @@ std::vector<SysfsPermissions>* out_sysfs_permissions, std::vector<Permissions>* out_dev_permissions); +} // namespace init +} // namespace android + #endif
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp new file mode 100644 index 0000000..4d9a1fa --- /dev/null +++ b/init/ueventd_test.cpp
@@ -0,0 +1,122 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <linux/futex.h> +#include <pthread.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <string> +#include <thread> +#include <vector> + +#include <android-base/file.h> +#include <android-base/scopeguard.h> +#include <android-base/test_utils.h> +#include <gtest/gtest.h> +#include <selinux/selinux.h> + +using namespace std::string_literals; + +template <typename T, typename F> +void WriteFromMultipleThreads(std::vector<std::pair<std::string, T>>& files_and_parameters, + F function) { + auto num_threads = files_and_parameters.size(); + pthread_barrier_t barrier; + pthread_barrier_init(&barrier, nullptr, num_threads); + auto barrier_destroy = + android::base::make_scope_guard([&barrier]() { pthread_barrier_destroy(&barrier); }); + + auto make_thread_function = [&function, &barrier](const auto& file, const auto& parameter) { + return [&]() { + function(parameter); + pthread_barrier_wait(&barrier); + android::base::WriteStringToFile("<empty>", file); + }; + }; + + std::vector<std::thread> threads; + // TODO(b/63712782): Structured bindings + templated containers are broken in clang :( + // for (const auto& [file, parameter] : files_and_parameters) { + for (const auto& pair : files_and_parameters) { + const auto& file = pair.first; + const auto& parameter = pair.second; + threads.emplace_back(std::thread(make_thread_function(file, parameter))); + } + + for (auto& thread : threads) { + thread.join(); + } +} + +TEST(ueventd, setegid_IsPerThread) { + if (getuid() != 0) { + GTEST_LOG_(INFO) << "Skipping test, must be run as root."; + return; + } + + TemporaryDir dir; + + gid_t gid = 0; + std::vector<std::pair<std::string, gid_t>> files_and_gids; + std::generate_n(std::back_inserter(files_and_gids), 100, [&gid, &dir]() { + gid++; + return std::pair(dir.path + "/gid_"s + std::to_string(gid), gid); + }); + + WriteFromMultipleThreads(files_and_gids, [](gid_t gid) { EXPECT_EQ(0, setegid(gid)); }); + + for (const auto& [file, expected_gid] : files_and_gids) { + struct stat info; + ASSERT_EQ(0, stat(file.c_str(), &info)); + EXPECT_EQ(expected_gid, info.st_gid); + } +} + +TEST(ueventd, setfscreatecon_IsPerThread) { + if (getuid() != 0) { + GTEST_LOG_(INFO) << "Skipping test, must be run as root."; + return; + } + if (!is_selinux_enabled() || security_getenforce() == 1) { + GTEST_LOG_(INFO) << "Skipping test, SELinux must be enabled and in permissive mode."; + return; + } + + const char* const contexts[] = { + "u:object_r:audio_device:s0", + "u:object_r:sensors_device:s0", + "u:object_r:video_device:s0" + "u:object_r:zero_device:s0", + }; + + TemporaryDir dir; + std::vector<std::pair<std::string, std::string>> files_and_contexts; + for (const char* context : contexts) { + files_and_contexts.emplace_back(dir.path + "/context_"s + context, context); + } + + WriteFromMultipleThreads(files_and_contexts, [](const std::string& context) { + EXPECT_EQ(0, setfscreatecon(context.c_str())); + }); + + for (const auto& [file, expected_context] : files_and_contexts) { + char* file_context; + ASSERT_GT(getfilecon(file.c_str(), &file_context), 0); + EXPECT_EQ(expected_context, file_context); + freecon(file_context); + } +}
diff --git a/init/util.cpp b/init/util.cpp index dfdfeb4..fdcb22d 100644 --- a/init/util.cpp +++ b/init/util.cpp
@@ -50,6 +50,11 @@ using android::base::boot_clock; using namespace std::literals::string_literals; +namespace android { +namespace init { + +const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/"); + // DecodeUid() - decodes and returns the given string, which can be either the // numeric or name representation, into the integer uid or gid. Returns // UINT_MAX on error. @@ -371,15 +376,31 @@ DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false); } -std::ostream& operator<<(std::ostream& os, const Timer& t) { - os << t.duration_s() << " seconds"; - return os; +static std::string init_android_dt_dir() { + // Use the standard procfs-based path by default + std::string android_dt_dir = kDefaultAndroidDtDir; + // The platform may specify a custom Android DT path in kernel cmdline + import_kernel_cmdline(false, + [&](const std::string& key, const std::string& value, bool in_qemu) { + if (key == "androidboot.android_dt_dir") { + android_dt_dir = value; + } + }); + LOG(INFO) << "Using Android DT directory " << android_dt_dir; + return android_dt_dir; } -// Reads the content of device tree file under kAndroidDtDir directory. +// FIXME: The same logic is duplicated in system/core/fs_mgr/ +const std::string& get_android_dt_dir() { + // Set once and saves time for subsequent calls to this function + static const std::string kAndroidDtDir = init_android_dt_dir(); + return kAndroidDtDir; +} + +// Reads the content of device tree file under the platform's Android DT directory. // Returns true if the read is success, false otherwise. bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) { - const std::string file_name = kAndroidDtDir + sub_path; + const std::string file_name = get_android_dt_dir() + sub_path; if (android::base::ReadFileToString(file_name, dt_content)) { if (!dt_content->empty()) { dt_content->pop_back(); // Trims the trailing '\0' out. @@ -398,3 +419,6 @@ } return false; } + +} // namespace init +} // namespace android
diff --git a/init/util.h b/init/util.h index 1ad6b77..29c10cb 100644 --- a/init/util.h +++ b/init/util.h
@@ -30,37 +30,18 @@ #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; +namespace android { +namespace init { + int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid, const char* socketcon, selabel_handle* sehandle); bool ReadFile(const std::string& path, std::string* content, std::string* err); bool WriteFile(const std::string& path, const std::string& content, std::string* err); -class Timer { - public: - Timer() : start_(boot_clock::now()) {} - - double duration_s() const { - typedef std::chrono::duration<double> double_duration; - return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count(); - } - - int64_t duration_ms() const { - return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_) - .count(); - } - - private: - android::base::boot_clock::time_point start_; -}; - -std::ostream& operator<<(std::ostream& os, const Timer& t); - bool DecodeUid(const std::string& name, uid_t* uid, std::string* err); int mkdir_recursive(const std::string& pathname, mode_t mode, selabel_handle* sehandle); @@ -74,8 +55,14 @@ void panic() __attribute__((__noreturn__)); -// Reads or compares the content of device tree file under kAndroidDtDir directory. +// Returns the platform's Android DT directory as specified in the kernel cmdline. +// If the platform does not configure a custom DT path, returns the standard one (based in procfs). +const std::string& get_android_dt_dir(); +// Reads or compares the content of device tree file under the platform's Android DT directory. bool read_android_dt_file(const std::string& sub_path, std::string* dt_content); bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content); +} // namespace init +} // namespace android + #endif
diff --git a/init/util_test.cpp b/init/util_test.cpp index 4bb8a83..c16ab74 100644 --- a/init/util_test.cpp +++ b/init/util_test.cpp
@@ -26,6 +26,9 @@ using namespace std::literals::string_literals; +namespace android { +namespace init { + TEST(util, ReadFile_ENOENT) { std::string s("hello"); std::string err; @@ -187,3 +190,6 @@ std::string path3 = android::base::StringPrintf("%s/three/directories/deep", test_dir.path); EXPECT_TRUE(is_dir(path1.c_str())); } + +} // namespace init +} // namespace android
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp index 7baa487..e0164b4 100644 --- a/init/watchdogd.cpp +++ b/init/watchdogd.cpp
@@ -31,6 +31,9 @@ #define DEV_NAME "/dev/watchdog" +namespace android { +namespace init { + int watchdogd_main(int argc, char **argv) { InitKernelLogging(argv); @@ -73,3 +76,6 @@ sleep(interval); } } + +} // namespace init +} // namespace android
diff --git a/init/watchdogd.h b/init/watchdogd.h index 8b48ab8..73f77d5 100644 --- a/init/watchdogd.h +++ b/init/watchdogd.h
@@ -17,6 +17,12 @@ #ifndef _INIT_WATCHDOGD_H_ #define _INIT_WATCHDOGD_H_ +namespace android { +namespace init { + int watchdogd_main(int argc, char **argv); +} // namespace init +} // namespace android + #endif
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc index 0792307..8b0c53e 100644 --- a/libappfuse/FuseBridgeLoop.cc +++ b/libappfuse/FuseBridgeLoop.cc
@@ -179,7 +179,11 @@ } const uint32_t opcode = buffer_.request.header.opcode; - LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode; + const uint64_t unique = buffer_.request.header.unique; + LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode << " unique=" << unique; + if (unique == 0) { + return FuseBridgeState::kWaitToReadEither; + } switch (opcode) { case FUSE_FORGET: // Do not reply to FUSE_FORGET.
diff --git a/libappfuse/tests/FuseBridgeLoopTest.cc b/libappfuse/tests/FuseBridgeLoopTest.cc index 51d6051..0a28451 100644 --- a/libappfuse/tests/FuseBridgeLoopTest.cc +++ b/libappfuse/tests/FuseBridgeLoopTest.cc
@@ -67,6 +67,7 @@ memset(&request_, 0, sizeof(FuseRequest)); request_.header.opcode = opcode; request_.header.len = sizeof(fuse_in_header); + request_.header.unique = 1; ASSERT_TRUE(request_.Write(dev_sockets_[0])); memset(&response_, 0, sizeof(FuseResponse));
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp index a643a29..7eefc95 100644 --- a/libbacktrace/Android.bp +++ b/libbacktrace/Android.bp
@@ -64,6 +64,10 @@ cc_library { name: "libbacktrace", vendor_available: true, + vndk: { + enabled: true, + support_system_process: true, + }, defaults: ["libbacktrace_common"], host_supported: true, @@ -161,6 +165,7 @@ shared_libs = [ "libbase", "libunwind", + "libziparchive", ], }
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp index e46d353..81f5e32 100644 --- a/libbacktrace/Backtrace.cpp +++ b/libbacktrace/Backtrace.cpp
@@ -84,10 +84,8 @@ } std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) { - uintptr_t relative_pc; std::string map_name; if (BacktraceMap::IsValid(frame->map)) { - relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc); if (!frame->map.name.empty()) { map_name = frame->map.name.c_str(); if (map_name[0] == '[' && map_name[map_name.size() - 1] == ']') { @@ -99,10 +97,9 @@ } } else { map_name = "<unknown>"; - relative_pc = frame->pc; } - std::string line(StringPrintf("#%02zu pc %" PRIPTR " ", frame->num, relative_pc)); + std::string line(StringPrintf("#%02zu pc %" PRIPTR " ", frame->num, frame->rel_pc)); line += map_name; // Special handling for non-zero offset maps, we need to print that // information.
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp index 3c509e6..2c87fa8 100644 --- a/libbacktrace/UnwindCurrent.cpp +++ b/libbacktrace/UnwindCurrent.cpp
@@ -133,6 +133,11 @@ backtrace_frame_data_t* prev = &frames_.at(num_frames-1); prev->stack_size = frame->sp - prev->sp; } + if (BacktraceMap::IsValid(frame->map)) { + frame->rel_pc = frame->pc - frame->map.start + frame->map.load_bias; + } else { + frame->rel_pc = frame->pc; + } num_frames++; } else { num_ignore_frames--;
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp index af79562..0b8232b 100644 --- a/libbacktrace/UnwindMap.cpp +++ b/libbacktrace/UnwindMap.cpp
@@ -57,7 +57,7 @@ map.start = unw_map.start; map.end = unw_map.end; map.offset = unw_map.offset; - map.load_base = unw_map.load_base; + map.load_bias = unw_map.load_base; map.flags = unw_map.flags; map.name = unw_map.path; @@ -106,7 +106,7 @@ map.start = unw_map.start; map.end = unw_map.end; map.offset = unw_map.offset; - map.load_base = unw_map.load_base; + map.load_bias = unw_map.load_base; map.flags = unw_map.flags; map.name = unw_map.path;
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp index 42ac1bc..87282ef 100644 --- a/libbacktrace/UnwindPtrace.cpp +++ b/libbacktrace/UnwindPtrace.cpp
@@ -135,6 +135,11 @@ } FillInMap(frame->pc, &frame->map); + if (BacktraceMap::IsValid(frame->map)) { + frame->rel_pc = frame->pc - frame->map.start + frame->map.load_bias; + } else { + frame->rel_pc = frame->pc; + } frame->func_name = GetFunctionName(frame->pc, &frame->func_offset, &frame->map);
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp index 465b3f9..16b1d79 100644 --- a/libbacktrace/backtrace_offline_test.cpp +++ b/libbacktrace/backtrace_offline_test.cpp
@@ -167,9 +167,9 @@ // 2. Dump maps for (auto it = map->begin(); it != map->end(); ++it) { testdata += android::base::StringPrintf( - "map: start: %" PRIxPTR " end: %" PRIxPTR " offset: %" PRIxPTR - " load_base: %" PRIxPTR " flags: %d name: %s\n", - it->start, it->end, it->offset, it->load_base, it->flags, it->name.c_str()); + "map: start: %" PRIxPTR " end: %" PRIxPTR " offset: %" PRIxPTR " load_bias: %" PRIxPTR + " flags: %d name: %s\n", + it->start, it->end, it->offset, it->load_bias, it->flags, it->name.c_str()); } // 3. Dump registers testdata += android::base::StringPrintf("registers: %zu ", sizeof(arg.unw_context)); @@ -246,9 +246,9 @@ backtrace_map_t& map = testdata->maps.back(); int pos; sscanf(line.c_str(), - "map: start: %" SCNxPTR " end: %" SCNxPTR " offset: %" SCNxPTR - " load_base: %" SCNxPTR " flags: %d name: %n", - &map.start, &map.end, &map.offset, &map.load_base, &map.flags, &pos); + "map: start: %" SCNxPTR " end: %" SCNxPTR " offset: %" SCNxPTR " load_bias: %" SCNxPTR + " flags: %d name: %n", + &map.start, &map.end, &map.offset, &map.load_bias, &map.flags, &pos); map.name = android::base::Trim(line.substr(pos)); } else if (android::base::StartsWith(line, "registers:")) { size_t size; @@ -392,8 +392,8 @@ // The last frame is outside of libart.so ASSERT_EQ(testdata.symbols.size() + 1, backtrace->NumFrames()); for (size_t i = 0; i + 1 < backtrace->NumFrames(); ++i) { - uintptr_t vaddr_in_file = backtrace->GetFrame(i)->pc - testdata.maps[0].start + - testdata.maps[0].load_base; + uintptr_t vaddr_in_file = + backtrace->GetFrame(i)->pc - testdata.maps[0].start + testdata.maps[0].load_bias; std::string name = FunctionNameForAddress(vaddr_in_file, testdata.symbols); ASSERT_EQ(name, testdata.symbols[i].name); }
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp index fb463b0..1ec6a45 100644 --- a/libbacktrace/backtrace_test.cpp +++ b/libbacktrace/backtrace_test.cpp
@@ -784,6 +784,7 @@ backtrace_frame_data_t frame; frame.num = 1; frame.pc = 2; + frame.rel_pc = 2; frame.sp = 0; frame.stack_size = 0; frame.func_offset = 0; @@ -799,9 +800,10 @@ // Check map name empty, but exists. frame.pc = 0xb0020; + frame.rel_pc = 0x20; frame.map.start = 0xb0000; frame.map.end = 0xbffff; - frame.map.load_base = 0; + frame.map.load_bias = 0; #if defined(__LP64__) EXPECT_EQ("#01 pc 0000000000000020 <anonymous:00000000000b0000>", #else @@ -813,7 +815,7 @@ frame.pc = 0xc0020; frame.map.start = 0xc0000; frame.map.end = 0xcffff; - frame.map.load_base = 0; + frame.map.load_bias = 0; frame.map.name = "[anon:thread signal stack]"; #if defined(__LP64__) EXPECT_EQ("#01 pc 0000000000000020 [anon:thread signal stack:00000000000c0000]", @@ -824,6 +826,7 @@ // Check relative pc is set and map name is set. frame.pc = 0x12345679; + frame.rel_pc = 0x12345678; frame.map.name = "MapFake"; frame.map.start = 1; frame.map.end = 1; @@ -852,9 +855,10 @@ #endif backtrace->FormatFrameData(&frame)); - // Check func_name is set, func offset is non-zero, and load_base is non-zero. + // Check func_name is set, func offset is non-zero, and load_bias is non-zero. + frame.rel_pc = 0x123456dc; frame.func_offset = 645; - frame.map.load_base = 100; + frame.map.load_bias = 100; #if defined(__LP64__) EXPECT_EQ("#01 pc 00000000123456dc MapFake (ProcFake+645)", #else @@ -1737,9 +1741,13 @@ FinishRemoteProcess(pid); } -TEST(libbacktrace, unwind_remote_through_signal_using_handler) { UnwindThroughSignal(false); } +TEST(libbacktrace, unwind_remote_through_signal_using_handler) { + UnwindThroughSignal(false); +} -TEST(libbacktrace, unwind_remote_through_signal_using_action) { UnwindThroughSignal(true); } +TEST(libbacktrace, unwind_remote_through_signal_using_action) { + UnwindThroughSignal(true); +} #if defined(ENABLE_PSS_TESTS) #include "GetPss.h"
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h index 4f73a65..b919e81 100644 --- a/libbacktrace/include/backtrace/Backtrace.h +++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -55,6 +55,7 @@ struct backtrace_frame_data_t { size_t num; // The current fame number. uintptr_t pc; // The absolute pc. + uintptr_t rel_pc; // The relative pc. uintptr_t sp; // The top of the stack. size_t stack_size; // The size of the stack, zero indicate an unknown stack size. backtrace_map_t map; // The map associated with the given pc.
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h index 8ab0dfa..f7a55b8 100644 --- a/libbacktrace/include/backtrace/BacktraceMap.h +++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -41,7 +41,7 @@ uintptr_t start = 0; uintptr_t end = 0; uintptr_t offset = 0; - uintptr_t load_base = 0; + uintptr_t load_bias = 0; int flags = 0; std::string name; }; @@ -89,20 +89,14 @@ const_iterator begin() const { return maps_.begin(); } const_iterator end() const { return maps_.end(); } + size_t size() const { return maps_.size(); } + virtual bool Build(); static inline bool IsValid(const backtrace_map_t& map) { return map.end > 0; } - static uintptr_t GetRelativePc(const backtrace_map_t& map, uintptr_t pc) { - if (IsValid(map)) { - return pc - map.start + map.load_base; - } else { - return pc; - } - } - protected: BacktraceMap(pid_t pid);
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp index 4a5f2a7..47de12a 100644 --- a/libcrypto_utils/Android.bp +++ b/libcrypto_utils/Android.bp
@@ -17,6 +17,9 @@ cc_library { name: "libcrypto_utils", vendor_available: true, + vndk: { + enabled: true, + }, host_supported: true, srcs: [ "android_pubkey.c",
diff --git a/libcutils/Android.bp b/libcutils/Android.bp index 245deb1..d00ff5f 100644 --- a/libcutils/Android.bp +++ b/libcutils/Android.bp
@@ -50,6 +50,10 @@ cc_library { name: "libcutils", vendor_available: true, + vndk: { + enabled: true, + support_system_process: true, + }, host_supported: true, srcs: [ "config_utils.c", @@ -150,6 +154,7 @@ "libutils_headers", ], export_header_lib_headers: ["libcutils_headers"], + local_include_dirs: ["include"], cflags: [ "-Werror",
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.c index 96ca566..819a846 100644 --- a/libcutils/canned_fs_config.c +++ b/libcutils/canned_fs_config.c
@@ -23,6 +23,7 @@ #include <string.h> #include <private/android_filesystem_config.h> +#include <private/fs_config.h> #include <private/canned_fs_config.h> typedef struct {
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp index c39071c..cc96ff8 100644 --- a/libcutils/fs_config.cpp +++ b/libcutils/fs_config.cpp
@@ -267,14 +267,27 @@ return false; } +static inline bool prefix_cmp(bool partial, const char* prefix, size_t len, const char* path, + size_t plen) { + return ((partial && plen >= len) || (plen == len)) && !strncmp(prefix, path, len); +} + // alias prefixes of "<partition>/<stuff>" to "system/<partition>/<stuff>" or // "system/<partition>/<stuff>" to "<partition>/<stuff>" -static bool prefix_cmp(const char* prefix, const char* path, size_t len) { - if (!strncmp(prefix, path, len)) return true; +static bool fs_config_cmp(bool partial, const char* prefix, size_t len, const char* path, + size_t plen) { + // If name ends in * then allow partial matches. + if (!partial && prefix[len - 1] == '*') { + len--; + partial = true; + } + + if (prefix_cmp(partial, prefix, len, path, plen)) return true; static const char system[] = "system/"; if (!strncmp(path, system, strlen(system))) { path += strlen(system); + plen -= strlen(system); } else if (len <= strlen(system)) { return false; } else if (strncmp(prefix, system, strlen(system))) { @@ -283,25 +296,11 @@ prefix += strlen(system); len -= strlen(system); } - return is_partition(prefix, len) && !strncmp(prefix, path, len); + return is_partition(prefix, len) && prefix_cmp(partial, prefix, len, path, plen); } - -static bool fs_config_cmp(bool dir, const char* prefix, size_t len, const char* path, size_t plen) { - if (dir) { - if (plen < len) { - return false; - } - } else { - // If name ends in * then allow partial matches. - if (prefix[len - 1] == '*') { - return prefix_cmp(prefix, path, len - 1); - } - if (plen != len) { - return false; - } - } - return prefix_cmp(prefix, path, len); -} +#ifndef __ANDROID_VNDK__ +auto __for_testing_only__fs_config_cmp = fs_config_cmp; +#endif void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities) {
diff --git a/libcutils/include/cutils/android_filesystem_config.h b/libcutils/include/cutils/android_filesystem_config.h new file mode 120000 index 0000000..d2a92fe --- /dev/null +++ b/libcutils/include/cutils/android_filesystem_config.h
@@ -0,0 +1 @@ +../private/android_filesystem_config.h \ No newline at end of file
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h index 9683f91..4c1113b 100644 --- a/libcutils/include/cutils/sched_policy.h +++ b/libcutils/include/cutils/sched_policy.h
@@ -34,7 +34,7 @@ * Check if Linux kernel enables SCHEDTUNE feature (only available in Android * common kernel or Linaro LSK, not in mainline Linux as of v4.9) * - * Return value: 1 if Linux kernel CONFIG_SCHEDTUNE=y; 0 otherwise. + * Return value: 1 if Linux kernel CONFIG_CGROUP_SCHEDTUNE=y; 0 otherwise. */ extern bool schedboost_enabled();
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h index 02141d6..d4ba019 100644 --- a/libcutils/include/private/android_filesystem_config.h +++ b/libcutils/include/private/android_filesystem_config.h
@@ -14,11 +14,6 @@ * limitations under the License. */ -/* This file is used to define the properties of the filesystem -** images generated by build tools (mkbootfs and mkyaffs2image) and -** by the device side of adb. -*/ - /* * This file is consumed by build/tools/fs_config and is used * for generating various files. Anything #define AID_<name> @@ -49,18 +44,12 @@ #ifndef _ANDROID_FILESYSTEM_CONFIG_H_ #define _ANDROID_FILESYSTEM_CONFIG_H_ -#include <stdint.h> -#include <sys/cdefs.h> #include <sys/types.h> -#if defined(__BIONIC__) -#include <linux/capability.h> -#else -#include "android_filesystem_capability.h" +#if !defined(__ANDROID_VNDK__) && !defined(EXCLUDE_FS_CONFIG_STRUCTURES) +#include <private/fs_config.h> #endif -#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name)) - /* This is the master Users and Groups config for the platform. * DO NOT EVER RENUMBER */ @@ -193,36 +182,4 @@ * Also see build/tools/fs_config for more details. */ -#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES) - -struct fs_path_config { - unsigned mode; - unsigned uid; - unsigned gid; - uint64_t capabilities; - const char* prefix; -}; - -/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */ - -__BEGIN_DECLS - -/* - * Used in: - * build/tools/fs_config/fs_config.c - * build/tools/fs_get_stats/fs_get_stats.c - * system/extras/ext4_utils/make_ext4fs_main.c - * external/squashfs-tools/squashfs-tools/android.c - * system/core/cpio/mkbootfs.c - * system/core/adb/file_sync_service.cpp - * system/extras/ext4_utils/canned_fs_config.c - */ -void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid, - unsigned* mode, uint64_t* capabilities); - -ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc); - -__END_DECLS - -#endif #endif
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h index 7dad668..aab5042 100644 --- a/libcutils/include/private/fs_config.h +++ b/libcutils/include/private/fs_config.h
@@ -14,10 +14,24 @@ * limitations under the License. */ +/* This file is used to define the properties of the filesystem +** images generated by build tools (mkbootfs and mkyaffs2image) and +** by the device side of adb. +*/ + #ifndef _LIBS_CUTILS_PRIVATE_FS_CONFIG_H #define _LIBS_CUTILS_PRIVATE_FS_CONFIG_H #include <stdint.h> +#include <sys/cdefs.h> + +#if defined(__BIONIC__) +#include <linux/capability.h> +#else // defined(__BIONIC__) +#include "android_filesystem_capability.h" +#endif // defined(__BIONIC__) + +#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name)) /* * binary format for the runtime <partition>/etc/fs_config_(dirs|files) @@ -34,4 +48,33 @@ char prefix[]; } __attribute__((__aligned__(sizeof(uint64_t)))); +struct fs_path_config { + unsigned mode; + unsigned uid; + unsigned gid; + uint64_t capabilities; + const char* prefix; +}; + +/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */ + +__BEGIN_DECLS + +/* + * Used in: + * build/tools/fs_config/fs_config.c + * build/tools/fs_get_stats/fs_get_stats.c + * system/extras/ext4_utils/make_ext4fs_main.c + * external/squashfs-tools/squashfs-tools/android.c + * system/core/cpio/mkbootfs.c + * system/core/adb/file_sync_service.cpp + * system/extras/ext4_utils/canned_fs_config.c + */ +void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid, + unsigned* mode, uint64_t* capabilities); + +ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc); + +__END_DECLS + #endif /* _LIBS_CUTILS_PRIVATE_FS_CONFIG_H */
diff --git a/libcutils/include_vndk/cutils/android_filesystem_config.h b/libcutils/include_vndk/cutils/android_filesystem_config.h new file mode 120000 index 0000000..13a5a08 --- /dev/null +++ b/libcutils/include_vndk/cutils/android_filesystem_config.h
@@ -0,0 +1 @@ +../../include/private/android_filesystem_config.h \ No newline at end of file
diff --git a/libcutils/include_vndk/private b/libcutils/include_vndk/private deleted file mode 120000 index 2245a85..0000000 --- a/libcutils/include_vndk/private +++ /dev/null
@@ -1 +0,0 @@ -../include/private \ No newline at end of file
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp index 7170b48..b00fa85 100644 --- a/libcutils/sched_policy.cpp +++ b/libcutils/sched_policy.cpp
@@ -28,13 +28,6 @@ #define UNUSED __attribute__((__unused__)) -#ifndef SLOGE -#define SLOGE ALOGE -#endif -#ifndef SLOGW -#define SLOGW ALOGW -#endif - /* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged. * Call this any place a SchedPolicy is used as an input parameter. * Returns the possibly re-mapped policy. @@ -124,11 +117,8 @@ on where init.rc mounts cpuset. That's why we'd better require this configuration be set if CONFIG_CPUSETS is set. - With runtime check using the following function, build time - variables like ENABLE_CPUSETS (used in Android.mk) or cpusets (used - in Android.bp) are not needed. + In older releases, this was controlled by build-time configuration. */ - bool cpusets_enabled() { static bool enabled = (access("/dev/cpuset/tasks", F_OK) == 0); @@ -137,15 +127,11 @@ /* Similar to CONFIG_CPUSETS above, but with a different configuration - CONFIG_SCHEDTUNE that's in Android common Linux kernel and Linaro + CONFIG_CGROUP_SCHEDTUNE that's in Android common Linux kernel and Linaro Stable Kernel (LSK), but not in mainline Linux as of v4.9. - With runtime check using the following function, build time - variables like ENABLE_SCHEDBOOST (used in Android.mk) or schedboost - (used in Android.bp) are not needed. - + In older releases, this was controlled by build-time configuration. */ - bool schedboost_enabled() { static bool enabled = (access("/dev/stune/tasks", F_OK) == 0); @@ -343,7 +329,7 @@ return 0; } -static void set_timerslack_ns(int tid, unsigned long long slack) { +static void set_timerslack_ns(int tid, unsigned long slack) { // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface. // TODO: once we've backported this, log if the open(2) fails. if (__sys_supports_timerslack) { @@ -351,7 +337,7 @@ snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid); int fd = open(buf, O_WRONLY | O_CLOEXEC); if (fd != -1) { - int len = snprintf(buf, sizeof(buf), "%llu", slack); + int len = snprintf(buf, sizeof(buf), "%lu", slack); if (write(fd, buf, len) != len) { SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno)); }
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp index a62cd51..391adb6 100644 --- a/libcutils/tests/fs_config.cpp +++ b/libcutils/tests/fs_config.cpp
@@ -29,12 +29,39 @@ extern const fs_path_config* __for_testing_only__android_dirs; extern const fs_path_config* __for_testing_only__android_files; +extern bool (*__for_testing_only__fs_config_cmp)(bool, const char*, size_t, const char*, size_t); // Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we // hit a nullptr termination, before we declare the list is just too big or // could be missing the nullptr. static constexpr size_t max_idx = 4096; +static const struct fs_config_cmp_test { + bool dir; + const char* prefix; + const char* path; + bool match; +} fs_config_cmp_tests[] = { + // clang-format off + { true, "system/lib", "system/lib/hw", true }, + { true, "vendor/lib", "system/vendor/lib/hw", true }, + { true, "system/vendor/lib", "vendor/lib/hw", true }, + { true, "system/vendor/lib", "system/vendor/lib/hw", true }, + { false, "vendor/bin/wifi", "system/vendor/bin/w", false }, + { false, "vendor/bin/wifi", "system/vendor/bin/wifi", true }, + { false, "vendor/bin/wifi", "system/vendor/bin/wifi2", false }, + { false, "system/vendor/bin/wifi", "system/vendor/bin/wifi", true, }, + { false, "odm/bin/wifi", "system/odm/bin/wifi", true }, + { false, "oem/bin/wifi", "system/oem/bin/wifi", true }, + { false, "data/bin/wifi", "system/data/bin/wifi", false }, + { false, "system/bin/*", "system/bin/wifi", true }, + { false, "vendor/bin/*", "system/vendor/bin/wifi", true }, + { false, "system/bin/*", "system/bin", false }, + { false, "system/vendor/bin/*", "vendor/bin/wifi", true }, + { false, NULL, NULL, false }, + // clang-format on +}; + static bool check_unique(std::vector<const char*>& paths, const std::string& config_name, const std::string& prefix) { bool retval = false; @@ -106,6 +133,22 @@ return check_unique(paths_tmp, config, prefix) || retval; } +static bool check_fs_config_cmp(const fs_config_cmp_test* tests) { + bool match, retval = false; + for (size_t idx = 0; tests[idx].prefix; ++idx) { + match = __for_testing_only__fs_config_cmp(tests[idx].dir, tests[idx].prefix, + strlen(tests[idx].prefix), tests[idx].path, + strlen(tests[idx].path)); + if (match != tests[idx].match) { + GTEST_LOG_(ERROR) << tests[idx].path << (match ? " matched " : " didn't match ") + << tests[idx].prefix; + retval = true; + break; + } + } + return retval; +} + #define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field)) static bool check_unique(const std::string& config, const std::string& prefix) { @@ -199,3 +242,7 @@ TEST(fs_config, odm_files_alias) { check_two(__for_testing_only__android_files, "files", "odm/"); } + +TEST(fs_config, system_alias) { + EXPECT_FALSE(check_fs_config_cmp(fs_config_cmp_tests)); +}
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp index 088981a..23a5c79 100644 --- a/libdiskconfig/Android.bp +++ b/libdiskconfig/Android.bp
@@ -1,6 +1,9 @@ cc_library { name: "libdiskconfig", vendor_available: true, + vndk: { + enabled: true, + }, srcs: [ "diskconfig.c", "diskutils.c",
diff --git a/libion/Android.bp b/libion/Android.bp index 6f267e4..6d9fae0 100644 --- a/libion/Android.bp +++ b/libion/Android.bp
@@ -1,7 +1,11 @@ cc_library { name: "libion", - vendor_available: true, + vendor_available: true, + vndk: { + enabled: true, + support_system_process: true, + }, srcs: ["ion.c"], shared_libs: ["liblog"], local_include_dirs: [
diff --git a/liblog/Android.bp b/liblog/Android.bp index e74aa82..b98d18f 100644 --- a/liblog/Android.bp +++ b/liblog/Android.bp
@@ -42,6 +42,24 @@ "logd_writer.c", ] +cc_library_headers { + name: "liblog_headers", + host_supported: true, + vendor_available: true, + export_include_dirs: ["include"], + target: { + windows: { + enabled: true, + }, + linux_bionic: { + enabled: true, + }, + vendor: { + export_include_dirs: ["include_vndk"], + }, + }, +} + // Shared and static library for host and device // ======================================================== cc_library { @@ -81,7 +99,8 @@ }, }, - export_include_dirs: ["include"], + header_libs: ["liblog_headers"], + export_header_lib_headers: ["liblog_headers"], cflags: [ "-Werror", @@ -100,7 +119,7 @@ } ndk_headers { - name: "liblog_headers", + name: "liblog_ndk_headers", from: "include/android", to: "android", srcs: ["include/android/log.h"],
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h index 3a215e9..d01708d 100644 --- a/liblog/include/log/log.h +++ b/liblog/include/log/log.h
@@ -161,7 +161,7 @@ #endif #if __ANDROID_USE_LIBLOG_CLOCK_INTERFACE -clockid_t android_log_clockid(); +clockid_t android_log_clockid(void); #endif #endif /* __linux__ */ @@ -185,7 +185,7 @@ * May be used to clean up File descriptors after a Fork, the resources are * all O_CLOEXEC so wil self clean on exec(). */ -void __android_log_close(); +void __android_log_close(void); #endif #ifndef __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h index da16158..68c2e9a 100644 --- a/liblog/include/log/log_main.h +++ b/liblog/include/log/log_main.h
@@ -18,10 +18,9 @@ #define _LIBS_LOG_LOG_MAIN_H #include <android/log.h> +#include <sys/cdefs.h> -#ifdef __cplusplus -extern "C" { -#endif +__BEGIN_DECLS /* * Normally we strip the effects of ALOGV (VERBOSE messages), @@ -385,8 +384,6 @@ #pragma clang diagnostic pop #endif -#ifdef __cplusplus -} -#endif +__END_DECLS #endif /* _LIBS_LOG_LOG_MAIN_H */
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h index 3764faf..309f5d1 100644 --- a/liblog/include/log/log_time.h +++ b/liblog/include/log/log_time.h
@@ -28,6 +28,10 @@ #ifndef __struct_log_time_defined #define __struct_log_time_defined +#define LOG_TIME_SEC(t) ((t)->tv_sec) +/* next power of two after NS_PER_SEC */ +#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2)) + #ifdef __cplusplus /* @@ -167,15 +171,15 @@ #endif } __attribute__((__packed__)); -#else +#else /* __cplusplus */ typedef struct log_time { uint32_t tv_sec; uint32_t tv_nsec; } __attribute__((__packed__)) log_time; -#endif +#endif /* __cplusplus */ -#endif +#endif /* __struct_log_time_defined */ #endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h index 01623df..a79beec 100644 --- a/liblog/include_vndk/log/log.h +++ b/liblog/include_vndk/log/log.h
@@ -9,6 +9,7 @@ #include <log/log_radio.h> #include <log/log_read.h> #include <log/log_safetynet.h> +#include <log/log_system.h> #include <log/log_time.h> /*
diff --git a/liblog/include_vndk/log/log_system.h b/liblog/include_vndk/log/log_system.h new file mode 120000 index 0000000..d0d3904 --- /dev/null +++ b/liblog/include_vndk/log/log_system.h
@@ -0,0 +1 @@ +../../include/log/log_system.h \ No newline at end of file
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h deleted file mode 120000 index abfe439..0000000 --- a/liblog/include_vndk/log/log_time.h +++ /dev/null
@@ -1 +0,0 @@ -../../include/log/log_time.h \ No newline at end of file
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h new file mode 100644 index 0000000..5a09959 --- /dev/null +++ b/liblog/include_vndk/log/log_time.h
@@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005-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 _LIBS_LOG_LOG_TIME_H +#define _LIBS_LOG_LOG_TIME_H + +#include <stdint.h> + +/* struct log_time is a wire-format variant of struct timespec */ +#ifndef NS_PER_SEC +#define NS_PER_SEC 1000000000ULL +#endif +#ifndef US_PER_SEC +#define US_PER_SEC 1000000ULL +#endif +#ifndef MS_PER_SEC +#define MS_PER_SEC 1000ULL +#endif + +#ifndef __struct_log_time_defined +#define __struct_log_time_defined + +#define LOG_TIME_SEC(t) ((t)->tv_sec) +/* next power of two after NS_PER_SEC */ +#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2)) + +typedef struct log_time { + uint32_t tv_sec; + uint32_t tv_nsec; +} __attribute__((__packed__)) log_time; + +#endif + +#endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c index 600f4bb..603ba24 100644 --- a/liblog/logd_reader.c +++ b/liblog/logd_reader.c
@@ -590,20 +590,30 @@ memset(log_msg, 0, sizeof(*log_msg)); + unsigned int new_alarm = 0; if (logger_list->mode & ANDROID_LOG_NONBLOCK) { + if ((logger_list->mode & ANDROID_LOG_WRAP) && + (logger_list->start.tv_sec || logger_list->start.tv_nsec)) { + /* b/64143705 */ + new_alarm = (ANDROID_LOG_WRAP_DEFAULT_TIMEOUT * 11) / 10 + 10; + logger_list->mode &= ~ANDROID_LOG_WRAP; + } else { + new_alarm = 30; + } + memset(&ignore, 0, sizeof(ignore)); ignore.sa_handler = caught_signal; sigemptyset(&ignore.sa_mask); /* particularily useful if tombstone is reporting for logd */ sigaction(SIGALRM, &ignore, &old_sigaction); - old_alarm = alarm(30); + old_alarm = alarm(new_alarm); } /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */ ret = recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0); e = errno; - if (logger_list->mode & ANDROID_LOG_NONBLOCK) { + if (new_alarm) { if ((ret == 0) || (e == EINTR)) { e = EAGAIN; ret = -1;
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk index ab96429..39b52ac 100644 --- a/liblog/tests/Android.mk +++ b/liblog/tests/Android.mk
@@ -54,7 +54,7 @@ -Werror \ -fno-builtin \ -test_src_files := \ +cts_src_files := \ libc_test.cpp \ liblog_test_default.cpp \ liblog_test_local.cpp \ @@ -64,7 +64,11 @@ log_radio_test.cpp \ log_read_test.cpp \ log_system_test.cpp \ - log_time_test.cpp + log_time_test.cpp \ + log_wrap_test.cpp + +test_src_files := \ + $(cts_src_files) \ # Build tests for the device (with .so). Run with: # adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests @@ -81,15 +85,15 @@ include $(CLEAR_VARS) LOCAL_MODULE := $(cts_executable) LOCAL_MODULE_TAGS := tests -LOCAL_CFLAGS += $(test_c_flags) -LOCAL_SRC_FILES := $(test_src_files) +LOCAL_CFLAGS += $(test_c_flags) -DNO_PSTORE +LOCAL_SRC_FILES := $(cts_src_files) LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest LOCAL_MULTILIB := both LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64 LOCAL_SHARED_LIBRARIES := liblog libcutils libbase LOCAL_STATIC_LIBRARIES := libgtest libgtest_main -LOCAL_COMPATIBILITY_SUITE := cts +LOCAL_COMPATIBILITY_SUITE := cts vts LOCAL_CTS_TEST_PACKAGE := android.core.liblog include $(BUILD_CTS_EXECUTABLE)
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp index 46ec5ef..56dbf1f 100644 --- a/liblog/tests/liblog_test.cpp +++ b/liblog/tests/liblog_test.cpp
@@ -116,6 +116,7 @@ return ret; } +#ifndef NO_PSTORE static bool isPmsgActive() { pid_t pid = getpid(); @@ -125,6 +126,7 @@ return std::string::npos != myPidFds.find(" -> /dev/pmsg0"); } +#endif /* NO_PSTORE */ static bool isLogdwActive() { std::string logdwSignature = @@ -189,22 +191,25 @@ EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); #ifdef USING_LOGGER_DEFAULT // Check that we can close and reopen the logger - bool pmsgActiveAfter__android_log_btwrite; bool logdwActiveAfter__android_log_btwrite; if (getuid() == AID_ROOT) { tested__android_log_close = true; - pmsgActiveAfter__android_log_btwrite = isPmsgActive(); - logdwActiveAfter__android_log_btwrite = isLogdwActive(); +#ifndef NO_PSTORE + bool pmsgActiveAfter__android_log_btwrite = isPmsgActive(); EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite); +#endif /* NO_PSTORE */ + logdwActiveAfter__android_log_btwrite = isLogdwActive(); EXPECT_TRUE(logdwActiveAfter__android_log_btwrite); } else if (!tested__android_log_close) { fprintf(stderr, "WARNING: can not test __android_log_close()\n"); } __android_log_close(); if (getuid() == AID_ROOT) { +#ifndef NO_PSTORE bool pmsgActiveAfter__android_log_close = isPmsgActive(); - bool logdwActiveAfter__android_log_close = isLogdwActive(); EXPECT_FALSE(pmsgActiveAfter__android_log_close); +#endif /* NO_PSTORE */ + bool logdwActiveAfter__android_log_close = isLogdwActive(); EXPECT_FALSE(logdwActiveAfter__android_log_close); } #endif @@ -213,9 +218,11 @@ EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1))); #ifdef USING_LOGGER_DEFAULT if (getuid() == AID_ROOT) { - pmsgActiveAfter__android_log_btwrite = isPmsgActive(); - logdwActiveAfter__android_log_btwrite = isLogdwActive(); +#ifndef NO_PSTORE + bool pmsgActiveAfter__android_log_btwrite = isPmsgActive(); EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite); +#endif /* NO_PSTORE */ + logdwActiveAfter__android_log_btwrite = isLogdwActive(); EXPECT_TRUE(logdwActiveAfter__android_log_btwrite); } #endif @@ -3036,12 +3043,15 @@ #ifdef USING_LOGGER_DEFAULT // Do not retest pmsg functionality #ifdef __ANDROID__ +#ifndef NO_PSTORE static const char __pmsg_file[] = "/data/william-shakespeare/MuchAdoAboutNothing.txt"; +#endif /* NO_PSTORE */ #endif TEST(liblog, __android_log_pmsg_file_write) { #ifdef __ANDROID__ +#ifndef NO_PSTORE __android_log_close(); if (getuid() == AID_ROOT) { tested__android_log_close = true; @@ -3092,12 +3102,16 @@ EXPECT_TRUE(pmsgActiveAfter__android_pmsg_file_write); EXPECT_TRUE(logdwActiveAfter__android_pmsg_file_write); } +#else /* NO_PSTORE */ + GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n"; +#endif /* NO_PSTORE */ #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif } #ifdef __ANDROID__ +#ifndef NO_PSTORE static ssize_t __pmsg_fn(log_id_t logId, char prio, const char* filename, const char* buf, size_t len, void* arg) { EXPECT_TRUE(NULL == arg); @@ -3118,10 +3132,12 @@ ? -ENOEXEC : 1; } +#endif /* NO_PSTORE */ #endif TEST(liblog, __android_log_pmsg_file_read) { #ifdef __ANDROID__ +#ifndef NO_PSTORE signaled = 0; __android_log_close(); @@ -3155,6 +3171,9 @@ EXPECT_LT(0, ret); EXPECT_EQ(1U, signaled); +#else /* NO_PSTORE */ + GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n"; +#endif /* NO_PSTORE */ #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif
diff --git a/liblog/tests/log_wrap_test.cpp b/liblog/tests/log_wrap_test.cpp new file mode 100644 index 0000000..ebf0b15 --- /dev/null +++ b/liblog/tests/log_wrap_test.cpp
@@ -0,0 +1,121 @@ +/* + * Copyright (C) 2013-2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +#include <string> + +#include <android-base/chrono_utils.h> +#include <android-base/stringprintf.h> +#include <android/log.h> // minimal logging API +#include <gtest/gtest.h> +#include <log/log_properties.h> +#include <log/log_read.h> +#include <log/log_time.h> +#include <log/log_transport.h> + +#ifdef __ANDROID__ +static void read_with_wrap() { + android_set_log_transport(LOGGER_LOGD); + + // Read the last line in the log to get a starting timestamp. We're assuming + // the log is not empty. + const int mode = ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK; + struct logger_list* logger_list = + android_logger_list_open(LOG_ID_MAIN, mode, 1000, 0); + + ASSERT_NE(logger_list, nullptr); + + log_msg log_msg; + int ret = android_logger_list_read(logger_list, &log_msg); + android_logger_list_close(logger_list); + ASSERT_GT(ret, 0); + + log_time start(log_msg.entry.sec, log_msg.entry.nsec); + ASSERT_NE(start, log_time()); + + logger_list = + android_logger_list_alloc_time(mode | ANDROID_LOG_WRAP, start, 0); + ASSERT_NE(logger_list, nullptr); + + struct logger* logger = android_logger_open(logger_list, LOG_ID_MAIN); + EXPECT_NE(logger, nullptr); + if (logger) { + android_logger_list_read(logger_list, &log_msg); + } + + android_logger_list_close(logger_list); +} + +static void caught_signal(int /* signum */) { +} +#endif + +// b/64143705 confirm fixed +TEST(liblog, wrap_mode_blocks) { +#ifdef __ANDROID__ + + android::base::Timer timer; + + // The read call is expected to take up to 2 hours in the happy case. + // We only want to make sure it waits for longer than 30s, but we can't + // use an alarm as the implementation uses it. So we run the test in + // a separate process. + pid_t pid = fork(); + + if (pid == 0) { + // child + read_with_wrap(); + _exit(0); + } + + struct sigaction ignore, old_sigaction; + memset(&ignore, 0, sizeof(ignore)); + ignore.sa_handler = caught_signal; + sigemptyset(&ignore.sa_mask); + sigaction(SIGALRM, &ignore, &old_sigaction); + alarm(45); + + bool killed = false; + for (;;) { + siginfo_t info = {}; + // This wait will succeed if the child exits, or fail with EINTR if the + // alarm goes off first - a loose approximation to a timed wait. + int ret = waitid(P_PID, pid, &info, WEXITED); + if (ret >= 0 || errno != EINTR) { + EXPECT_EQ(ret, 0); + if (!killed) { + EXPECT_EQ(info.si_status, 0); + } + break; + } + unsigned int alarm_left = alarm(0); + if (alarm_left > 0) { + alarm(alarm_left); + } else { + kill(pid, SIGTERM); + killed = true; + } + } + + alarm(0); + EXPECT_GT(timer.duration(), std::chrono::seconds(40)); +#else + GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif +}
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp index 68c580a..0955633 100644 --- a/libmemtrack/Android.bp +++ b/libmemtrack/Android.bp
@@ -2,6 +2,10 @@ cc_library_shared { name: "libmemtrack", + vendor_available: true, + vndk: { + enabled: true, + }, srcs: ["memtrack.cpp"], export_include_dirs: ["include"], local_include_dirs: ["include"],
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp index 6fe67a4..213be17 100644 --- a/libmemunreachable/Allocator.cpp +++ b/libmemunreachable/Allocator.cpp
@@ -33,9 +33,11 @@ #include "android-base/macros.h" -#include "anon_vma_naming.h" #include "Allocator.h" #include "LinkedList.h" +#include "anon_vma_naming.h" + +namespace android { // runtime interfaces used: // abort @@ -57,10 +59,9 @@ static constexpr size_t kUsableChunkSize = kChunkSize - kPageSize; static constexpr size_t kMaxBucketAllocationSize = kChunkSize / 4; static constexpr size_t kMinBucketAllocationSize = 8; -static constexpr unsigned int kNumBuckets = const_log2(kMaxBucketAllocationSize) - - const_log2(kMinBucketAllocationSize) + 1; -static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize - / kPageSize; +static constexpr unsigned int kNumBuckets = + const_log2(kMaxBucketAllocationSize) - const_log2(kMinBucketAllocationSize) + 1; +static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize / kPageSize; std::atomic<int> heap_count; @@ -93,7 +94,7 @@ void FreeLocked(void* ptr); struct MapAllocation { - void *ptr; + void* ptr; size_t size; MapAllocation* next; }; @@ -107,8 +108,7 @@ } static inline unsigned int size_to_bucket(size_t size) { - if (size < kMinBucketAllocationSize) - return kMinBucketAllocationSize; + if (size < kMinBucketAllocationSize) return kMinBucketAllocationSize; return log2(size - 1) + 1 - const_log2(kMinBucketAllocationSize); } @@ -140,8 +140,7 @@ // Trim beginning if (aligned_ptr != ptr) { - ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr) - - reinterpret_cast<uintptr_t>(ptr); + ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr) - reinterpret_cast<uintptr_t>(ptr); munmap(ptr, extra); map_size -= extra; ptr = aligned_ptr; @@ -151,14 +150,13 @@ if (map_size != size) { assert(map_size > size); assert(ptr != NULL); - munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size), - map_size - size); + munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size), map_size - size); } -#define PR_SET_VMA 0x53564d41 -#define PR_SET_VMA_ANON_NAME 0 - prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, - reinterpret_cast<uintptr_t>(ptr), size, "leak_detector_malloc"); +#define PR_SET_VMA 0x53564d41 +#define PR_SET_VMA_ANON_NAME 0 + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(ptr), size, + "leak_detector_malloc"); return ptr; } @@ -170,36 +168,31 @@ Chunk(HeapImpl* heap, int bucket); ~Chunk() {} - void *Alloc(); + void* Alloc(); void Free(void* ptr); void Purge(); bool Empty(); static Chunk* ptr_to_chunk(void* ptr) { - return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr) - & ~(kChunkSize - 1)); + return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr) & ~(kChunkSize - 1)); } static bool is_chunk(void* ptr) { return (reinterpret_cast<uintptr_t>(ptr) & (kChunkSize - 1)) != 0; } - unsigned int free_count() { - return free_count_; - } - HeapImpl* heap() { - return heap_; - } - LinkedList<Chunk*> node_; // linked list sorted by minimum free count + unsigned int free_count() { return free_count_; } + HeapImpl* heap() { return heap_; } + LinkedList<Chunk*> node_; // linked list sorted by minimum free count private: DISALLOW_COPY_AND_ASSIGN(Chunk); HeapImpl* heap_; unsigned int bucket_; - unsigned int allocation_size_; // size of allocations in chunk, min 8 bytes - unsigned int max_allocations_; // maximum number of allocations in the chunk - unsigned int first_free_bitmap_; // index into bitmap for first non-full entry - unsigned int free_count_; // number of available allocations - unsigned int frees_since_purge_; // number of calls to Free since last Purge + unsigned int allocation_size_; // size of allocations in chunk, min 8 bytes + unsigned int max_allocations_; // maximum number of allocations in the chunk + unsigned int first_free_bitmap_; // index into bitmap for first non-full entry + unsigned int free_count_; // number of available allocations + unsigned int frees_since_purge_; // number of calls to Free since last Purge // bitmap of pages that have been dirtied uint32_t dirty_pages_[div_round_up(kUsablePagesPerChunk, 32)]; @@ -210,13 +203,10 @@ char data_[0]; unsigned int ptr_to_n(void* ptr) { - ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr) - - reinterpret_cast<uintptr_t>(data_); + ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(data_); return offset / allocation_size_; } - void* n_to_ptr(unsigned int n) { - return data_ + n * allocation_size_; - } + void* n_to_ptr(unsigned int n) { return data_ + n * allocation_size_; } }; static_assert(sizeof(Chunk) <= kPageSize, "header must fit in page"); @@ -225,23 +215,27 @@ assert(count == sizeof(Chunk)); void* mem = MapAligned(kChunkSize, kChunkSize); if (!mem) { - abort(); //throw std::bad_alloc; + abort(); // throw std::bad_alloc; } return mem; } // Override new operator on chunk to use mmap to allocate kChunkSize -void Chunk::operator delete(void *ptr) { +void Chunk::operator delete(void* ptr) { assert(reinterpret_cast<Chunk*>(ptr) == ptr_to_chunk(ptr)); munmap(ptr, kChunkSize); } -Chunk::Chunk(HeapImpl* heap, int bucket) : - node_(this), heap_(heap), bucket_(bucket), allocation_size_( - bucket_to_size(bucket)), max_allocations_( - kUsableChunkSize / allocation_size_), first_free_bitmap_(0), free_count_( - max_allocations_), frees_since_purge_(0) { +Chunk::Chunk(HeapImpl* heap, int bucket) + : node_(this), + heap_(heap), + bucket_(bucket), + allocation_size_(bucket_to_size(bucket)), + max_allocations_(kUsableChunkSize / allocation_size_), + first_free_bitmap_(0), + free_count_(max_allocations_), + frees_since_purge_(0) { memset(dirty_pages_, 0, sizeof(dirty_pages_)); memset(free_bitmap_, 0xff, sizeof(free_bitmap_)); } @@ -254,8 +248,7 @@ assert(free_count_ > 0); unsigned int i = first_free_bitmap_; - while (free_bitmap_[i] == 0) - i++; + while (free_bitmap_[i] == 0) i++; assert(i < arraysize(free_bitmap_)); unsigned int bit = __builtin_ffs(free_bitmap_[i]) - 1; assert(free_bitmap_[i] & (1U << bit)); @@ -306,38 +299,35 @@ void Chunk::Purge() { frees_since_purge_ = 0; - //unsigned int allocsPerPage = kPageSize / allocation_size_; + // unsigned int allocsPerPage = kPageSize / allocation_size_; } // Override new operator on HeapImpl to use mmap to allocate a page -void* HeapImpl::operator new(std::size_t count __attribute__((unused))) - noexcept { +void* HeapImpl::operator new(std::size_t count __attribute__((unused))) noexcept { assert(count == sizeof(HeapImpl)); void* mem = MapAligned(kPageSize, kPageSize); if (!mem) { - abort(); //throw std::bad_alloc; + abort(); // throw std::bad_alloc; } heap_count++; return mem; } -void HeapImpl::operator delete(void *ptr) { +void HeapImpl::operator delete(void* ptr) { munmap(ptr, kPageSize); } -HeapImpl::HeapImpl() : - free_chunks_(), full_chunks_(), map_allocation_list_(NULL) { -} +HeapImpl::HeapImpl() : free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {} bool HeapImpl::Empty() { for (unsigned int i = 0; i < kNumBuckets; i++) { - for (LinkedList<Chunk*> *it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) { + for (LinkedList<Chunk*>* it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) { if (!it->data()->Empty()) { return false; } } - for (LinkedList<Chunk*> *it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) { + for (LinkedList<Chunk*>* it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) { if (!it->data()->Empty()) { return false; } @@ -350,12 +340,12 @@ HeapImpl::~HeapImpl() { for (unsigned int i = 0; i < kNumBuckets; i++) { while (!free_chunks_[i].empty()) { - Chunk *chunk = free_chunks_[i].next()->data(); + Chunk* chunk = free_chunks_[i].next()->data(); chunk->node_.remove(); delete chunk; } while (!full_chunks_[i].empty()) { - Chunk *chunk = full_chunks_[i].next()->data(); + Chunk* chunk = full_chunks_[i].next()->data(); chunk->node_.remove(); delete chunk; } @@ -373,18 +363,18 @@ } int bucket = size_to_bucket(size); if (free_chunks_[bucket].empty()) { - Chunk *chunk = new Chunk(this, bucket); + Chunk* chunk = new Chunk(this, bucket); free_chunks_[bucket].insert(chunk->node_); } return free_chunks_[bucket].next()->data()->Alloc(); } -void HeapImpl::Free(void *ptr) { +void HeapImpl::Free(void* ptr) { std::lock_guard<std::mutex> lk(m_); FreeLocked(ptr); } -void HeapImpl::FreeLocked(void *ptr) { +void HeapImpl::FreeLocked(void* ptr) { if (!Chunk::is_chunk(ptr)) { HeapImpl::MapFree(ptr); } else { @@ -397,12 +387,11 @@ void* HeapImpl::MapAlloc(size_t size) { size = (size + kPageSize - 1) & ~(kPageSize - 1); - MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked( - sizeof(MapAllocation))); + MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(sizeof(MapAllocation))); void* ptr = MapAligned(size, kChunkSize); if (!ptr) { FreeLocked(allocation); - abort(); //throw std::bad_alloc; + abort(); // throw std::bad_alloc; } allocation->ptr = ptr; allocation->size = size; @@ -412,10 +401,9 @@ return ptr; } -void HeapImpl::MapFree(void *ptr) { - MapAllocation **allocation = &map_allocation_list_; - while (*allocation && (*allocation)->ptr != ptr) - allocation = &(*allocation)->next; +void HeapImpl::MapFree(void* ptr) { + MapAllocation** allocation = &map_allocation_list_; + while (*allocation && (*allocation)->ptr != ptr) allocation = &(*allocation)->next; assert(*allocation != nullptr); @@ -425,22 +413,22 @@ *allocation = (*allocation)->next; } -void HeapImpl::MoveToFreeList(Chunk *chunk, int bucket) { +void HeapImpl::MoveToFreeList(Chunk* chunk, int bucket) { MoveToList(chunk, &free_chunks_[bucket]); } -void HeapImpl::MoveToFullList(Chunk *chunk, int bucket) { +void HeapImpl::MoveToFullList(Chunk* chunk, int bucket) { MoveToList(chunk, &full_chunks_[bucket]); } -void HeapImpl::MoveToList(Chunk *chunk, LinkedList<Chunk*>* head) { +void HeapImpl::MoveToList(Chunk* chunk, LinkedList<Chunk*>* head) { // Remove from old list chunk->node_.remove(); - LinkedList<Chunk*> *node = head; + LinkedList<Chunk*>* node = head; // Insert into new list, sorted by lowest free count - while (node->next() != head && node->data() != nullptr - && node->data()->free_count() < chunk->free_count()) + while (node->next() != head && node->data() != nullptr && + node->data()->free_count() < chunk->free_count()) node = node->next(); node->insert(chunk->node_); @@ -469,10 +457,12 @@ impl_->Free(ptr); } -void Heap::deallocate(HeapImpl*impl, void* ptr) { +void Heap::deallocate(HeapImpl* impl, void* ptr) { impl->Free(ptr); } bool Heap::empty() { return impl_->Empty(); } + +} // namespace android
diff --git a/libmemunreachable/Allocator.h b/libmemunreachable/Allocator.h index 5390739..837a12b 100644 --- a/libmemunreachable/Allocator.h +++ b/libmemunreachable/Allocator.h
@@ -27,18 +27,20 @@ #include <unordered_map> #include <unordered_set> #include <vector> + +namespace android { + extern std::atomic<int> heap_count; class HeapImpl; -template<typename T> +template <typename T> class Allocator; - // Non-templated class that implements wraps HeapImpl to keep // implementation out of the header file class Heap { -public: + public: Heap(); ~Heap(); @@ -59,110 +61,99 @@ static void deallocate(HeapImpl* impl, void* ptr); // Allocate a class of type T - template<class T> + template <class T> T* allocate() { return reinterpret_cast<T*>(allocate(sizeof(T))); } // Comparators, copied objects will be equal - bool operator ==(const Heap& other) const { - return impl_ == other.impl_; - } - bool operator !=(const Heap& other) const { - return !(*this == other); - } + bool operator==(const Heap& other) const { return impl_ == other.impl_; } + bool operator!=(const Heap& other) const { return !(*this == other); } // std::unique_ptr wrapper that allocates using allocate and deletes using // deallocate - template<class T> + template <class T> using unique_ptr = std::unique_ptr<T, std::function<void(void*)>>; - template<class T, class... Args> + template <class T, class... Args> unique_ptr<T> make_unique(Args&&... args) { HeapImpl* impl = impl_; - return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...), - [impl](void* ptr) { - reinterpret_cast<T*>(ptr)->~T(); - deallocate(impl, ptr); - }); + return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...), [impl](void* ptr) { + reinterpret_cast<T*>(ptr)->~T(); + deallocate(impl, ptr); + }); } // std::unique_ptr wrapper that allocates using allocate and deletes using // deallocate - template<class T> + template <class T> using shared_ptr = std::shared_ptr<T>; - template<class T, class... Args> + template <class T, class... Args> shared_ptr<T> make_shared(Args&&... args); -protected: + protected: HeapImpl* impl_; bool owns_impl_; }; // STLAllocator implements the std allocator interface on top of a Heap -template<typename T> +template <typename T> class STLAllocator { -public: + public: using value_type = T; - ~STLAllocator() { - } + ~STLAllocator() {} // Construct an STLAllocator on top of a Heap - STLAllocator(const Heap& heap) : // NOLINT, implicit - heap_(heap) { - } + STLAllocator(const Heap& heap) + : // NOLINT, implicit + heap_(heap) {} // Rebind an STLAllocator from an another STLAllocator - template<typename U> - STLAllocator(const STLAllocator<U>& other) : // NOLINT, implicit - heap_(other.heap_) { - } + template <typename U> + STLAllocator(const STLAllocator<U>& other) + : // NOLINT, implicit + heap_(other.heap_) {} STLAllocator(const STLAllocator&) = default; STLAllocator<T>& operator=(const STLAllocator<T>&) = default; - T* allocate(std::size_t n) { - return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T))); - } + T* allocate(std::size_t n) { return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T))); } - void deallocate(T* ptr, std::size_t) { - heap_.deallocate(ptr); - } + void deallocate(T* ptr, std::size_t) { heap_.deallocate(ptr); } - template<typename U> - bool operator ==(const STLAllocator<U>& other) const { + template <typename U> + bool operator==(const STLAllocator<U>& other) const { return heap_ == other.heap_; } - template<typename U> - inline bool operator !=(const STLAllocator<U>& other) const { + template <typename U> + inline bool operator!=(const STLAllocator<U>& other) const { return !(this == other); } - template<typename U> + template <typename U> friend class STLAllocator; -protected: + protected: Heap heap_; }; - // Allocator extends STLAllocator with some convenience methods for allocating // a single object and for constructing unique_ptr and shared_ptr objects with // appropriate deleters. -template<class T> +template <class T> class Allocator : public STLAllocator<T> { public: ~Allocator() {} - Allocator(const Heap& other) : // NOLINT, implicit - STLAllocator<T>(other) { - } + Allocator(const Heap& other) + : // NOLINT, implicit + STLAllocator<T>(other) {} - template<typename U> - Allocator(const STLAllocator<U>& other) : // NOLINT, implicit - STLAllocator<T>(other) { - } + template <typename U> + Allocator(const STLAllocator<U>& other) + : // NOLINT, implicit + STLAllocator<T>(other) {} Allocator(const Allocator&) = default; Allocator<T>& operator=(const Allocator<T>&) = default; @@ -171,24 +162,20 @@ using STLAllocator<T>::deallocate; using STLAllocator<T>::heap_; - T* allocate() { - return STLAllocator<T>::allocate(1); - } - void deallocate(void* ptr) { - heap_.deallocate(ptr); - } + T* allocate() { return STLAllocator<T>::allocate(1); } + void deallocate(void* ptr) { heap_.deallocate(ptr); } using shared_ptr = Heap::shared_ptr<T>; - template<class... Args> - shared_ptr make_shared(Args&& ...args) { + template <class... Args> + shared_ptr make_shared(Args&&... args) { return heap_.template make_shared<T>(std::forward<Args>(args)...); } using unique_ptr = Heap::unique_ptr<T>; - template<class... Args> - unique_ptr make_unique(Args&& ...args) { + template <class... Args> + unique_ptr make_unique(Args&&... args) { return heap_.template make_unique<T>(std::forward<Args>(args)...); } }; @@ -196,33 +183,36 @@ // std::unique_ptr wrapper that allocates using allocate and deletes using // deallocate. Implemented outside class definition in order to pass // Allocator<T> to shared_ptr. -template<class T, class... Args> +template <class T, class... Args> inline Heap::shared_ptr<T> Heap::make_shared(Args&&... args) { return std::allocate_shared<T, Allocator<T>, Args...>(Allocator<T>(*this), - std::forward<Args>(args)...); + std::forward<Args>(args)...); } namespace allocator { -template<class T> +template <class T> using vector = std::vector<T, Allocator<T>>; -template<class T> +template <class T> using list = std::list<T, Allocator<T>>; -template<class Key, class T, class Compare = std::less<Key>> +template <class Key, class T, class Compare = std::less<Key>> using map = std::map<Key, T, Compare, Allocator<std::pair<const Key, T>>>; -template<class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>> -using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>; +template <class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>> +using unordered_map = + std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>; -template<class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>> +template <class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>> using unordered_set = std::unordered_set<Key, Hash, KeyEqual, Allocator<Key>>; -template<class Key, class Compare = std::less<Key>> +template <class Key, class Compare = std::less<Key>> using set = std::set<Key, Compare, Allocator<Key>>; using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>; } +} // namespace android + #endif
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp index cdac76b..826a576 100644 --- a/libmemunreachable/Android.bp +++ b/libmemunreachable/Android.bp
@@ -26,6 +26,7 @@ defaults: ["libmemunreachable_defaults"], srcs: [ "Allocator.cpp", + "Binder.cpp", "HeapWalker.cpp", "LeakFolding.cpp", "LeakPipe.cpp", @@ -84,3 +85,18 @@ }, }, } + +cc_test { + name: "memunreachable_binder_test", + defaults: ["libmemunreachable_defaults"], + srcs: [ + "tests/Binder_test.cpp", + "tests/MemUnreachable_test.cpp", + ], + shared_libs: [ + "libbinder", + "libhwbinder", + "libmemunreachable", + "libutils", + ], +}
diff --git a/libmemunreachable/Binder.cpp b/libmemunreachable/Binder.cpp new file mode 100644 index 0000000..60512a3 --- /dev/null +++ b/libmemunreachable/Binder.cpp
@@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sys/cdefs.h> +#include <unistd.h> + +#include <functional> + +#include "Binder.h" +#include "log.h" + +__BEGIN_DECLS + +// Weak undefined references to the symbols in libbinder and libhwbinder +// so that libmemunreachable can call them in processes that have them +// loaded without requiring libmemunreachable to have dependencies on them. +ssize_t __attribute__((weak)) getBinderKernelReferences(size_t, uintptr_t*); +ssize_t __attribute__((weak)) getHWBinderKernelReferences(size_t, uintptr_t*); + +__END_DECLS + +namespace android { + +static bool BinderReferencesToVector(allocator::vector<uintptr_t>& refs, + std::function<ssize_t(size_t, uintptr_t*)> fn) { + if (fn == nullptr) { + return true; + } + + size_t size = refs.size(); + + do { + refs.resize(size); + + ssize_t ret = fn(refs.size(), refs.data()); + if (ret < 0) { + return false; + } + + size = ret; + } while (size > refs.size()); + + refs.resize(size); + return true; +} + +bool BinderReferences(allocator::vector<uintptr_t>& refs) { + refs.clear(); + + allocator::vector<uintptr_t> binder_refs{refs.get_allocator()}; + if (BinderReferencesToVector(refs, getBinderKernelReferences)) { + refs.insert(refs.end(), binder_refs.begin(), binder_refs.end()); + } else { + MEM_ALOGE("getBinderKernelReferences failed"); + } + + allocator::vector<uintptr_t> hwbinder_refs{refs.get_allocator()}; + if (BinderReferencesToVector(hwbinder_refs, getHWBinderKernelReferences)) { + refs.insert(refs.end(), hwbinder_refs.begin(), hwbinder_refs.end()); + } else { + MEM_ALOGE("getHWBinderKernelReferences failed"); + } + + return true; +} + +} // namespace android
diff --git a/libunwindstack/Log.h b/libmemunreachable/Binder.h similarity index 65% copy from libunwindstack/Log.h copy to libmemunreachable/Binder.h index 2d01aa8..bf4fd3e 100644 --- a/libunwindstack/Log.h +++ b/libmemunreachable/Binder.h
@@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,15 @@ * limitations under the License. */ -#ifndef _LIBUNWINDSTACK_LOG_H -#define _LIBUNWINDSTACK_LOG_H +#ifndef LIBMEMUNREACHABLE_BINDER_H_ +#define LIBMEMUNREACHABLE_BINDER_H_ -#include <stdint.h> +#include "Allocator.h" -void log_to_stdout(bool enable); -void log(uint8_t indent, const char* format, ...); +namespace android { -#endif // _LIBUNWINDSTACK_LOG_H +bool BinderReferences(allocator::vector<uintptr_t>& refs); + +} // namespace android + +#endif // LIBMEMUNREACHABLE_BINDER_H_
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp index c365ae5..2403ad0 100644 --- a/libmemunreachable/HeapWalker.cpp +++ b/libmemunreachable/HeapWalker.cpp
@@ -28,6 +28,8 @@ #include "ScopedSignalHandler.h" #include "log.h" +namespace android { + bool HeapWalker::Allocation(uintptr_t begin, uintptr_t end) { if (end == begin) { end = begin + 1; @@ -114,8 +116,8 @@ return true; } -bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit, - size_t* num_leaks_out, size_t* leak_bytes_out) { +bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit, size_t* num_leaks_out, + size_t* leak_bytes_out) { leaked.clear(); size_t num_leaks = 0; @@ -148,9 +150,9 @@ static bool MapOverPage(void* addr) { const size_t page_size = sysconf(_SC_PAGE_SIZE); - void *page = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & ~(page_size-1)); + void* page = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & ~(page_size - 1)); - void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0); + void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); if (ret == MAP_FAILED) { MEM_ALOGE("failed to map page at %p: %s", page, strerror(errno)); return false; @@ -159,7 +161,8 @@ return true; } -void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si, void* /*uctx*/) { +void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si, + void* /*uctx*/) { uintptr_t addr = reinterpret_cast<uintptr_t>(si->si_addr); if (addr != walking_ptr_) { handler.reset(); @@ -172,3 +175,5 @@ } ScopedSignalHandler::SignalFn ScopedSignalHandler::handler_; + +} // namespace android
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h index b25696f..5c7ec13 100644 --- a/libmemunreachable/HeapWalker.h +++ b/libmemunreachable/HeapWalker.h
@@ -25,6 +25,8 @@ #include "ScopedSignalHandler.h" #include "Tarjan.h" +namespace android { + // A range [begin, end) struct Range { uintptr_t begin; @@ -34,31 +36,31 @@ bool operator==(const Range& other) const { return this->begin == other.begin && this->end == other.end; } - bool operator!=(const Range& other) const { - return !(*this == other); - } + bool operator!=(const Range& other) const { return !(*this == other); } }; // Comparator for Ranges that returns equivalence for overlapping ranges struct compare_range { - bool operator()(const Range& a, const Range& b) const { - return a.end <= b.begin; - } + bool operator()(const Range& a, const Range& b) const { return a.end <= b.begin; } }; class HeapWalker { public: - explicit HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator), - allocations_(allocator), allocation_bytes_(0), - roots_(allocator), root_vals_(allocator), - segv_handler_(allocator), walking_ptr_(0) { + explicit HeapWalker(Allocator<HeapWalker> allocator) + : allocator_(allocator), + allocations_(allocator), + allocation_bytes_(0), + roots_(allocator), + root_vals_(allocator), + segv_handler_(allocator), + walking_ptr_(0) { valid_allocations_range_.end = 0; valid_allocations_range_.begin = ~valid_allocations_range_.end; - segv_handler_.install(SIGSEGV, - [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) { + segv_handler_.install( + SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) { this->HandleSegFault(handler, signal, siginfo, uctx); - }); + }); } ~HeapWalker() {} @@ -68,15 +70,14 @@ bool DetectLeaks(); - bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks, - size_t* leak_bytes); + bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks, size_t* leak_bytes); size_t Allocations(); size_t AllocationBytes(); - template<class F> + template <class F> void ForEachPtrInRange(const Range& range, F&& f); - template<class F> + template <class F> void ForEachAllocation(F&& f); struct AllocationInfo { @@ -84,7 +85,6 @@ }; private: - void RecurseRoot(const Range& root); bool WordContainsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info); void HandleSegFault(ScopedSignalHandler&, int, siginfo_t*, void*); @@ -103,7 +103,7 @@ uintptr_t walking_ptr_; }; -template<class F> +template <class F> inline void HeapWalker::ForEachPtrInRange(const Range& range, F&& f) { uintptr_t begin = (range.begin + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1); // TODO(ccross): we might need to consider a pointer to the end of a buffer @@ -118,7 +118,7 @@ } } -template<class F> +template <class F> inline void HeapWalker::ForEachAllocation(F&& f) { for (auto& it : allocations_) { const Range& range = it.first; @@ -127,4 +127,6 @@ } } +} // namespace android + #endif
diff --git a/libmemunreachable/Leak.h b/libmemunreachable/Leak.h index eaeeea7..de64b64 100644 --- a/libmemunreachable/Leak.h +++ b/libmemunreachable/Leak.h
@@ -26,9 +26,9 @@ // as a key in std::unordered_map. namespace std { -template<> -struct hash<Leak::Backtrace> { - std::size_t operator()(const Leak::Backtrace& key) const { +template <> +struct hash<android::Leak::Backtrace> { + std::size_t operator()(const android::Leak::Backtrace& key) const { std::size_t seed = 0; hash_combine(seed, key.num_frames); @@ -40,7 +40,7 @@ } private: - template<typename T> + template <typename T> inline void hash_combine(std::size_t& seed, const T& v) const { std::hash<T> hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); @@ -49,9 +49,12 @@ } // namespace std +namespace android { + static bool operator==(const Leak::Backtrace& lhs, const Leak::Backtrace& rhs) { return (lhs.num_frames == rhs.num_frames) && - memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0; + memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0; +} } #endif
diff --git a/libmemunreachable/LeakFolding.cpp b/libmemunreachable/LeakFolding.cpp index be4d20c..69f320c 100644 --- a/libmemunreachable/LeakFolding.cpp +++ b/libmemunreachable/LeakFolding.cpp
@@ -22,6 +22,8 @@ #include "Tarjan.h" #include "log.h" +namespace android { + // Converts possibly cyclic graph of leaks to a DAG by combining // strongly-connected components into a object, stored in the scc pointer // of each node in the component. @@ -31,11 +33,11 @@ Allocator<SCCInfo> scc_allocator = allocator_; - for (auto& scc_nodes: scc_list) { + for (auto& scc_nodes : scc_list) { Allocator<SCCInfo>::unique_ptr leak_scc; leak_scc = scc_allocator.make_unique(scc_allocator); - for (auto& node: scc_nodes) { + for (auto& node : scc_nodes) { node->ptr->scc = leak_scc.get(); leak_scc->count++; leak_scc->size += node->ptr->range.size(); @@ -46,7 +48,7 @@ for (auto& it : leak_map_) { LeakInfo& leak = it.second; - for (auto& ref: leak.node.references_out) { + for (auto& ref : leak.node.references_out) { if (leak.scc != ref->ptr->scc) { leak.scc->node.Edge(&ref->ptr->scc->node); } @@ -55,17 +57,14 @@ } void LeakFolding::AccumulateLeaks(SCCInfo* dominator) { - std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, - [&](SCCInfo* scc) { - if (scc->accumulator != dominator) { - scc->accumulator = dominator; - dominator->cuumulative_size += scc->size; - dominator->cuumulative_count += scc->count; - scc->node.Foreach([&](SCCInfo* ref) { - walk(ref); - }); - } - }); + std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, [&](SCCInfo* scc) { + if (scc->accumulator != dominator) { + scc->accumulator = dominator; + dominator->cuumulative_size += scc->size; + dominator->cuumulative_count += scc->count; + scc->node.Foreach([&](SCCInfo* ref) { walk(ref); }); + } + }); walk(dominator); } @@ -73,27 +72,25 @@ Allocator<LeakInfo> leak_allocator = allocator_; // Find all leaked allocations insert them into leak_map_ and leak_graph_ - heap_walker_.ForEachAllocation( - [&](const Range& range, HeapWalker::AllocationInfo& allocation) { - if (!allocation.referenced_from_root) { - auto it = leak_map_.emplace(std::piecewise_construct, - std::forward_as_tuple(range), - std::forward_as_tuple(range, allocator_)); - LeakInfo& leak = it.first->second; - leak_graph_.push_back(&leak.node); - } - }); + heap_walker_.ForEachAllocation([&](const Range& range, HeapWalker::AllocationInfo& allocation) { + if (!allocation.referenced_from_root) { + auto it = leak_map_.emplace(std::piecewise_construct, std::forward_as_tuple(range), + std::forward_as_tuple(range, allocator_)); + LeakInfo& leak = it.first->second; + leak_graph_.push_back(&leak.node); + } + }); // Find references between leaked allocations and connect them in leak_graph_ for (auto& it : leak_map_) { LeakInfo& leak = it.second; heap_walker_.ForEachPtrInRange(leak.range, - [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) { - if (!ptr_info->referenced_from_root) { - LeakInfo* ptr_leak = &leak_map_.at(ptr_range); - leak.node.Edge(&ptr_leak->node); - } - }); + [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) { + if (!ptr_info->referenced_from_root) { + LeakInfo* ptr_leak = &leak_map_.at(ptr_range); + leak.node.Edge(&ptr_leak->node); + } + }); } // Convert the cyclic graph to a DAG by grouping strongly connected components @@ -110,8 +107,8 @@ return true; } -bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked, - size_t* num_leaks_out, size_t* leak_bytes_out) { +bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked, size_t* num_leaks_out, + size_t* leak_bytes_out) { size_t num_leaks = 0; size_t leak_bytes = 0; for (auto& it : leak_map_) { @@ -123,9 +120,8 @@ for (auto& it : leak_map_) { const LeakInfo& leak = it.second; if (leak.scc->dominator) { - leaked.emplace_back(Leak{leak.range, - leak.scc->cuumulative_count - 1, - leak.scc->cuumulative_size - leak.range.size()}); + leaked.emplace_back(Leak{leak.range, leak.scc->cuumulative_count - 1, + leak.scc->cuumulative_size - leak.range.size()}); } } @@ -138,3 +134,5 @@ return true; } + +} // namespace android
diff --git a/libmemunreachable/LeakFolding.h b/libmemunreachable/LeakFolding.h index 9c6a525..09affac 100644 --- a/libmemunreachable/LeakFolding.h +++ b/libmemunreachable/LeakFolding.h
@@ -19,11 +19,16 @@ #include "HeapWalker.h" +namespace android { + class LeakFolding { public: LeakFolding(Allocator<void> allocator, HeapWalker& heap_walker) - : allocator_(allocator), heap_walker_(heap_walker), - leak_map_(allocator), leak_graph_(allocator), leak_scc_(allocator) {} + : allocator_(allocator), + heap_walker_(heap_walker), + leak_map_(allocator), + leak_graph_(allocator), + leak_scc_(allocator) {} bool FoldLeaks(); @@ -33,8 +38,7 @@ size_t referenced_size; }; - bool Leaked(allocator::vector<Leak>& leaked, - size_t* num_leaks_out, size_t* leak_bytes_out); + bool Leaked(allocator::vector<Leak>& leaked, size_t* num_leaks_out, size_t* leak_bytes_out); private: DISALLOW_COPY_AND_ASSIGN(LeakFolding); @@ -54,9 +58,15 @@ bool dominator; SCCInfo* accumulator; - explicit SCCInfo(Allocator<SCCInfo> allocator) : node(this, allocator), - count(0), size(0), cuumulative_count(0), cuumulative_size(0), - dominator(false), accumulator(nullptr) {} + explicit SCCInfo(Allocator<SCCInfo> allocator) + : node(this, allocator), + count(0), + size(0), + cuumulative_count(0), + cuumulative_size(0), + dominator(false), + accumulator(nullptr) {} + private: SCCInfo(SCCInfo&&) = delete; DISALLOW_COPY_AND_ASSIGN(SCCInfo); @@ -71,8 +81,7 @@ SCCInfo* scc; LeakInfo(const Range& range, Allocator<LeakInfo> allocator) - : node(this, allocator), range(range), - scc(nullptr) {} + : node(this, allocator), range(range), scc(nullptr) {} private: DISALLOW_COPY_AND_ASSIGN(LeakInfo); @@ -86,4 +95,6 @@ allocator::vector<Allocator<SCCInfo>::unique_ptr> leak_scc_; }; -#endif // LIBMEMUNREACHABLE_LEAK_FOLDING_H_ +} // namespace android + +#endif // LIBMEMUNREACHABLE_LEAK_FOLDING_H_
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp index 78117e2..8ea9ad6 100644 --- a/libmemunreachable/LeakPipe.cpp +++ b/libmemunreachable/LeakPipe.cpp
@@ -21,9 +21,11 @@ #include "log.h" +namespace android { + bool LeakPipe::SendFd(int sock, int fd) { - struct msghdr hdr{}; - struct iovec iov{}; + struct msghdr hdr {}; + struct iovec iov {}; unsigned int data = 0xfdfdfdfd; alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))]; @@ -56,8 +58,8 @@ } int LeakPipe::ReceiveFd(int sock) { - struct msghdr hdr{}; - struct iovec iov{}; + struct msghdr hdr {}; + struct iovec iov {}; unsigned int data; alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))]; @@ -87,3 +89,5 @@ return *(int*)CMSG_DATA(cmsg); } + +} // namespace android
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h index 3ea2d8f..94d4aa4 100644 --- a/libmemunreachable/LeakPipe.h +++ b/libmemunreachable/LeakPipe.h
@@ -26,6 +26,8 @@ #include "ScopedPipe.h" #include "log.h" +namespace android { + // LeakPipe implements a pipe that can transfer vectors of simple objects // between processes. The pipe is created in the sending process and // transferred over a socketpair that was created before forking. This ensures @@ -34,15 +36,13 @@ class LeakPipe { public: LeakPipe() { - int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_); + int ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv_); if (ret < 0) { MEM_LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno)); } } - ~LeakPipe() { - Close(); - } + ~LeakPipe() { Close(); } void Close() { close(sv_[0]); @@ -77,13 +77,9 @@ public: LeakPipeBase() : fd_(-1) {} - ~LeakPipeBase() { - Close(); - } + ~LeakPipeBase() { Close(); } - void SetFd(int fd) { - fd_ = fd; - } + void SetFd(int fd) { fd_ = fd; } void Close() { close(fd_); @@ -101,7 +97,7 @@ public: using LeakPipeBase::LeakPipeBase; - template<typename T> + template <typename T> bool Send(const T& value) { ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T))); if (ret < 0) { @@ -115,7 +111,7 @@ return true; } - template<class T, class Alloc = std::allocator<T>> + template <class T, class Alloc = std::allocator<T>> bool SendVector(const std::vector<T, Alloc>& vector) { size_t size = vector.size() * sizeof(T); if (!Send(size)) { @@ -139,7 +135,7 @@ public: using LeakPipeBase::LeakPipeBase; - template<typename T> + template <typename T> bool Receive(T* value) { ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T))); if (ret < 0) { @@ -153,7 +149,7 @@ return true; } - template<class T, class Alloc = std::allocator<T>> + template <class T, class Alloc = std::allocator<T>> bool ReceiveVector(std::vector<T, Alloc>& vector) { size_t size = 0; if (!Receive(&size)) { @@ -178,16 +174,11 @@ return true; } - }; - LeakPipeReceiver& Receiver() { - return receiver_; - } + LeakPipeReceiver& Receiver() { return receiver_; } - LeakPipeSender& Sender() { - return sender_; - } + LeakPipeSender& Sender() { return sender_; } private: LeakPipeReceiver receiver_; @@ -198,4 +189,6 @@ int sv_[2]; }; -#endif // LIBMEMUNREACHABLE_LEAK_PIPE_H_ +} // namespace android + +#endif // LIBMEMUNREACHABLE_LEAK_PIPE_H_
diff --git a/libmemunreachable/LineBuffer.cpp b/libmemunreachable/LineBuffer.cpp index d3580c0..4ea0542 100644 --- a/libmemunreachable/LineBuffer.cpp +++ b/libmemunreachable/LineBuffer.cpp
@@ -23,8 +23,10 @@ #include "LineBuffer.h" -LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len) : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) { -} +namespace android { + +LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len) + : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {} bool LineBuffer::GetLine(char** line, size_t* line_len) { while (true) { @@ -60,3 +62,5 @@ bytes_ += bytes; } } + +} // namespace android
diff --git a/libmemunreachable/LineBuffer.h b/libmemunreachable/LineBuffer.h index a015c46..cc6cd0c 100644 --- a/libmemunreachable/LineBuffer.h +++ b/libmemunreachable/LineBuffer.h
@@ -19,6 +19,8 @@ #include <stdint.h> +namespace android { + class LineBuffer { public: LineBuffer(int fd, char* buffer, size_t buffer_len); @@ -33,4 +35,6 @@ size_t bytes_ = 0; }; -#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H +} // namespace android + +#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H
diff --git a/libmemunreachable/LinkedList.h b/libmemunreachable/LinkedList.h index 132842d..36fe9fd 100644 --- a/libmemunreachable/LinkedList.h +++ b/libmemunreachable/LinkedList.h
@@ -17,44 +17,47 @@ #ifndef LIBMEMUNREACHABLE_LINKED_LIST_H_ #define LIBMEMUNREACHABLE_LINKED_LIST_H_ -template<class T> +namespace android { + +template <class T> class LinkedList { -public: - LinkedList() : next_(this), prev_(this), data_() {} - explicit LinkedList(T data) : LinkedList() { - data_ = data; - } - ~LinkedList() {} - void insert(LinkedList<T>& node) { - assert(node.empty()); - node.next_ = this->next_; - node.next_->prev_ = &node; - this->next_ = &node; - node.prev_ = this; - } - void remove() { - this->next_->prev_ = this->prev_; - this->prev_->next_ = this->next_; - this->next_ = this; - this->prev_ = this; - } - T data() { return data_; } - bool empty() { return next_ == this && prev_ == this; } - LinkedList<T> *next() { return next_; } -private: - LinkedList<T> *next_; - LinkedList<T> *prev_; - T data_; + public: + LinkedList() : next_(this), prev_(this), data_() {} + explicit LinkedList(T data) : LinkedList() { data_ = data; } + ~LinkedList() {} + void insert(LinkedList<T>& node) { + assert(node.empty()); + node.next_ = this->next_; + node.next_->prev_ = &node; + this->next_ = &node; + node.prev_ = this; + } + void remove() { + this->next_->prev_ = this->prev_; + this->prev_->next_ = this->next_; + this->next_ = this; + this->prev_ = this; + } + T data() { return data_; } + bool empty() { return next_ == this && prev_ == this; } + LinkedList<T>* next() { return next_; } + + private: + LinkedList<T>* next_; + LinkedList<T>* prev_; + T data_; }; -template<class T> +template <class T> class LinkedListHead { -public: - LinkedListHead() : node_() {} - ~LinkedListHead() {} + public: + LinkedListHead() : node_() {} + ~LinkedListHead() {} -private: - LinkedList<T> node_; + private: + LinkedList<T> node_; }; +} // namespace android + #endif
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp index 1c84744..5e062fd 100644 --- a/libmemunreachable/MemUnreachable.cpp +++ b/libmemunreachable/MemUnreachable.cpp
@@ -15,18 +15,20 @@ */ #include <inttypes.h> +#include <string.h> #include <functional> #include <iomanip> #include <mutex> -#include <string> #include <sstream> +#include <string> #include <unordered_map> -#include <backtrace.h> #include <android-base/macros.h> +#include <backtrace.h> #include "Allocator.h" +#include "Binder.h" #include "HeapWalker.h" #include "Leak.h" #include "LeakFolding.h" @@ -37,30 +39,34 @@ #include "Semaphore.h" #include "ThreadCapture.h" -#include "memunreachable/memunreachable.h" #include "bionic.h" #include "log.h" - -const size_t Leak::contents_length; +#include "memunreachable/memunreachable.h" using namespace std::chrono_literals; +namespace android { + +const size_t Leak::contents_length; + class MemUnreachable { public: - MemUnreachable(pid_t pid, Allocator<void> allocator) : pid_(pid), allocator_(allocator), - heap_walker_(allocator_) {} + MemUnreachable(pid_t pid, Allocator<void> allocator) + : pid_(pid), allocator_(allocator), heap_walker_(allocator_) {} bool CollectAllocations(const allocator::vector<ThreadInfo>& threads, - const allocator::vector<Mapping>& mappings); - bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit, - size_t* num_leaks, size_t* leak_bytes); + const allocator::vector<Mapping>& mappings, + const allocator::vector<uintptr_t>& refs); + bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit, size_t* num_leaks, + size_t* leak_bytes); size_t Allocations() { return heap_walker_.Allocations(); } size_t AllocationBytes() { return heap_walker_.AllocationBytes(); } + private: bool ClassifyMappings(const allocator::vector<Mapping>& mappings, - allocator::vector<Mapping>& heap_mappings, - allocator::vector<Mapping>& anon_mappings, - allocator::vector<Mapping>& globals_mappings, - allocator::vector<Mapping>& stack_mappings); + allocator::vector<Mapping>& heap_mappings, + allocator::vector<Mapping>& anon_mappings, + allocator::vector<Mapping>& globals_mappings, + allocator::vector<Mapping>& stack_mappings); DISALLOW_COPY_AND_ASSIGN(MemUnreachable); pid_t pid_; Allocator<void> allocator_; @@ -68,16 +74,18 @@ }; static void HeapIterate(const Mapping& heap_mapping, - const std::function<void(uintptr_t, size_t)>& func) { + const std::function<void(uintptr_t, size_t)>& func) { malloc_iterate(heap_mapping.begin, heap_mapping.end - heap_mapping.begin, - [](uintptr_t base, size_t size, void* arg) { - auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg); - (*f)(base, size); - }, const_cast<void*>(reinterpret_cast<const void*>(&func))); + [](uintptr_t base, size_t size, void* arg) { + auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg); + (*f)(base, size); + }, + const_cast<void*>(reinterpret_cast<const void*>(&func))); } bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads, - const allocator::vector<Mapping>& mappings) { + const allocator::vector<Mapping>& mappings, + const allocator::vector<uintptr_t>& refs) { MEM_ALOGI("searching process %d for allocations", pid_); allocator::vector<Mapping> heap_mappings{mappings}; allocator::vector<Mapping> anon_mappings{mappings}; @@ -113,13 +121,15 @@ heap_walker_.Root(thread_it->regs); } + heap_walker_.Root(refs); + MEM_ALOGI("searching done"); return true; } -bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks, - size_t limit, size_t* num_leaks, size_t* leak_bytes) { +bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit, + size_t* num_leaks, size_t* leak_bytes) { MEM_ALOGI("sweeping process %d for unreachable memory", pid_); leaks.clear(); @@ -127,7 +137,6 @@ return false; } - allocator::vector<Range> leaked1{allocator_}; heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes); @@ -152,12 +161,12 @@ // in backtrace_map. leaks.reserve(leaked.size()); - for (auto& it: leaked) { + for (auto& it : leaked) { leaks.emplace_back(); Leak* leak = &leaks.back(); - ssize_t num_backtrace_frames = malloc_backtrace(reinterpret_cast<void*>(it.range.begin), - leak->backtrace.frames, leak->backtrace.max_frames); + ssize_t num_backtrace_frames = malloc_backtrace( + reinterpret_cast<void*>(it.range.begin), leak->backtrace.frames, leak->backtrace.max_frames); if (num_backtrace_frames > 0) { leak->backtrace.num_frames = num_backtrace_frames; @@ -183,14 +192,13 @@ leak->referenced_size = it.referenced_size; leak->total_size = leak->size + leak->referenced_size; memcpy(leak->contents, reinterpret_cast<void*>(it.range.begin), - std::min(leak->size, Leak::contents_length)); + std::min(leak->size, Leak::contents_length)); } MEM_ALOGI("folding done"); - std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) { - return a.total_size > b.total_size; - }); + std::sort(leaks.begin(), leaks.end(), + [](const Leak& a, const Leak& b) { return a.total_size > b.total_size; }); if (leaks.size() > limit) { leaks.resize(limit); @@ -205,11 +213,10 @@ } bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings, - allocator::vector<Mapping>& heap_mappings, - allocator::vector<Mapping>& anon_mappings, - allocator::vector<Mapping>& globals_mappings, - allocator::vector<Mapping>& stack_mappings) -{ + allocator::vector<Mapping>& heap_mappings, + allocator::vector<Mapping>& anon_mappings, + allocator::vector<Mapping>& globals_mappings, + allocator::vector<Mapping>& stack_mappings) { heap_mappings.clear(); anon_mappings.clear(); globals_mappings.clear(); @@ -245,7 +252,8 @@ stack_mappings.emplace_back(*it); } else if (mapping_name.size() == 0) { globals_mappings.emplace_back(*it); - } else if (has_prefix(mapping_name, "[anon:") && mapping_name != "[anon:leak_detector_malloc]") { + } else if (has_prefix(mapping_name, "[anon:") && + mapping_name != "[anon:leak_detector_malloc]") { // TODO(ccross): it would be nice to treat named anonymous mappings as // possible leaks, but naming something in a .bss or .data section makes // it impossible to distinguish them from mmaped and then named mappings. @@ -256,7 +264,7 @@ return true; } -template<typename T> +template <typename T> static inline const char* plural(T val) { return (val == 1) ? "" : "s"; } @@ -279,6 +287,7 @@ ThreadCapture thread_capture(parent_pid, heap); allocator::vector<ThreadInfo> thread_info(heap); allocator::vector<Mapping> mappings(heap); + allocator::vector<uintptr_t> refs(heap); // ptrace all the threads if (!thread_capture.CaptureThreads()) { @@ -298,6 +307,11 @@ return 1; } + if (!BinderReferences(refs)) { + continue_parent_sem.Post(); + return 1; + } + // malloc must be enabled to call fork, at_fork handlers take the same // locks as ScopedDisableMalloc. All threads are paused in ptrace, so // memory state is still consistent. Unfreeze the original thread so it @@ -323,7 +337,7 @@ MemUnreachable unreachable{parent_pid, heap}; - if (!unreachable.CollectAllocations(thread_info, mappings)) { + if (!unreachable.CollectAllocations(thread_info, mappings, refs)) { _exit(2); } size_t num_allocations = unreachable.Allocations(); @@ -403,7 +417,6 @@ } std::string Leak::ToString(bool log_contents) const { - std::ostringstream oss; oss << " " << std::dec << size; @@ -492,8 +505,8 @@ oss << std::endl; for (auto it = leaks.begin(); it != leaks.end(); it++) { - oss << it->ToString(log_contents); - oss << std::endl; + oss << it->ToString(log_contents); + oss << std::endl; } return oss.str(); @@ -511,9 +524,11 @@ return info.ToString(log_contents); } +} // namespace android + bool LogUnreachableMemory(bool log_contents, size_t limit) { - UnreachableMemoryInfo info; - if (!GetUnreachableMemory(info, limit)) { + android::UnreachableMemoryInfo info; + if (!android::GetUnreachableMemory(info, limit)) { return false; } @@ -523,10 +538,9 @@ return true; } - bool NoLeaks() { - UnreachableMemoryInfo info; - if (!GetUnreachableMemory(info, 0)) { + android::UnreachableMemoryInfo info; + if (!android::GetUnreachableMemory(info, 0)) { return false; }
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp index 57b2321..9a06870 100644 --- a/libmemunreachable/ProcessMappings.cpp +++ b/libmemunreachable/ProcessMappings.cpp
@@ -14,8 +14,8 @@ * limitations under the License. */ -#include <inttypes.h> #include <fcntl.h> +#include <inttypes.h> #include <string.h> #include <unistd.h> @@ -25,6 +25,8 @@ #include "ProcessMappings.h" #include "log.h" +namespace android { + // This function is not re-entrant since it uses a static buffer for // the line data. bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) { @@ -42,8 +44,8 @@ int name_pos; char perms[5]; Mapping mapping{}; - if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n", - &mapping.begin, &mapping.end, perms, &name_pos) == 3) { + if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n", &mapping.begin, + &mapping.end, perms, &name_pos) == 3) { if (perms[0] == 'r') { mapping.read = true; } @@ -64,3 +66,5 @@ } return true; } + +} // namespace android
diff --git a/libmemunreachable/ProcessMappings.h b/libmemunreachable/ProcessMappings.h index d3b7496..a0e97e9 100644 --- a/libmemunreachable/ProcessMappings.h +++ b/libmemunreachable/ProcessMappings.h
@@ -19,6 +19,8 @@ #include "Allocator.h" +namespace android { + struct Mapping { uintptr_t begin; uintptr_t end; @@ -33,4 +35,6 @@ // the line data. bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings); -#endif // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_ +} // namespace android + +#endif // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp index 73b0493..aca2a82 100644 --- a/libmemunreachable/PtracerThread.cpp +++ b/libmemunreachable/PtracerThread.cpp
@@ -23,17 +23,19 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> #include <sys/mman.h> #include <sys/syscall.h> #include <sys/types.h> #include <sys/wait.h> +#include <unistd.h> #include "android-base/macros.h" +#include "PtracerThread.h" #include "anon_vma_naming.h" #include "log.h" -#include "PtracerThread.h" + +namespace android { class Stack { public: @@ -41,7 +43,7 @@ int prot = PROT_READ | PROT_WRITE; int flags = MAP_PRIVATE | MAP_ANONYMOUS; page_size_ = sysconf(_SC_PAGE_SIZE); - size_ += page_size_*2; // guard pages + size_ += page_size_ * 2; // guard pages base_ = mmap(NULL, size_, prot, flags, -1, 0); if (base_ == MAP_FAILED) { base_ = NULL; @@ -52,22 +54,20 @@ mprotect(base_, page_size_, PROT_NONE); mprotect(top(), page_size_, PROT_NONE); }; - ~Stack() { - munmap(base_, size_); - }; + ~Stack() { munmap(base_, size_); }; void* top() { return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_) + size_ - page_size_); }; + private: DISALLOW_COPY_AND_ASSIGN(Stack); - void *base_; + void* base_; size_t size_; size_t page_size_; }; -PtracerThread::PtracerThread(const std::function<int()>& func) : - child_pid_(0) { +PtracerThread::PtracerThread(const std::function<int()>& func) : child_pid_(0) { stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN); if (stack_->top() == nullptr) { MEM_LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno)); @@ -93,14 +93,13 @@ std::unique_lock<std::mutex> lk(m_); // Convert from void(*)(void*) to lambda with captures - auto proxy = [](void *arg) -> int { + auto proxy = [](void* arg) -> int { prctl(PR_SET_NAME, "libmemunreachable ptrace thread"); return (*reinterpret_cast<std::function<int()>*>(arg))(); }; - child_pid_ = clone(proxy, stack_->top(), - CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/, - reinterpret_cast<void*>(&func_)); + child_pid_ = clone(proxy, stack_->top(), CLONE_VM | CLONE_FS | CLONE_FILES /*|CLONE_UNTRACED*/, + reinterpret_cast<void*>(&func_)); if (child_pid_ < 0) { MEM_ALOGE("failed to clone child: %s", strerror(errno)); return false; @@ -151,3 +150,5 @@ void PtracerThread::ClearTracer() { prctl(PR_SET_PTRACER, 0); } + +} // namespace android
diff --git a/libmemunreachable/PtracerThread.h b/libmemunreachable/PtracerThread.h index f88b599..4f9c420 100644 --- a/libmemunreachable/PtracerThread.h +++ b/libmemunreachable/PtracerThread.h
@@ -24,6 +24,8 @@ #include "Allocator.h" +namespace android { + class Stack; // PtracerThread is similar to std::thread, except that it creates a "thread" @@ -36,6 +38,7 @@ ~PtracerThread(); bool Start(); int Join(); + private: void SetTracer(pid_t); void ClearTracer(); @@ -47,4 +50,6 @@ pid_t child_pid_; }; -#endif // LIBMEMUNREACHABLE_PTRACER_THREAD_H_ +} // namespace android + +#endif // LIBMEMUNREACHABLE_PTRACER_THREAD_H_
diff --git a/libmemunreachable/ScopedAlarm.h b/libmemunreachable/ScopedAlarm.h index 287f479..bb50b9e 100644 --- a/libmemunreachable/ScopedAlarm.h +++ b/libmemunreachable/ScopedAlarm.h
@@ -23,15 +23,15 @@ #include <chrono> #include <functional> +namespace android { + class ScopedAlarm { public: ScopedAlarm(std::chrono::microseconds us, std::function<void()> func) { func_ = func; - struct sigaction oldact{}; - struct sigaction act{}; - act.sa_handler = [](int) { - ScopedAlarm::func_(); - }; + struct sigaction oldact {}; + struct sigaction act {}; + act.sa_handler = [](int) { ScopedAlarm::func_(); }; sigaction(SIGALRM, &act, &oldact); std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us); @@ -43,11 +43,15 @@ ~ScopedAlarm() { itimerval t = itimerval{}; setitimer(ITIMER_REAL, &t, NULL); - struct sigaction act{}; + struct sigaction act {}; act.sa_handler = SIG_DFL; sigaction(SIGALRM, &act, NULL); } + private: static std::function<void()> func_; }; + +} // namespace android + #endif
diff --git a/libmemunreachable/ScopedDisableMalloc.h b/libmemunreachable/ScopedDisableMalloc.h index 758d317..655e826 100644 --- a/libmemunreachable/ScopedDisableMalloc.h +++ b/libmemunreachable/ScopedDisableMalloc.h
@@ -21,16 +21,16 @@ #include "android-base/macros.h" +#include "ScopedAlarm.h" #include "bionic.h" #include "log.h" -#include "ScopedAlarm.h" -class DisableMallocGuard{ +namespace android { + +class DisableMallocGuard { public: - DisableMallocGuard() : disabled_(false){} - ~DisableMallocGuard() { - Enable(); - } + DisableMallocGuard() : disabled_(false) {} + ~DisableMallocGuard() { Enable(); } void Disable() { if (!disabled_) { @@ -45,6 +45,7 @@ disabled_ = false; } } + private: DISALLOW_COPY_AND_ASSIGN(DisableMallocGuard); bool disabled_; @@ -59,13 +60,9 @@ // here. class ScopedDisableMalloc { public: - ScopedDisableMalloc() { - disable_malloc_.Disable(); - } + ScopedDisableMalloc() { disable_malloc_.Disable(); } - ~ScopedDisableMalloc() { - disable_malloc_.Enable(); - } + ~ScopedDisableMalloc() { disable_malloc_.Enable(); } private: DISALLOW_COPY_AND_ASSIGN(ScopedDisableMalloc); @@ -74,18 +71,15 @@ class ScopedDisableMallocTimeout { public: - explicit ScopedDisableMallocTimeout(std::chrono::milliseconds timeout = std::chrono::milliseconds(2000)) : - timeout_(timeout), timed_out_(false), disable_malloc_() { + explicit ScopedDisableMallocTimeout( + std::chrono::milliseconds timeout = std::chrono::milliseconds(2000)) + : timeout_(timeout), timed_out_(false), disable_malloc_() { Disable(); } - ~ScopedDisableMallocTimeout() { - Enable(); - } + ~ScopedDisableMallocTimeout() { Enable(); } - bool timed_out() { - return timed_out_; - } + bool timed_out() { return timed_out_; } void Enable() { disable_malloc_.Enable(); @@ -110,4 +104,6 @@ DisableMallocGuard disable_malloc_; }; -#endif // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_ +} // namespace android + +#endif // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h index 7f44953..adabfd8 100644 --- a/libmemunreachable/ScopedPipe.h +++ b/libmemunreachable/ScopedPipe.h
@@ -21,6 +21,8 @@ #include "log.h" +namespace android { + class ScopedPipe { public: ScopedPipe() : pipefd_{-1, -1} { @@ -29,28 +31,22 @@ MEM_LOG_ALWAYS_FATAL("failed to open pipe"); } } - ~ScopedPipe() { - Close(); - } + ~ScopedPipe() { Close(); } ScopedPipe(ScopedPipe&& other) { SetReceiver(other.ReleaseReceiver()); SetSender(other.ReleaseSender()); } - ScopedPipe& operator = (ScopedPipe&& other) { + ScopedPipe& operator=(ScopedPipe&& other) { SetReceiver(other.ReleaseReceiver()); SetSender(other.ReleaseSender()); return *this; } - void CloseReceiver() { - close(ReleaseReceiver()); - } + void CloseReceiver() { close(ReleaseReceiver()); } - void CloseSender() { - close(ReleaseSender()); - } + void CloseSender() { close(ReleaseSender()); } void Close() { CloseReceiver(); @@ -78,4 +74,7 @@ int pipefd_[2]; }; + +} // namespace android + #endif
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h index ada2ae4..ff53fad 100644 --- a/libmemunreachable/ScopedSignalHandler.h +++ b/libmemunreachable/ScopedSignalHandler.h
@@ -26,18 +26,18 @@ #include "log.h" +namespace android { + class ScopedSignalHandler { public: using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>; explicit ScopedSignalHandler(Allocator<Fn> allocator) : allocator_(allocator), signal_(-1) {} - ~ScopedSignalHandler() { - reset(); - } + ~ScopedSignalHandler() { reset(); } template <class F> void install(int signal, F&& f) { - MEM_LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed"); + if (signal_ != -1) MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed"); handler_ = SignalFn(std::allocator_arg, allocator_, [=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); }); @@ -65,7 +65,6 @@ } } - private: using SignalFn = std::function<void(int, siginfo_t*, void*)>; DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler); @@ -77,4 +76,6 @@ static SignalFn handler_; }; -#endif // LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_ +} // namespace android + +#endif // LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
diff --git a/libmemunreachable/Semaphore.h b/libmemunreachable/Semaphore.h index 6bcf4ea..cd73972 100644 --- a/libmemunreachable/Semaphore.h +++ b/libmemunreachable/Semaphore.h
@@ -22,6 +22,8 @@ #include "android-base/macros.h" +namespace android { + class Semaphore { public: explicit Semaphore(int count = 0) : count_(count) {} @@ -29,7 +31,7 @@ void Wait(std::chrono::milliseconds ms) { std::unique_lock<std::mutex> lk(m_); - cv_.wait_for(lk, ms, [&]{ + cv_.wait_for(lk, ms, [&] { if (count_ > 0) { count_--; return true; @@ -44,6 +46,7 @@ } cv_.notify_one(); } + private: DISALLOW_COPY_AND_ASSIGN(Semaphore); @@ -52,5 +55,6 @@ std::condition_variable cv_; }; +} // namespace android -#endif // LIBMEMUNREACHABLE_SEMAPHORE_H_ +#endif // LIBMEMUNREACHABLE_SEMAPHORE_H_
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h index 2546341..355679f 100644 --- a/libmemunreachable/Tarjan.h +++ b/libmemunreachable/Tarjan.h
@@ -24,7 +24,9 @@ #include "Allocator.h" -template<class T> +namespace android { + +template <class T> class Node { public: allocator::set<Node<T>*> references_in; @@ -34,39 +36,41 @@ T* ptr; - Node(T* ptr, Allocator<Node> allocator) : references_in(allocator), references_out(allocator), - ptr(ptr) {}; + Node(T* ptr, Allocator<Node> allocator) + : references_in(allocator), references_out(allocator), ptr(ptr){}; Node(Node&& rhs) = default; void Edge(Node<T>* ref) { references_out.emplace(ref); ref->references_in.emplace(this); } - template<class F> + template <class F> void Foreach(F&& f) { - for (auto& node: references_out) { + for (auto& node : references_out) { f(node->ptr); } } + private: DISALLOW_COPY_AND_ASSIGN(Node<T>); }; -template<class T> +template <class T> using Graph = allocator::vector<Node<T>*>; -template<class T> +template <class T> using SCC = allocator::vector<Node<T>*>; -template<class T> +template <class T> using SCCList = allocator::vector<SCC<T>>; -template<class T> +template <class T> class TarjanAlgorithm { public: - explicit TarjanAlgorithm(Allocator<void> allocator) : index_(0), - stack_(allocator), components_(allocator) {} + explicit TarjanAlgorithm(Allocator<void> allocator) + : index_(0), stack_(allocator), components_(allocator) {} void Execute(Graph<T>& graph, SCCList<T>& out); + private: static constexpr size_t UNDEFINED_INDEX = static_cast<size_t>(-1); void Tarjan(Node<T>* vertex, Graph<T>& graph); @@ -76,17 +80,17 @@ SCCList<T> components_; }; -template<class T> +template <class T> void TarjanAlgorithm<T>::Execute(Graph<T>& graph, SCCList<T>& out) { stack_.clear(); components_.clear(); index_ = 0; - for (auto& it: graph) { + for (auto& it : graph) { it->index = UNDEFINED_INDEX; it->lowlink = UNDEFINED_INDEX; } - for (auto& it: graph) { + for (auto& it : graph) { if (it->index == UNDEFINED_INDEX) { Tarjan(it, graph); } @@ -94,14 +98,14 @@ out.swap(components_); } -template<class T> +template <class T> void TarjanAlgorithm<T>::Tarjan(Node<T>* vertex, Graph<T>& graph) { assert(vertex->index == UNDEFINED_INDEX); vertex->index = index_; vertex->lowlink = index_; index_++; stack_.push_back(vertex); - for (auto& it: vertex->references_out) { + for (auto& it : vertex->references_out) { Node<T>* vertex_next = it; if (vertex_next->index == UNDEFINED_INDEX) { Tarjan(vertex_next, graph); @@ -123,10 +127,12 @@ } } -template<class T> +template <class T> void Tarjan(Graph<T>& graph, SCCList<T>& out) { TarjanAlgorithm<T> tarjan{graph.get_allocator()}; tarjan.Execute(graph, out); } -#endif // LIBMEMUNREACHABLE_TARJAN_H_ +} // namespace android + +#endif // LIBMEMUNREACHABLE_TARJAN_H_
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp index 3891f2d..45eb55d 100644 --- a/libmemunreachable/ThreadCapture.cpp +++ b/libmemunreachable/ThreadCapture.cpp
@@ -21,13 +21,13 @@ #include <fcntl.h> #include <limits.h> #include <stdlib.h> -#include <unistd.h> #include <sys/ptrace.h> #include <sys/stat.h> #include <sys/syscall.h> #include <sys/types.h> #include <sys/uio.h> #include <sys/wait.h> +#include <unistd.h> #include <map> #include <memory> @@ -39,6 +39,8 @@ #include "Allocator.h" #include "log.h" +namespace android { + // bionic interfaces used: // atoi // strlcat @@ -50,12 +52,12 @@ // Convert a pid > 0 to a string. sprintf might allocate, so we can't use it. // Returns a pointer somewhere in buf to a null terminated string, or NULL // on error. -static char *pid_to_str(char *buf, size_t len, pid_t pid) { +static char* pid_to_str(char* buf, size_t len, pid_t pid) { if (pid <= 0) { return nullptr; } - char *ptr = buf + len - 1; + char* ptr = buf + len - 1; *ptr = 0; while (pid > 0) { ptr--; @@ -79,6 +81,7 @@ bool ReleaseThread(pid_t tid); bool CapturedThreadInfo(ThreadInfoList& threads); void InjectTestFunc(std::function<void(pid_t)>&& f) { inject_test_func_ = f; } + private: int CaptureThread(pid_t tid); bool ReleaseThread(pid_t tid, unsigned int signal); @@ -92,9 +95,8 @@ std::function<void(pid_t)> inject_test_func_; }; -ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator) : - captured_threads_(allocator), allocator_(allocator), pid_(pid) { -} +ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator) + : captured_threads_(allocator), allocator_(allocator), pid_(pid) {} bool ThreadCaptureImpl::ListThreads(TidList& tids) { tids.clear(); @@ -115,11 +117,11 @@ } struct linux_dirent64 { - uint64_t d_ino; - int64_t d_off; - uint16_t d_reclen; - char d_type; - char d_name[]; + uint64_t d_ino; + int64_t d_off; + uint16_t d_reclen; + char d_type; + char d_name[]; } __attribute((packed)); char dirent_buf[4096]; ssize_t nread; @@ -209,7 +211,7 @@ bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) { thread_info.tid = tid; - const unsigned int max_num_regs = 128; // larger than number of registers on any device + const unsigned int max_num_regs = 128; // larger than number of registers on any device uintptr_t regs[max_num_regs]; struct iovec iovec; iovec.iov_base = ®s; @@ -243,7 +245,7 @@ thread_info.stack = std::pair<uintptr_t, uintptr_t>(regs[sp], 0); - return true; + return true; } int ThreadCaptureImpl::CaptureThread(pid_t tid) { @@ -266,7 +268,7 @@ unsigned int resume_signal = 0; - unsigned int signal = WSTOPSIG(status); + unsigned int signal = WSTOPSIG(status); if ((status >> 16) == PTRACE_EVENT_STOP) { switch (signal) { case SIGSTOP: @@ -307,7 +309,7 @@ bool ThreadCaptureImpl::ReleaseThreads() { bool ret = true; - for (auto it = captured_threads_.begin(); it != captured_threads_.end(); ) { + for (auto it = captured_threads_.begin(); it != captured_threads_.end();) { if (ReleaseThread(it->first, it->second)) { it = captured_threads_.erase(it); } else { @@ -361,3 +363,5 @@ void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) { impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f)); } + +} // namespace android
diff --git a/libmemunreachable/ThreadCapture.h b/libmemunreachable/ThreadCapture.h index 1022cad..961cb60 100644 --- a/libmemunreachable/ThreadCapture.h +++ b/libmemunreachable/ThreadCapture.h
@@ -21,6 +21,8 @@ #include "Allocator.h" +namespace android { + struct ThreadInfo { pid_t tid; allocator::vector<uintptr_t> regs; @@ -33,7 +35,7 @@ class ThreadCaptureImpl; class ThreadCapture { -public: + public: ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator); ~ThreadCapture(); @@ -44,11 +46,13 @@ bool CapturedThreadInfo(ThreadInfoList& threads); void InjectTestFunc(std::function<void(pid_t)>&& f); -private: + private: ThreadCapture(const ThreadCapture&) = delete; void operator=(const ThreadCapture&) = delete; Allocator<ThreadCaptureImpl>::unique_ptr impl_; }; +} // namespace android + #endif
diff --git a/libmemunreachable/anon_vma_naming.h b/libmemunreachable/anon_vma_naming.h index 1e4ade1..fb31e41 100644 --- a/libmemunreachable/anon_vma_naming.h +++ b/libmemunreachable/anon_vma_naming.h
@@ -19,7 +19,7 @@ #include <sys/prctl.h> -#define PR_SET_VMA 0x53564d41 -#define PR_SET_VMA_ANON_NAME 0 +#define PR_SET_VMA 0x53564d41 +#define PR_SET_VMA_ANON_NAME 0 -#endif // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_ +#endif // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
diff --git a/libmemunreachable/bionic.h b/libmemunreachable/bionic.h index 83d07a8..dd1ec79 100644 --- a/libmemunreachable/bionic.h +++ b/libmemunreachable/bionic.h
@@ -17,9 +17,9 @@ #ifndef LIBMEMUNREACHABLE_BIONIC_H_ #define LIBMEMUNREACHABLE_BIONIC_H_ -#include <sys/cdefs.h> #include <stdint.h> #include <stdlib.h> +#include <sys/cdefs.h> __BEGIN_DECLS @@ -27,9 +27,9 @@ extern void malloc_disable(); extern void malloc_enable(); extern int malloc_iterate(uintptr_t base, size_t size, - void (*callback)(uintptr_t base, size_t size, void* arg), void* arg); + void (*callback)(uintptr_t base, size_t size, void* arg), void* arg); extern ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count); __END_DECLS -#endif // LIBMEMUNREACHABLE_BIONIC_H_ +#endif // LIBMEMUNREACHABLE_BIONIC_H_
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h index 9b227fd..438fcaf 100644 --- a/libmemunreachable/include/memunreachable/memunreachable.h +++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -17,12 +17,15 @@ #ifndef LIBMEMUNREACHABLE_MEMUNREACHABLE_H_ #define LIBMEMUNREACHABLE_MEMUNREACHABLE_H_ +#include <string.h> #include <sys/cdefs.h> #ifdef __cplusplus -#include <vector> #include <string> +#include <vector> + +namespace android { struct Leak { uintptr_t begin; @@ -73,6 +76,8 @@ std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100); +} // namespace android + #endif __BEGIN_DECLS @@ -83,4 +88,4 @@ __END_DECLS -#endif // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_ +#endif // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h index 1725c53..44c5f85 100644 --- a/libmemunreachable/log.h +++ b/libmemunreachable/log.h
@@ -26,13 +26,21 @@ #define MEM_ALOGE(...) async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, ##__VA_ARGS__) #define MEM_ALOGW(...) async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, ##__VA_ARGS__) #define MEM_ALOGI(...) async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG, ##__VA_ARGS__) -#define MEM_ALOGV(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__) +#define MEM_ALOGV_IMPL(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__) + +#ifdef NDEBUG +#define MEM_ALOGV(...) \ + do { \ + if (0) { \ + MEM_ALOGV_IMPL(__VA_ARGS__); \ + } \ + } while (0) +#else +#define MEM_ALOGV(...) MEM_ALOGV_IMPL(__VA_ARGS__) +#endif #define MEM_LOG_ALWAYS_FATAL(...) async_safe_fatal(__VA_ARGS__) -#define MEM_LOG_ALWAYS_FATAL_IF(cond, ...) \ - ((__predict_false(cond)) ? async_safe_fatal(__VA_ARGS__) : (void)0) - #else #include <log/log.h> @@ -43,8 +51,7 @@ #define MEM_ALOGI ALOGI #define MEM_LOG_ALWAYS_FATAL LOG_ALWAYS_FATAL -#define MEM_LOG_ALWAYS_FATAL_IF LOG_ALWAYS_FATAL_IF #endif -#endif // LIBMEMUNREACHABLE_LOG_H_ +#endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libmemunreachable/tests/Allocator_test.cpp b/libmemunreachable/tests/Allocator_test.cpp index 21c8218..8991a7b 100644 --- a/libmemunreachable/tests/Allocator_test.cpp +++ b/libmemunreachable/tests/Allocator_test.cpp
@@ -16,44 +16,44 @@ #include <Allocator.h> -#include <gtest/gtest.h> #include <ScopedDisableMalloc.h> +#include <gtest/gtest.h> +namespace android { std::function<void()> ScopedAlarm::func_; class AllocatorTest : public testing::Test { protected: AllocatorTest() : heap(), disable_malloc_() {} - virtual void SetUp() { - heap_count = 0; - } + virtual void SetUp() { heap_count = 0; } virtual void TearDown() { ASSERT_EQ(heap_count, 0); ASSERT_TRUE(heap.empty()); ASSERT_FALSE(disable_malloc_.timed_out()); } Heap heap; + private: ScopedDisableMallocTimeout disable_malloc_; }; TEST_F(AllocatorTest, simple) { Allocator<char[100]> allocator(heap); - void *ptr = allocator.allocate(); + void* ptr = allocator.allocate(); ASSERT_TRUE(ptr != NULL); allocator.deallocate(ptr); } TEST_F(AllocatorTest, multiple) { Allocator<char[100]> allocator(heap); - void *ptr1 = allocator.allocate(); + void* ptr1 = allocator.allocate(); ASSERT_TRUE(ptr1 != NULL); - void *ptr2 = allocator.allocate(); + void* ptr2 = allocator.allocate(); ASSERT_TRUE(ptr2 != NULL); ASSERT_NE(ptr1, ptr2); allocator.deallocate(ptr1); - void *ptr3 = allocator.allocate(); + void* ptr3 = allocator.allocate(); ASSERT_EQ(ptr1, ptr3); allocator.deallocate(ptr3); allocator.deallocate(ptr2); @@ -63,7 +63,7 @@ const int num = 4096; const int size = 128; Allocator<char[size]> allocator(heap); - void *ptr[num]; + void* ptr[num]; for (int i = 0; i < num; i++) { ptr[i] = allocator.allocate(); memset(ptr[i], 0xaa, size); @@ -87,7 +87,7 @@ TEST_F(AllocatorTest, large) { const size_t size = 1024 * 1024; Allocator<char[size]> allocator(heap); - void *ptr = allocator.allocate(); + void* ptr = allocator.allocate(); memset(ptr, 0xaa, size); allocator.deallocate(ptr); } @@ -96,7 +96,7 @@ const int num = 128; const int size = 1024 * 1024; Allocator<char[size]> allocator(heap); - void *ptr[num]; + void* ptr[num]; for (int i = 0; i < num; i++) { ptr[i] = allocator.allocate(); memset(ptr[i], 0xaa, size); @@ -172,3 +172,5 @@ ASSERT_NE(ptr, nullptr); } + +} // namespace android
diff --git a/libmemunreachable/tests/AndroidTest.xml b/libmemunreachable/tests/AndroidTest.xml new file mode 100644 index 0000000..604c0ec --- /dev/null +++ b/libmemunreachable/tests/AndroidTest.xml
@@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Config for memunreachable_test"> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" value="memunreachable_test->/data/local/tmp/memunreachable_test" /> + </target_preparer> + <option name="test-suite-tag" value="apct" /> + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="memunreachable_test" /> + </test> +</configuration>
diff --git a/libmemunreachable/tests/Binder_test.cpp b/libmemunreachable/tests/Binder_test.cpp new file mode 100644 index 0000000..6e85d5a --- /dev/null +++ b/libmemunreachable/tests/Binder_test.cpp
@@ -0,0 +1,156 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> + +#include <binder/Binder.h> +#include <binder/IBinder.h> +#include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/ProcessState.h> + +#include <gtest/gtest.h> + +#include "Allocator.h" +#include "Binder.h" + +namespace android { + +static const String16 service_name("test.libmemunreachable_binder"); + +class BinderService : public BBinder { + public: + BinderService() = default; + virtual ~BinderService() = default; + + virtual status_t onTransact(uint32_t /*code*/, const Parcel& data, Parcel* reply, + uint32_t /*flags*/ = 0) { + reply->writeStrongBinder(ref); + ref = data.readStrongBinder(); + return 0; + } + + private: + sp<IBinder> ref; +}; + +class BinderObject : public BBinder { + public: + BinderObject() = default; + ~BinderObject() = default; +}; + +class ServiceProcess { + public: + ServiceProcess() : child_(0) {} + ~ServiceProcess() { Stop(); } + + bool Run() { + pid_t ret = fork(); + if (ret < 0) { + return false; + } else if (ret == 0) { + // child + _exit(Service()); + } else { + // parent + child_ = ret; + return true; + } + } + + bool Stop() { + if (child_ > 0) { + if (kill(child_, SIGTERM)) { + return false; + } + int status = 0; + if (TEMP_FAILURE_RETRY(waitpid(child_, &status, 0)) != child_) { + return false; + } + child_ = 0; + return WIFEXITED(status) && WEXITSTATUS(status) == 0; + } + + return true; + } + + int Service() { + sp<ProcessState> proc{ProcessState::self()}; + sp<IServiceManager> sm = defaultServiceManager(); + if (sm == nullptr) { + fprintf(stderr, "Failed to get service manager\n"); + return 1; + } + if (sm->addService(service_name, new BinderService()) != OK) { + fprintf(stderr, "Failed to add test service\n"); + return 1; + } + proc->startThreadPool(); + pause(); + return 0; + } + + private: + pid_t child_; +}; + +class BinderTest : public ::testing::Test { + protected: + ServiceProcess service_process_; +}; + +TEST_F(BinderTest, binder) { + ServiceProcess service_process; + ASSERT_TRUE(service_process.Run()); + + sp<IServiceManager> sm = defaultServiceManager(); + ASSERT_TRUE(sm != nullptr); + + // A small sleep allows the service to start, which + // prevents a longer sleep in getService. + usleep(100000); + + sp<IBinder> service = sm->getService(service_name); + ASSERT_TRUE(service != nullptr); + + sp<IBinder> binder{new BinderObject()}; + + Parcel send; + Parcel reply; + + send.writeStrongBinder(binder); + status_t rv = service->transact(0, send, &reply); + ASSERT_EQ(static_cast<status_t>(OK), rv); + + Heap heap; + allocator::vector<uintptr_t> refs{heap}; + + ASSERT_TRUE(BinderReferences(refs)); + + bool found_ref = false; + for (auto ref : refs) { + if (ref == reinterpret_cast<uintptr_t>(binder.get())) { + found_ref = true; + } + } + + ASSERT_TRUE(found_ref); +} + +} // namespace android
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp index 4e6155b..c630049 100644 --- a/libmemunreachable/tests/DisableMalloc_test.cpp +++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -19,11 +19,13 @@ #include <chrono> #include <functional> -#include <gtest/gtest.h> #include <ScopedDisableMalloc.h> +#include <gtest/gtest.h> using namespace std::chrono_literals; +namespace android { + class DisableMallocTest : public ::testing::Test { protected: void alarm(std::chrono::microseconds us) { @@ -36,75 +38,83 @@ }; TEST_F(DisableMallocTest, reenable) { - ASSERT_EXIT({ - alarm(100ms); - void *ptr1 = malloc(128); - ASSERT_NE(ptr1, nullptr); - free(ptr1); - { - ScopedDisableMalloc disable_malloc; - } - void *ptr2 = malloc(128); - ASSERT_NE(ptr2, nullptr); - free(ptr2); - _exit(1); - }, ::testing::ExitedWithCode(1), ""); + ASSERT_EXIT( + { + alarm(100ms); + void* ptr1 = malloc(128); + ASSERT_NE(ptr1, nullptr); + free(ptr1); + { ScopedDisableMalloc disable_malloc; } + void* ptr2 = malloc(128); + ASSERT_NE(ptr2, nullptr); + free(ptr2); + _exit(1); + }, + ::testing::ExitedWithCode(1), ""); } TEST_F(DisableMallocTest, deadlock_allocate) { - ASSERT_DEATH({ - void *ptr = malloc(128); - ASSERT_NE(ptr, nullptr); - free(ptr); - { - alarm(100ms); - ScopedDisableMalloc disable_malloc; - void* ptr = malloc(128); - ASSERT_NE(ptr, nullptr); - free(ptr); - } - }, ""); + ASSERT_DEATH( + { + void* ptr = malloc(128); + ASSERT_NE(ptr, nullptr); + free(ptr); + { + alarm(100ms); + ScopedDisableMalloc disable_malloc; + void* ptr = malloc(128); + ASSERT_NE(ptr, nullptr); + free(ptr); + } + }, + ""); } TEST_F(DisableMallocTest, deadlock_new) { - ASSERT_DEATH({ - char* ptr = new(char); - ASSERT_NE(ptr, nullptr); - delete(ptr); - { - alarm(100ms); - ScopedDisableMalloc disable_malloc; - char* ptr = new (std::nothrow)(char); - ASSERT_NE(ptr, nullptr); - delete(ptr); - } - }, ""); + ASSERT_DEATH( + { + char* ptr = new (char); + ASSERT_NE(ptr, nullptr); + delete (ptr); + { + alarm(100ms); + ScopedDisableMalloc disable_malloc; + char* ptr = new (std::nothrow)(char); + ASSERT_NE(ptr, nullptr); + delete (ptr); + } + }, + ""); } TEST_F(DisableMallocTest, deadlock_delete) { - ASSERT_DEATH({ - char* ptr = new(char); - ASSERT_NE(ptr, nullptr); - { - alarm(250ms); - ScopedDisableMalloc disable_malloc; - delete(ptr); - // Force ptr usage or this code gets optimized away by the arm64 compiler. - ASSERT_NE(ptr, nullptr); - } - }, ""); + ASSERT_DEATH( + { + char* ptr = new (char); + ASSERT_NE(ptr, nullptr); + { + alarm(250ms); + ScopedDisableMalloc disable_malloc; + delete (ptr); + // Force ptr usage or this code gets optimized away by the arm64 compiler. + ASSERT_NE(ptr, nullptr); + } + }, + ""); } TEST_F(DisableMallocTest, deadlock_free) { - ASSERT_DEATH({ - void *ptr = malloc(128); - ASSERT_NE(ptr, nullptr); - { - alarm(100ms); - ScopedDisableMalloc disable_malloc; - free(ptr); - } - }, ""); + ASSERT_DEATH( + { + void* ptr = malloc(128); + ASSERT_NE(ptr, nullptr); + { + alarm(100ms); + ScopedDisableMalloc disable_malloc; + free(ptr); + } + }, + ""); } TEST_F(DisableMallocTest, deadlock_fork) { @@ -113,6 +123,8 @@ alarm(100ms); ScopedDisableMalloc disable_malloc; fork(); - } - }, ""); } +}, ""); +} + +} // namespace android
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp index 98e4aa1..84a0ec6 100644 --- a/libmemunreachable/tests/HeapWalker_test.cpp +++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -19,10 +19,12 @@ #include "HeapWalker.h" -#include <gtest/gtest.h> #include <ScopedDisableMalloc.h> +#include <gtest/gtest.h> #include "Allocator.h" +namespace android { + class HeapWalkerTest : public ::testing::Test { public: HeapWalkerTest() : disable_malloc_(), heap_() {} @@ -172,20 +174,20 @@ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes)); EXPECT_EQ(2U, num_leaks); - EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes); + EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes); ASSERT_EQ(2U, leaked.size()); } TEST_F(HeapWalkerTest, segv) { const size_t page_size = sysconf(_SC_PAGE_SIZE); - void* buffer1 = mmap(NULL, page_size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + void* buffer1 = mmap(NULL, page_size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(buffer1, nullptr); void* buffer2; buffer2 = &buffer1; HeapWalker heap_walker(heap_); - heap_walker.Allocation(buffer_begin(buffer1), buffer_begin(buffer1)+page_size); + heap_walker.Allocation(buffer_begin(buffer1), buffer_begin(buffer1) + page_size); heap_walker.Root(buffer_begin(buffer2), buffer_end(buffer2)); ASSERT_EQ(true, heap_walker.DetectLeaks()); @@ -199,3 +201,5 @@ EXPECT_EQ(0U, leaked_bytes); ASSERT_EQ(0U, leaked.size()); } + +} // namespace android
diff --git a/libmemunreachable/tests/HostMallocStub.cpp b/libmemunreachable/tests/HostMallocStub.cpp index a7e3f07..0ef0487 100644 --- a/libmemunreachable/tests/HostMallocStub.cpp +++ b/libmemunreachable/tests/HostMallocStub.cpp
@@ -16,8 +16,6 @@ #include "bionic.h" -void malloc_disable() { -} +void malloc_disable() {} -void malloc_enable() { -} +void malloc_enable() {}
diff --git a/libmemunreachable/tests/LeakFolding_test.cpp b/libmemunreachable/tests/LeakFolding_test.cpp index e85df5f..f5b3631 100644 --- a/libmemunreachable/tests/LeakFolding_test.cpp +++ b/libmemunreachable/tests/LeakFolding_test.cpp
@@ -14,13 +14,15 @@ * limitations under the License. */ -#include "HeapWalker.h" #include "LeakFolding.h" +#include "HeapWalker.h" -#include <gtest/gtest.h> #include <ScopedDisableMalloc.h> +#include <gtest/gtest.h> #include "Allocator.h" +namespace android { + class LeakFoldingTest : public ::testing::Test { public: LeakFoldingTest() : disable_malloc_(), heap_() {} @@ -84,7 +86,7 @@ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes)); EXPECT_EQ(2U, num_leaks); - EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes); + EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes); ASSERT_EQ(2U, leaked.size()); EXPECT_EQ(0U, leaked[0].referenced_count); EXPECT_EQ(0U, leaked[0].referenced_size); @@ -113,7 +115,7 @@ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes)); EXPECT_EQ(2U, num_leaks); - EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes); + EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes); ASSERT_EQ(1U, leaked.size()); EXPECT_EQ(1U, leaked[0].referenced_count); EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size); @@ -144,10 +146,10 @@ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes)); EXPECT_EQ(3U, num_leaks); - EXPECT_EQ(3*sizeof(uintptr_t), leaked_bytes); + EXPECT_EQ(3 * sizeof(uintptr_t), leaked_bytes); ASSERT_EQ(1U, leaked.size()); EXPECT_EQ(2U, leaked[0].referenced_count); - EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size); + EXPECT_EQ(2 * sizeof(uintptr_t), leaked[0].referenced_size); } TEST_F(LeakFoldingTest, dominator_cycle) { @@ -175,13 +177,13 @@ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes)); EXPECT_EQ(3U, num_leaks); - EXPECT_EQ(5*sizeof(uintptr_t), leaked_bytes); + EXPECT_EQ(5 * sizeof(uintptr_t), leaked_bytes); ASSERT_EQ(2U, leaked.size()); EXPECT_EQ(2U, leaked[0].referenced_count); - EXPECT_EQ(3*sizeof(uintptr_t), leaked[0].referenced_size); + EXPECT_EQ(3 * sizeof(uintptr_t), leaked[0].referenced_size); EXPECT_EQ(2U, leaked[1].referenced_count); - EXPECT_EQ(3*sizeof(uintptr_t), leaked[1].referenced_size); + EXPECT_EQ(3 * sizeof(uintptr_t), leaked[1].referenced_size); } TEST_F(LeakFoldingTest, two_cycles) { @@ -218,12 +220,12 @@ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes)); EXPECT_EQ(6U, num_leaks); - EXPECT_EQ(6*sizeof(uintptr_t), leaked_bytes); + EXPECT_EQ(6 * sizeof(uintptr_t), leaked_bytes); ASSERT_EQ(2U, leaked.size()); EXPECT_EQ(2U, leaked[0].referenced_count); - EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size); + EXPECT_EQ(2 * sizeof(uintptr_t), leaked[0].referenced_size); EXPECT_EQ(2U, leaked[1].referenced_count); - EXPECT_EQ(2*sizeof(uintptr_t), leaked[1].referenced_size); + EXPECT_EQ(2 * sizeof(uintptr_t), leaked[1].referenced_size); } TEST_F(LeakFoldingTest, two_dominator_cycles) { @@ -254,7 +256,7 @@ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes)); EXPECT_EQ(4U, num_leaks); - EXPECT_EQ(4*sizeof(uintptr_t), leaked_bytes); + EXPECT_EQ(4 * sizeof(uintptr_t), leaked_bytes); ASSERT_EQ(4U, leaked.size()); EXPECT_EQ(1U, leaked[0].referenced_count); EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size); @@ -272,13 +274,13 @@ HeapWalker heap_walker(heap_); - for (size_t i = 0; i < n; i ++) { + for (size_t i = 0; i < n; i++) { ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]), - reinterpret_cast<uintptr_t>(&buffer[i+1]))); + reinterpret_cast<uintptr_t>(&buffer[i + 1]))); } for (size_t i = 0; i < n - 1; i++) { - buffer[i] = &buffer[i+1]; + buffer[i] = &buffer[i + 1]; } buffer[n - 1] = &buffer[0]; @@ -306,15 +308,15 @@ HeapWalker heap_walker(heap_); for (size_t i = 0; i < n - 1; i++) { - buffer[i] = &buffer[i+1]; + buffer[i] = &buffer[i + 1]; } buffer[n - 1] = &buffer[0]; buffer1[0] = &buffer[0]; - for (size_t i = 0; i < n; i ++) { + for (size_t i = 0; i < n; i++) { ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]), - reinterpret_cast<uintptr_t>(&buffer[i+1]))); + reinterpret_cast<uintptr_t>(&buffer[i + 1]))); } ALLOCATION(heap_walker, buffer1); @@ -425,3 +427,5 @@ EXPECT_EQ(3U, leaked[3].referenced_count); EXPECT_EQ(6 * sizeof(uintptr_t), leaked[3].referenced_size); } + +} // namespace android
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp index 71da365..ec89388 100644 --- a/libmemunreachable/tests/MemUnreachable_test.cpp +++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -16,36 +16,32 @@ #include <fcntl.h> #include <stdlib.h> -#include <unistd.h> #include <sys/prctl.h> +#include <unistd.h> #include <gtest/gtest.h> #include <memunreachable/memunreachable.h> +namespace android { + class HiddenPointer { public: - explicit HiddenPointer(size_t size = 256) { - Set(malloc(size)); - } - ~HiddenPointer() { - Free(); - } - void* Get() { - return reinterpret_cast<void*>(~ptr_); - } + explicit HiddenPointer(size_t size = 256) { Set(malloc(size)); } + ~HiddenPointer() { Free(); } + void* Get() { return reinterpret_cast<void*>(~ptr_); } void Free() { free(Get()); Set(nullptr); } + private: - void Set(void* ptr) { - ptr_ = ~reinterpret_cast<uintptr_t>(ptr); - } + void Set(void* ptr) { ptr_ = ~reinterpret_cast<uintptr_t>(ptr); } volatile uintptr_t ptr_; }; -static void Ref(void* ptr) { +// Trick the compiler into thinking a value on the stack is still referenced. +static void Ref(void** ptr) { write(0, ptr, 0); } @@ -63,14 +59,14 @@ { void* ptr = hidden_ptr.Get(); - Ref(ptr); + Ref(&ptr); UnreachableMemoryInfo info; ASSERT_TRUE(GetUnreachableMemory(info)); ASSERT_EQ(0U, info.leaks.size()); - Ref(ptr); + ptr = nullptr; } { @@ -216,3 +212,5 @@ ASSERT_TRUE(LogUnreachableMemory(true, 100)); } + +} // namespace android
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp index 44aabd7..4fbf729 100644 --- a/libmemunreachable/tests/ThreadCapture_test.cpp +++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -34,6 +34,8 @@ using namespace std::chrono_literals; +namespace android { + class ThreadListTest : public ::testing::TestWithParam<int> { public: ThreadListTest() : stop_(false) {} @@ -45,12 +47,10 @@ WaitForThreads(); } - virtual void TearDown() { - ASSERT_TRUE(heap.empty()); - } + virtual void TearDown() { ASSERT_TRUE(heap.empty()); } protected: - template<class Function> + template <class Function> void StartThreads(unsigned int threads, Function&& func) { threads_.reserve(threads); tids_.reserve(threads); @@ -68,14 +68,14 @@ { std::unique_lock<std::mutex> lk(m_); - cv_stop_.wait(lk, [&] {return stop_;}); + cv_stop_.wait(lk, [&] { return stop_; }); } }); } { std::unique_lock<std::mutex> lk(m_); - cv_start_.wait(lk, [&]{ return tids_.size() == threads; }); + cv_start_.wait(lk, [&] { return tids_.size() == threads; }); } } @@ -93,9 +93,7 @@ tids_.clear(); } - std::vector<pid_t>& tids() { - return tids_; - } + std::vector<pid_t>& tids() { return tids_; } Heap heap; @@ -143,7 +141,7 @@ TEST_P(ThreadListTest, list_some) { const unsigned int threads = GetParam() - 1; - StartThreads(threads, [](){}); + StartThreads(threads, []() {}); std::vector<pid_t> expected_tids = tids(); expected_tids.push_back(getpid()); @@ -176,10 +174,8 @@ public: ThreadCaptureTest() {} ~ThreadCaptureTest() {} - void Fork(std::function<void()>&& child_init, - std::function<void()>&& child_cleanup, - std::function<void(pid_t)>&& parent) { - + void Fork(std::function<void()>&& child_init, std::function<void()>&& child_cleanup, + std::function<void(pid_t)>&& parent) { ScopedPipe start_pipe; ScopedPipe stop_pipe; @@ -211,39 +207,40 @@ TEST_P(ThreadCaptureTest, capture_some) { const unsigned int threads = GetParam(); - Fork([&](){ - // child init - StartThreads(threads - 1, [](){}); - }, - [&](){ - // child cleanup - StopThreads(); - }, - [&](pid_t child){ - // parent - ASSERT_GT(child, 0); + Fork( + [&]() { + // child init + StartThreads(threads - 1, []() {}); + }, + [&]() { + // child cleanup + StopThreads(); + }, + [&](pid_t child) { + // parent + ASSERT_GT(child, 0); - { - ScopedDisableMallocTimeout disable_malloc; + { + ScopedDisableMallocTimeout disable_malloc; - ThreadCapture thread_capture(child, heap); - auto list_tids = allocator::vector<pid_t>(heap); + ThreadCapture thread_capture(child, heap); + auto list_tids = allocator::vector<pid_t>(heap); - ASSERT_TRUE(thread_capture.ListThreads(list_tids)); - ASSERT_EQ(threads, list_tids.size()); + ASSERT_TRUE(thread_capture.ListThreads(list_tids)); + ASSERT_EQ(threads, list_tids.size()); - ASSERT_TRUE(thread_capture.CaptureThreads()); + ASSERT_TRUE(thread_capture.CaptureThreads()); - auto thread_info = allocator::vector<ThreadInfo>(heap); - ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info)); - ASSERT_EQ(threads, thread_info.size()); - ASSERT_TRUE(thread_capture.ReleaseThreads()); + auto thread_info = allocator::vector<ThreadInfo>(heap); + ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info)); + ASSERT_EQ(threads, thread_info.size()); + ASSERT_TRUE(thread_capture.ReleaseThreads()); - if (!HasFailure()) { - ASSERT_FALSE(disable_malloc.timed_out()); - } -} - }); + if (!HasFailure()) { + ASSERT_FALSE(disable_malloc.timed_out()); + } + } + }); } INSTANTIATE_TEST_CASE_P(ThreadCaptureTest, ThreadCaptureTest, ::testing::Values(1, 2, 10, 1024)); @@ -262,7 +259,7 @@ ScopedDisableMallocTimeout disable_malloc; ThreadCapture thread_capture(ret, heap); - thread_capture.InjectTestFunc([&](pid_t tid){ + thread_capture.InjectTestFunc([&](pid_t tid) { syscall(SYS_tgkill, ret, tid, SIGKILL); usleep(10000); }); @@ -288,62 +285,65 @@ // For signal handler static ScopedPipe* g_pipe; - Fork([&](){ - // child init - pipe.CloseReceiver(); + Fork( + [&]() { + // child init + pipe.CloseReceiver(); - g_pipe = &pipe; + g_pipe = &pipe; - struct sigaction act{}; - act.sa_handler = [](int){ - char buf = '+'; - write(g_pipe->Sender(), &buf, 1); - g_pipe->CloseSender(); - }; - sigaction(sig, &act, NULL); - sigset_t set; - sigemptyset(&set); - sigaddset(&set, sig); - pthread_sigmask(SIG_UNBLOCK, &set, NULL); - }, - [&](){ - // child cleanup - g_pipe = nullptr; - pipe.Close(); - }, - [&](pid_t child){ - // parent - ASSERT_GT(child, 0); - pipe.CloseSender(); + struct sigaction act {}; + act.sa_handler = [](int) { + char buf = '+'; + write(g_pipe->Sender(), &buf, 1); + g_pipe->CloseSender(); + }; + sigaction(sig, &act, NULL); + sigset_t set; + sigemptyset(&set); + sigaddset(&set, sig); + pthread_sigmask(SIG_UNBLOCK, &set, NULL); + }, + [&]() { + // child cleanup + g_pipe = nullptr; + pipe.Close(); + }, + [&](pid_t child) { + // parent + ASSERT_GT(child, 0); + pipe.CloseSender(); - { - ScopedDisableMallocTimeout disable_malloc; + { + ScopedDisableMallocTimeout disable_malloc; - ThreadCapture thread_capture(child, heap); - thread_capture.InjectTestFunc([&](pid_t tid){ - syscall(SYS_tgkill, child, tid, sig); - usleep(10000); + ThreadCapture thread_capture(child, heap); + thread_capture.InjectTestFunc([&](pid_t tid) { + syscall(SYS_tgkill, child, tid, sig); + usleep(10000); + }); + auto list_tids = allocator::vector<pid_t>(heap); + + ASSERT_TRUE(thread_capture.ListThreads(list_tids)); + ASSERT_EQ(1U, list_tids.size()); + + ASSERT_TRUE(thread_capture.CaptureThreads()); + + auto thread_info = allocator::vector<ThreadInfo>(heap); + ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info)); + ASSERT_EQ(1U, thread_info.size()); + ASSERT_TRUE(thread_capture.ReleaseThreads()); + + usleep(100000); + char buf; + ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1))); + ASSERT_EQ(buf, '+'); + + if (!HasFailure()) { + ASSERT_FALSE(disable_malloc.timed_out()); + } + } }); - auto list_tids = allocator::vector<pid_t>(heap); - - ASSERT_TRUE(thread_capture.ListThreads(list_tids)); - ASSERT_EQ(1U, list_tids.size()); - - ASSERT_TRUE(thread_capture.CaptureThreads()); - - auto thread_info = allocator::vector<ThreadInfo>(heap); - ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info)); - ASSERT_EQ(1U, thread_info.size()); - ASSERT_TRUE(thread_capture.ReleaseThreads()); - - usleep(100000); - char buf; - ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1))); - ASSERT_EQ(buf, '+'); - - if (!HasFailure()) { - ASSERT_FALSE(disable_malloc.timed_out()); - } - } - }); } + +} // namespace android
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp index 38859d1..e292403 100644 --- a/libmetricslogger/Android.bp +++ b/libmetricslogger/Android.bp
@@ -20,10 +20,11 @@ "-Wextra", "-Werror", - // 524291 corresponds to sysui_histogram, from - // frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags - "-DHISTOGRAM_LOG_TAG=524292", - "-DCOUNT_LOG_TAG=524290", + // The following define maps the sysui_multi_action logtag ID as represented by: + // frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags + // + // TODO(jhawkins): Query this value at runtime. + "-DMULTI_ACTION_LOG_TAG=524292", ], } @@ -31,7 +32,6 @@ // ----------------------------------------------------------------------------- cc_library_shared { name: "libmetricslogger", - vendor_available: true, srcs: metricslogger_lib_src_files, defaults: ["metricslogger_defaults"], }
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h index 36e124d..189bc4b 100644 --- a/libmetricslogger/include/metricslogger/metrics_logger.h +++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -28,14 +28,26 @@ // log buffer. void LogCounter(const std::string& name, int32_t val); +// Logs a Tron multi_action with category|category| containing the string +// |value| in the field |field|. +void LogMultiAction(int32_t category, int32_t field, const std::string& value); + // TODO: replace these with the metric_logger.proto definitions enum { LOGBUILDER_CATEGORY = 757, + LOGBUILDER_TYPE = 758, LOGBUILDER_NAME = 799, LOGBUILDER_BUCKET = 801, LOGBUILDER_VALUE = 802, LOGBUILDER_COUNTER = 803, LOGBUILDER_HISTOGRAM = 804, + + ACTION_BOOT = 1098, + FIELD_PLATFORM_REASON = 1099, +}; + +enum { + TYPE_ACTION = 4, }; } // namespace metricslogger
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp index 6f65e10..0ca024d 100644 --- a/libmetricslogger/metrics_logger.cpp +++ b/libmetricslogger/metrics_logger.cpp
@@ -25,17 +25,24 @@ // Mirror com.android.internal.logging.MetricsLogger#histogram(). void LogHistogram(const std::string& event, int32_t data) { - android_log_event_list log(HISTOGRAM_LOG_TAG); + android_log_event_list log(MULTI_ACTION_LOG_TAG); log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event << LOGBUILDER_BUCKET << data << LOGBUILDER_VALUE << 1 << LOG_ID_EVENTS; } // Mirror com.android.internal.logging.MetricsLogger#count(). void LogCounter(const std::string& name, int32_t val) { - android_log_event_list log(COUNT_LOG_TAG); + android_log_event_list log(MULTI_ACTION_LOG_TAG); log << LOGBUILDER_CATEGORY << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE << val << LOG_ID_EVENTS; } +// Mirror com.android.internal.logging.MetricsLogger#action(). +void LogMultiAction(int32_t category, int32_t field, const std::string& value) { + android_log_event_list log(MULTI_ACTION_LOG_TAG); + log << LOGBUILDER_CATEGORY << category << LOGBUILDER_TYPE << TYPE_ACTION + << field << value << LOG_ID_EVENTS; +} + } // namespace metricslogger } // namespace android
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp index 377b7dd..1cea4cd 100644 --- a/libnativebridge/Android.bp +++ b/libnativebridge/Android.bp
@@ -1,4 +1,11 @@ +cc_library_headers { + name: "libnativebridge-dummy-headers", + + host_supported: true, + export_include_dirs=["include"], +} + cc_library { name: "libnativebridge", @@ -7,6 +14,8 @@ shared_libs: ["liblog"], clang: true, + export_include_dirs=["include"], + cflags: [ "-Werror", "-Wall", @@ -23,4 +32,4 @@ }, } -subdirs = ["tests"] \ No newline at end of file +subdirs = ["tests"]
diff --git a/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h similarity index 100% rename from include/nativebridge/native_bridge.h rename to libnativebridge/include/nativebridge/native_bridge.h
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp index efd3978..e31dae0 100644 --- a/libnativebridge/tests/Android.bp +++ b/libnativebridge/tests/Android.bp
@@ -23,6 +23,7 @@ "-Wextra", "-Werror", ], + header_libs: ["libnativebridge-dummy-headers"], cppflags: ["-fvisibility=protected"], target: { android: {
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h index 99ae3a7..3563fc1 100644 --- a/libnativeloader/include/nativeloader/native_loader.h +++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -34,6 +34,7 @@ int32_t target_sdk_version, jobject class_loader, bool is_shared, + bool is_for_vendor, jstring library_path, jstring permitted_path);
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp index 36a2e44..5d160ee 100644 --- a/libnativeloader/native_loader.cpp +++ b/libnativeloader/native_loader.cpp
@@ -15,7 +15,7 @@ */ #include "nativeloader/native_loader.h" -#include "ScopedUtfChars.h" +#include <nativehelper/ScopedUtfChars.h> #include <dlfcn.h> #ifdef __ANDROID__ @@ -82,6 +82,11 @@ "/etc/public.libraries.txt"; static constexpr const char* kPublicNativeLibrariesVendorConfig = "/vendor/etc/public.libraries.txt"; +static constexpr const char* kLlndkNativeLibrariesSystemConfigPathFromRoot = + "/etc/llndk.libraries.txt"; +static constexpr const char* kVndkspNativeLibrariesSystemConfigPathFromRoot = + "/etc/vndksp.libraries.txt"; + // The device may be configured to have the vendor libraries loaded to a separate namespace. // For historical reasons this namespace was named sphal but effectively it is intended @@ -89,6 +94,11 @@ // vendor and system namespaces. static constexpr const char* kVendorNamespaceName = "sphal"; +static constexpr const char* kVndkNamespaceName = "vndk"; + +static constexpr const char* kClassloaderNamespaceName = "classloader-namespace"; +static constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace"; + // (http://b/27588281) This is a workaround for apps using custom classloaders and calling // System.load() with an absolute path which is outside of the classloader library search path. // This list includes all directories app is allowed to access this way. @@ -108,6 +118,7 @@ uint32_t target_sdk_version, jobject class_loader, bool is_shared, + bool is_for_vendor, jstring java_library_path, jstring java_permitted_path, NativeLoaderNamespace* ns, @@ -163,9 +174,39 @@ is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str()); } + std::string system_exposed_libraries = system_public_libraries_; + const char* namespace_name = kClassloaderNamespaceName; + android_namespace_t* vndk_ns = nullptr; + if (is_for_vendor && !is_shared) { + LOG_FATAL_IF(is_native_bridge, "Unbundled vendor apk must not use translated architecture"); + + // For vendor apks, give access to the vendor lib even though + // they are treated as unbundled; the libs and apks are still bundled + // together in the vendor partition. +#if defined(__LP64__) + std::string vendor_lib_path = "/vendor/lib64"; +#else + std::string vendor_lib_path = "/vendor/lib"; +#endif + library_path = library_path + ":" + vendor_lib_path.c_str(); + permitted_path = permitted_path + ":" + vendor_lib_path.c_str(); + + // Also give access to LLNDK libraries since they are available to vendors + system_exposed_libraries = system_exposed_libraries + ":" + system_llndk_libraries_.c_str(); + + // Give access to VNDK-SP libraries from the 'vndk' namespace. + vndk_ns = android_get_exported_namespace(kVndkNamespaceName); + LOG_ALWAYS_FATAL_IF(vndk_ns == nullptr, + "Cannot find \"%s\" namespace for vendor apks", kVndkNamespaceName); + + // Different name is useful for debugging + namespace_name = kVendorClassloaderNamespaceName; + ALOGD("classloader namespace configured for unbundled vendor apk. library_path=%s", library_path.c_str()); + } + NativeLoaderNamespace native_loader_ns; if (!is_native_bridge) { - android_namespace_t* ns = android_create_namespace("classloader-namespace", + android_namespace_t* ns = android_create_namespace(namespace_name, nullptr, library_path.c_str(), namespace_type, @@ -181,11 +222,19 @@ // which is expected behavior in this case. android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName); - if (!android_link_namespaces(ns, nullptr, system_public_libraries_.c_str())) { + if (!android_link_namespaces(ns, nullptr, system_exposed_libraries.c_str())) { *error_msg = dlerror(); return false; } + if (vndk_ns != nullptr && !system_vndksp_libraries_.empty()) { + // vendor apks are allowed to use VNDK-SP libraries. + if (!android_link_namespaces(ns, vndk_ns, system_vndksp_libraries_.c_str())) { + *error_msg = dlerror(); + return false; + } + } + if (!vendor_public_libraries_.empty()) { if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) { *error_msg = dlerror(); @@ -195,7 +244,7 @@ native_loader_ns = NativeLoaderNamespace(ns); } else { - native_bridge_namespace_t* ns = NativeBridgeCreateNamespace("classloader-namespace", + native_bridge_namespace_t* ns = NativeBridgeCreateNamespace(namespace_name, nullptr, library_path.c_str(), namespace_type, @@ -209,7 +258,7 @@ native_bridge_namespace_t* vendor_ns = NativeBridgeGetVendorNamespace(); - if (!NativeBridgeLinkNamespaces(ns, nullptr, system_public_libraries_.c_str())) { + if (!NativeBridgeLinkNamespaces(ns, nullptr, system_exposed_libraries.c_str())) { *error_msg = NativeBridgeGetError(); return false; } @@ -259,6 +308,10 @@ std::string root_dir = android_root_env != nullptr ? android_root_env : "/system"; std::string public_native_libraries_system_config = root_dir + kPublicNativeLibrariesSystemConfigPathFromRoot; + std::string llndk_native_libraries_system_config = + root_dir + kLlndkNativeLibrariesSystemConfigPathFromRoot; + std::string vndksp_native_libraries_system_config = + root_dir + kVndkspNativeLibrariesSystemConfigPathFromRoot; std::string error_msg; LOG_ALWAYS_FATAL_IF(!ReadConfig(public_native_libraries_system_config, &sonames, &error_msg), @@ -294,6 +347,14 @@ system_public_libraries_ = base::Join(sonames, ':'); sonames.clear(); + ReadConfig(kLlndkNativeLibrariesSystemConfigPathFromRoot, &sonames); + system_llndk_libraries_ = base::Join(sonames, ':'); + + sonames.clear(); + ReadConfig(kVndkspNativeLibrariesSystemConfigPathFromRoot, &sonames); + system_vndksp_libraries_ = base::Join(sonames, ':'); + + sonames.clear(); // This file is optional, quietly ignore if the file does not exist. ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames); @@ -404,6 +465,8 @@ std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_; std::string system_public_libraries_; std::string vendor_public_libraries_; + std::string system_llndk_libraries_; + std::string system_vndksp_libraries_; DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces); }; @@ -430,6 +493,7 @@ int32_t target_sdk_version, jobject class_loader, bool is_shared, + bool is_for_vendor, jstring library_path, jstring permitted_path) { #if defined(__ANDROID__) @@ -441,6 +505,7 @@ target_sdk_version, class_loader, is_shared, + is_for_vendor, library_path, permitted_path, &ns, @@ -449,7 +514,7 @@ return env->NewStringUTF(error_msg.c_str()); } #else - UNUSED(env, target_sdk_version, class_loader, is_shared, + UNUSED(env, target_sdk_version, class_loader, is_shared, is_for_vendor, library_path, permitted_path); #endif return nullptr; @@ -478,7 +543,8 @@ if (!g_namespaces->Create(env, target_sdk_version, class_loader, - false, + false /* is_shared */, + false /* is_for_vendor */, library_path, nullptr, &ns,
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp index 9967ef8..1d43775 100644 --- a/libnetutils/Android.bp +++ b/libnetutils/Android.bp
@@ -1,6 +1,9 @@ cc_library_shared { name: "libnetutils", vendor_available: true, + vndk: { + enabled: true, + }, srcs: [ "dhcpclient.c",
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c index f28d726..1856e5c 100644 --- a/libpixelflinger/codeflinger/mips64_disassem.c +++ b/libpixelflinger/codeflinger/mips64_disassem.c
@@ -555,6 +555,7 @@ } else { vprintf(fmt, argp); } + va_end(argp); } /*
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c index 3007b15..83a9740 100644 --- a/libpixelflinger/codeflinger/mips_disassem.c +++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -562,6 +562,7 @@ } else { vprintf(fmt, argp); } + va_end(argp); }
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h index f0c3795..9fa4154 100644 --- a/libprocessgroup/include/processgroup/processgroup.h +++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -33,6 +33,10 @@ int createProcessGroup(uid_t uid, int initialPid); +bool setProcessGroupSwappiness(uid_t uid, int initialPid, int swappiness); +bool setProcessGroupSoftLimit(uid_t uid, int initialPid, int64_t softLimitInBytes); +bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes); + void removeAllProcessGroups(void); __END_DECLS
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp index f5d4e1c..8526b3a 100644 --- a/libprocessgroup/processgroup.cpp +++ b/libprocessgroup/processgroup.cpp
@@ -35,16 +35,16 @@ #include <set> #include <thread> +#include <android-base/file.h> #include <android-base/logging.h> #include <android-base/unique_fd.h> #include <private/android_filesystem_config.h> #include <processgroup/processgroup.h> -using namespace std::chrono_literals; +using android::base::WriteStringToFile; -// Uncomment line below use memory cgroups for keeping track of (forked) PIDs -// #define USE_MEMCG 1 +using namespace std::chrono_literals; #define MEM_CGROUP_PATH "/dev/memcg/apps" #define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks" @@ -91,7 +91,6 @@ }; static const char* getCgroupRootPath() { -#ifdef USE_MEMCG static const char* cgroup_root_path = NULL; std::call_once(init_path_flag, [&]() { // Check if mem cgroup is mounted, only then check for write-access to avoid @@ -100,9 +99,6 @@ ACCT_CGROUP_PATH : MEM_CGROUP_PATH; }); return cgroup_root_path; -#else - return ACCT_CGROUP_PATH; -#endif } static int convertUidToPath(char *path, size_t size, uid_t uid) @@ -409,22 +405,40 @@ strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path)); - int fd = open(path, O_WRONLY); - if (fd == -1) { - int ret = -errno; - PLOG(ERROR) << "Failed to open " << path; - return ret; - } - - char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0}; - int len = snprintf(pid, sizeof(pid), "%d", initialPid); - int ret = 0; - if (write(fd, pid, len) < 0) { + if (!WriteStringToFile(std::to_string(initialPid), path)) { ret = -errno; - PLOG(ERROR) << "Failed to write '" << pid << "' to " << path; + PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << path; } - close(fd); return ret; } + +static bool setProcessGroupValue(uid_t uid, int pid, const char* fileName, int64_t value) { + char path[PROCESSGROUP_MAX_PATH_LEN] = {0}; + if (strcmp(getCgroupRootPath(), MEM_CGROUP_PATH)) { + PLOG(ERROR) << "Memcg is not mounted." << path; + return false; + } + + convertUidPidToPath(path, sizeof(path), uid, pid); + strlcat(path, fileName, sizeof(path)); + + if (!WriteStringToFile(std::to_string(value), path)) { + PLOG(ERROR) << "Failed to write '" << value << "' to " << path; + return false; + } + return true; +} + +bool setProcessGroupSwappiness(uid_t uid, int pid, int swappiness) { + return setProcessGroupValue(uid, pid, "/memory.swappiness", swappiness); +} + +bool setProcessGroupSoftLimit(uid_t uid, int pid, int64_t soft_limit_in_bytes) { + return setProcessGroupValue(uid, pid, "/memory.soft_limit_in_bytes", soft_limit_in_bytes); +} + +bool setProcessGroupLimit(uid_t uid, int pid, int64_t limit_in_bytes) { + return setProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes); +}
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp index aedaa38..b568ee5 100644 --- a/libprocinfo/Android.bp +++ b/libprocinfo/Android.bp
@@ -23,6 +23,9 @@ cc_library { name: "libprocinfo", vendor_available: true, + vndk: { + enabled: true, + }, host_supported: true, srcs: [ "process.cpp",
diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h index fb140ff..db56fc1 100644 --- a/libprocinfo/include/procinfo/process.h +++ b/libprocinfo/include/procinfo/process.h
@@ -35,8 +35,18 @@ #if defined(__linux__) +enum ProcessState { + kProcessStateUnknown, + kProcessStateRunning, + kProcessStateSleeping, + kProcessStateUninterruptibleWait, + kProcessStateStopped, + kProcessStateZombie, +}; + struct ProcessInfo { std::string name; + ProcessState state; pid_t tid; pid_t pid; pid_t ppid;
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp index c513e16..6e5be6e 100644 --- a/libprocinfo/process.cpp +++ b/libprocinfo/process.cpp
@@ -44,6 +44,24 @@ return GetProcessInfoFromProcPidFd(dirfd.get(), process_info); } +static ProcessState parse_state(const char* state) { + switch (*state) { + case 'R': + return kProcessStateRunning; + case 'S': + return kProcessStateSleeping; + case 'D': + return kProcessStateUninterruptibleWait; + case 'T': + return kProcessStateStopped; + case 'Z': + return kProcessStateZombie; + default: + LOG(ERROR) << "unknown process state: " << *state; + return kProcessStateUnknown; + } +} + bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) { int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC); @@ -60,7 +78,7 @@ } int field_bitmap = 0; - static constexpr int finished_bitmap = 127; + static constexpr int finished_bitmap = 255; char* line = nullptr; size_t len = 0; @@ -98,6 +116,9 @@ } else if (header == "Gid:") { process_info->gid = atoi(tab + 1); field_bitmap |= 64; + } else if (header == "State:") { + process_info->state = parse_state(tab + 1); + field_bitmap |= 128; } }
diff --git a/libprocinfo/process_test.cpp b/libprocinfo/process_test.cpp index 5ffd236..9da9278 100644 --- a/libprocinfo/process_test.cpp +++ b/libprocinfo/process_test.cpp
@@ -21,6 +21,7 @@ #include <sys/types.h> #include <unistd.h> +#include <chrono> #include <set> #include <thread> #include <vector> @@ -29,6 +30,8 @@ #include <android-base/stringprintf.h> +using namespace std::chrono_literals; + #if !defined(__BIONIC__) #include <syscall.h> static pid_t gettid() { @@ -82,3 +85,34 @@ } }).join(); } + +TEST(process_info, process_state) { + int pipefd[2]; + ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC)); + pid_t forkpid = fork(); + + ASSERT_NE(-1, forkpid); + if (forkpid == 0) { + close(pipefd[1]); + char buf; + TEMP_FAILURE_RETRY(read(pipefd[0], &buf, 1)); + _exit(0); + } + + // Give the child some time to get to the read. + std::this_thread::sleep_for(100ms); + + android::procinfo::ProcessInfo procinfo; + ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo)); + ASSERT_EQ(android::procinfo::kProcessStateSleeping, procinfo.state); + + ASSERT_EQ(0, kill(forkpid, SIGKILL)); + + // Give the kernel some time to kill the child. + std::this_thread::sleep_for(100ms); + + ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo)); + ASSERT_EQ(android::procinfo::kProcessStateZombie, procinfo.state); + + ASSERT_EQ(forkpid, waitpid(forkpid, nullptr, 0)); +}
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp index 130800e..32f1e1f 100644 --- a/libsuspend/Android.bp +++ b/libsuspend/Android.bp
@@ -3,6 +3,9 @@ cc_library { name: "libsuspend", vendor_available: true, + vndk: { + enabled: true, + }, srcs: [ "autosuspend.c",
diff --git a/libsync/Android.bp b/libsync/Android.bp index 257d42d..ce9e84a 100644 --- a/libsync/Android.bp +++ b/libsync/Android.bp
@@ -22,10 +22,15 @@ cc_library_shared { name: "libsync", - vendor_available: true, defaults: ["libsync_defaults"], } +llndk_library { + name: "libsync", + symbol_file: "libsync.map.txt", + export_include_dirs: ["include"], +} + // libsync_recovery is only intended for the recovery binary. // Future versions of the kernel WILL require an updated libsync, and will break // anything statically linked against the current libsync.
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt index daa28ae..53bb07a 100644 --- a/libsync/libsync.map.txt +++ b/libsync/libsync.map.txt
@@ -17,16 +17,12 @@ LIBSYNC { global: sync_merge; # introduced=26 - sync_get_fence_info; # introduced=26 - sync_free_fence_info; # introduced=26 + sync_file_info; # introduced=26 + sync_file_info_free; # introduced=26 + sync_wait; # vndk + sync_fence_info; # vndk + sync_pt_info; # vndk + sync_fence_info_free; # vndk local: *; }; - -LIBSYNC_PLATFORM { - global: - sync_wait; - sync_fence_info; - sync_pt_info; - sync_fence_info_free; -} LIBSYNC_PLATFORM;
diff --git a/libsync/sync.c b/libsync/sync.c index baeccda..0950082 100644 --- a/libsync/sync.c +++ b/libsync/sync.c
@@ -217,6 +217,8 @@ local_info.num_fences * sizeof(struct sync_fence_info)); if (!info) return NULL; + + info->num_fences = local_info.num_fences; info->sync_fence_info = (__u64)(uintptr_t)(info + 1); err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
diff --git a/libsystem/include/system/thread_defs.h b/libsystem/include/system/thread_defs.h index 377a48c..80d1160 100644 --- a/libsystem/include/system/thread_defs.h +++ b/libsystem/include/system/thread_defs.h
@@ -55,6 +55,9 @@ /* ui service treads might want to run at a urgent display (uncommon) */ ANDROID_PRIORITY_URGENT_DISPLAY = HAL_PRIORITY_URGENT_DISPLAY, + /* all normal video threads */ + ANDROID_PRIORITY_VIDEO = -10, + /* all normal audio threads */ ANDROID_PRIORITY_AUDIO = -16,
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp index 550ef42..d076a1a 100644 --- a/libsysutils/Android.bp +++ b/libsysutils/Android.bp
@@ -1,6 +1,9 @@ cc_library_shared { name: "libsysutils", vendor_available: true, + vndk: { + enabled: true, + }, srcs: [ "src/SocketListener.cpp",
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp index 1b6076f..87e2684 100644 --- a/libsysutils/src/FrameworkListener.cpp +++ b/libsysutils/src/FrameworkListener.cpp
@@ -68,7 +68,7 @@ android_errorWriteLog(0x534e4554, "29831647"); c->sendMsg(500, "Command too large for buffer", false); mSkipToNextNullByte = true; - return false; + return true; } int offset = 0; @@ -211,7 +211,6 @@ return; overflow: - LOG_EVENT_INT(78001, cli->getUid()); cli->sendMsg(500, "Command too long", false); goto out; }
diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp index 896dad3..aad0394 100644 --- a/libsysutils/src/NetlinkListener.cpp +++ b/libsysutils/src/NetlinkListener.cpp
@@ -57,8 +57,6 @@ count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket, mBuffer, sizeof(mBuffer), require_group, &uid)); if (count < 0) { - if (uid > 0) - LOG_EVENT_INT(65537, uid); SLOGE("recvmsg failed (%s)", strerror(errno)); return false; }
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index eb2b902..04c4cfa 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp
@@ -31,23 +31,26 @@ }, }, - multilib: { - lib32: { - suffix: "32", + arch: { + mips: { + enabled: false, }, - lib64: { - suffix: "64", + mips64: { + enabled: false, }, }, } -cc_defaults { - name: "libunwindstack_common", +cc_library { + name: "libunwindstack", defaults: ["libunwindstack_flags"], + export_include_dirs: ["include"], srcs: [ "ArmExidx.cpp", "DwarfCfa.cpp", + "DwarfDebugFrame.cpp", + "DwarfEhFrame.cpp", "DwarfMemory.cpp", "DwarfOp.cpp", "DwarfSection.cpp", @@ -55,40 +58,34 @@ "ElfInterface.cpp", "ElfInterfaceArm.cpp", "Log.cpp", - "Regs.cpp", "MapInfo.cpp", "Maps.cpp", "Memory.cpp", + "Regs.cpp", "Symbols.cpp", ], + arch: { + x86: { + srcs: ["AsmGetRegsX86.S"], + }, + x86_64: { + srcs: ["AsmGetRegsX86_64.S"], + }, + }, + shared_libs: [ "libbase", "liblog", - ], -} - -cc_library { - name: "libunwindstack", - defaults: ["libunwindstack_common"], -} - -cc_library { - name: "libunwindstack_debug", - defaults: ["libunwindstack_common"], - - cflags: [ - "-UNDEBUG", - "-O0", - "-g", + "liblzma", ], } //------------------------------------------------------------------------- // Unit Tests //------------------------------------------------------------------------- -cc_defaults { - name: "libunwindstack_test_common", +cc_test { + name: "libunwindstack_test", defaults: ["libunwindstack_flags"], srcs: [ @@ -96,6 +93,8 @@ "tests/ArmExidxExtractTest.cpp", "tests/DwarfCfaLogTest.cpp", "tests/DwarfCfaTest.cpp", + "tests/DwarfDebugFrameTest.cpp", + "tests/DwarfEhFrameTest.cpp", "tests/DwarfMemoryTest.cpp", "tests/DwarfOpLogTest.cpp", "tests/DwarfOpTest.cpp", @@ -104,16 +103,22 @@ "tests/ElfInterfaceArmTest.cpp", "tests/ElfInterfaceTest.cpp", "tests/ElfTest.cpp", + "tests/ElfTestUtils.cpp", "tests/LogFake.cpp", - "tests/MapInfoTest.cpp", + "tests/MapInfoCreateMemoryTest.cpp", + "tests/MapInfoGetElfTest.cpp", "tests/MapsTest.cpp", + "tests/MemoryBufferTest.cpp", "tests/MemoryFake.cpp", "tests/MemoryFileTest.cpp", "tests/MemoryLocalTest.cpp", "tests/MemoryRangeTest.cpp", "tests/MemoryRemoteTest.cpp", + "tests/MemoryTest.cpp", + "tests/RegsStepIfSignalHandlerTest.cpp", "tests/RegsTest.cpp", "tests/SymbolsTest.cpp", + "tests/UnwindTest.cpp", ], cflags: [ @@ -124,6 +129,8 @@ shared_libs: [ "libbase", "liblog", + "liblzma", + "libunwindstack", ], static_libs: [ @@ -137,52 +144,69 @@ ], }, }, -} -// These unit tests run against the shared library. -cc_test { - name: "libunwindstack_test", - defaults: ["libunwindstack_test_common"], - - shared_libs: [ - "libunwindstack", - ], -} - -// These unit tests run against the static debug library. -cc_test { - name: "libunwindstack_test_debug", - defaults: ["libunwindstack_test_common"], - - static_libs: [ - "libunwindstack_debug", + data: [ + "tests/files/elf32.xz", + "tests/files/elf64.xz", ], } //------------------------------------------------------------------------- -// Utility Executables +// Tools //------------------------------------------------------------------------- cc_defaults { - name: "libunwindstack_executables", + name: "libunwindstack_tools", defaults: ["libunwindstack_flags"], shared_libs: [ "libunwindstack", "libbase", + "liblzma", + ], +} + +cc_binary { + name: "unwind", + defaults: ["libunwindstack_tools"], + + srcs: [ + "tools/unwind.cpp", ], - static_libs: [ - "liblog", - ], - - compile_multilib: "both", + target: { + linux: { + host_ldlibs: [ + "-lrt", + ], + }, + }, } cc_binary { name: "unwind_info", - defaults: ["libunwindstack_executables"], + defaults: ["libunwindstack_tools"], srcs: [ - "unwind_info.cpp", + "tools/unwind_info.cpp", + ], +} + +cc_binary { + name: "unwind_symbols", + defaults: ["libunwindstack_tools"], + + srcs: [ + "tools/unwind_symbols.cpp", + ], +} + +// Generates the elf data for use in the tests for .gnu_debugdata frames. +// Once these files are generated, use the xz command to compress the data. +cc_binary_host { + name: "gen_gnudebugdata", + defaults: ["libunwindstack_flags"], + + srcs: [ + "tests/GenGnuDebugdata.cpp", ], }
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp index 12adf57..fed3e0e 100644 --- a/libunwindstack/ArmExidx.cpp +++ b/libunwindstack/ArmExidx.cpp
@@ -14,7 +14,6 @@ * limitations under the License. */ -#include <assert.h> #include <stdint.h> #include <deque> @@ -22,11 +21,15 @@ #include <android-base/stringprintf.h> +#include <unwindstack/Log.h> +#include <unwindstack/Memory.h> +#include <unwindstack/Regs.h> + #include "ArmExidx.h" -#include "Log.h" +#include "Check.h" #include "Machine.h" -#include "Memory.h" -#include "Regs.h" + +namespace unwindstack { void ArmExidx::LogRawData() { std::string log_str("Raw Data:"); @@ -173,7 +176,7 @@ } inline bool ArmExidx::DecodePrefix_10_00(uint8_t byte) { - assert((byte >> 4) == 0x8); + CHECK((byte >> 4) == 0x8); uint16_t registers = (byte & 0xf) << 8; if (!GetByte(&byte)) { @@ -232,7 +235,7 @@ } inline bool ArmExidx::DecodePrefix_10_01(uint8_t byte) { - assert((byte >> 4) == 0x9); + CHECK((byte >> 4) == 0x9); uint8_t bits = byte & 0xf; if (bits == 13 || bits == 15) { @@ -258,7 +261,7 @@ } inline bool ArmExidx::DecodePrefix_10_10(uint8_t byte) { - assert((byte >> 4) == 0xa); + CHECK((byte >> 4) == 0xa); // 10100nnn: Pop r4-r[4+nnn] // 10101nnn: Pop r4-r[4+nnn], r14 @@ -419,7 +422,7 @@ } inline bool ArmExidx::DecodePrefix_10_11_1nnn(uint8_t byte) { - assert((byte & ~0x07) == 0xb8); + CHECK((byte & ~0x07) == 0xb8); // 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX if (log_) { @@ -439,7 +442,7 @@ } inline bool ArmExidx::DecodePrefix_10(uint8_t byte) { - assert((byte >> 6) == 0x2); + CHECK((byte >> 6) == 0x2); switch ((byte >> 4) & 0x3) { case 0: @@ -469,7 +472,7 @@ } inline bool ArmExidx::DecodePrefix_11_000(uint8_t byte) { - assert((byte & ~0x07) == 0xc0); + CHECK((byte & ~0x07) == 0xc0); uint8_t bits = byte & 0x7; if (bits == 6) { @@ -550,7 +553,7 @@ } inline bool ArmExidx::DecodePrefix_11_001(uint8_t byte) { - assert((byte & ~0x07) == 0xc8); + CHECK((byte & ~0x07) == 0xc8); uint8_t bits = byte & 0x7; if (bits == 0) { @@ -605,7 +608,7 @@ } inline bool ArmExidx::DecodePrefix_11_010(uint8_t byte) { - assert((byte & ~0x07) == 0xd0); + CHECK((byte & ~0x07) == 0xd0); // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH if (log_) { @@ -624,7 +627,7 @@ } inline bool ArmExidx::DecodePrefix_11(uint8_t byte) { - assert((byte >> 6) == 0x3); + CHECK((byte >> 6) == 0x3); switch ((byte >> 3) & 0x7) { case 0: @@ -684,3 +687,5 @@ while (Decode()); return status_ == ARM_STATUS_FINISH; } + +} // namespace unwindstack
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h index 8c7f15a..f4361d4 100644 --- a/libunwindstack/ArmExidx.h +++ b/libunwindstack/ArmExidx.h
@@ -21,6 +21,8 @@ #include <deque> +namespace unwindstack { + // Forward declarations. class Memory; class RegsArm; @@ -105,4 +107,6 @@ bool pc_set_ = false; }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_ARM_EXIDX_H
diff --git a/libunwindstack/AsmGetRegsX86.S b/libunwindstack/AsmGetRegsX86.S new file mode 100644 index 0000000..14927a3 --- /dev/null +++ b/libunwindstack/AsmGetRegsX86.S
@@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + .text + .global AsmGetRegs + .balign 16 + .type AsmGetRegs, @function +AsmGetRegs: + .cfi_startproc + mov 4(%esp), %eax + movl $0, (%eax) + movl %ecx, 4(%eax) + movl %edx, 8(%eax) + movl %ebx, 12(%eax) + + /* ESP */ + leal 4(%esp), %ecx + movl %ecx, 16(%eax) + + movl %ebp, 20(%eax) + movl %esi, 24(%eax) + movl %edi, 28(%eax) + + /* EIP */ + movl (%esp), %ecx + movl %ecx, 32(%eax) + + movl %cs, 36(%eax) + movl %ss, 40(%eax) + movl %ds, 44(%eax) + movl %es, 48(%eax) + movl %fs, 52(%eax) + movl %gs, 56(%eax) + ret + + .cfi_endproc + .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsX86_64.S b/libunwindstack/AsmGetRegsX86_64.S new file mode 100644 index 0000000..4cd3b6f --- /dev/null +++ b/libunwindstack/AsmGetRegsX86_64.S
@@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + .text + .global AsmGetRegs + .balign 16 + .type AsmGetRegs, @function +AsmGetRegs: + .cfi_startproc + movq %rax, (%rdi) + movq %rdx, 8(%rdi) + movq %rcx, 16(%rdi) + movq %rbx, 24(%rdi) + movq %rsi, 32(%rdi) + movq %rdi, 40(%rdi) + movq %rbp, 48(%rdi) + + /* RSP */ + lea 8(%rsp), %rax + movq %rax, 56(%rdi) + + movq %r8, 64(%rdi) + movq %r9, 72(%rdi) + movq %r10, 80(%rdi) + movq %r11, 88(%rdi) + movq %r12, 96(%rdi) + movq %r13, 104(%rdi) + movq %r14, 112(%rdi) + movq %r15, 120(%rdi) + + /* RIP */ + movq (%rsp), %rax + movq %rax, 128(%rdi) + ret + + .cfi_endproc + .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/Check.h b/libunwindstack/Check.h new file mode 100644 index 0000000..2d216d7 --- /dev/null +++ b/libunwindstack/Check.h
@@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_ERROR_H +#define _LIBUNWINDSTACK_ERROR_H + +#include <stdlib.h> + +#include <unwindstack/Log.h> + +namespace unwindstack { + +#define CHECK(assertion) \ + if (__builtin_expect(!(assertion), false)) { \ + log(0, "%s:%d: %s\n", __FILE__, __LINE__, #assertion); \ + abort(); \ + } + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_ERROR_H
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp index 006f039..b1d55ba 100644 --- a/libunwindstack/DwarfCfa.cpp +++ b/libunwindstack/DwarfCfa.cpp
@@ -23,12 +23,15 @@ #include <android-base/stringprintf.h> +#include <unwindstack/DwarfLocation.h> +#include <unwindstack/Log.h> + #include "DwarfCfa.h" #include "DwarfEncoding.h" -#include "DwarfMemory.h" +#include "DwarfError.h" #include "DwarfOp.h" -#include "DwarfStructs.h" -#include "Log.h" + +namespace unwindstack { template <typename AddressType> constexpr typename DwarfCfa<AddressType>::process_func DwarfCfa<AddressType>::kCallbackTable[64]; @@ -711,3 +714,5 @@ // Explicitly instantiate DwarfCfa. template class DwarfCfa<uint32_t>; template class DwarfCfa<uint64_t>; + +} // namespace unwindstack
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h index 42ebae1..62b9b7a 100644 --- a/libunwindstack/DwarfCfa.h +++ b/libunwindstack/DwarfCfa.h
@@ -24,10 +24,13 @@ #include <type_traits> #include <vector> +#include <unwindstack/DwarfLocation.h> +#include <unwindstack/DwarfMemory.h> +#include <unwindstack/DwarfStructs.h> + #include "DwarfError.h" -#include "DwarfLocation.h" -#include "DwarfMemory.h" -#include "DwarfStructs.h" + +namespace unwindstack { // DWARF Standard home: http://dwarfstd.org/ // This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf @@ -252,4 +255,6 @@ }; }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_DWARF_CFA_H
diff --git a/libunwindstack/DwarfDebugFrame.cpp b/libunwindstack/DwarfDebugFrame.cpp new file mode 100644 index 0000000..5707596 --- /dev/null +++ b/libunwindstack/DwarfDebugFrame.cpp
@@ -0,0 +1,316 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <stdlib.h> + +#include <algorithm> + +#include <unwindstack/DwarfStructs.h> +#include <unwindstack/Memory.h> + +#include "DwarfDebugFrame.h" +#include "DwarfEncoding.h" +#include "DwarfError.h" + +namespace unwindstack { + +template <typename AddressType> +bool DwarfDebugFrame<AddressType>::Init(uint64_t offset, uint64_t size) { + offset_ = offset; + end_offset_ = offset + size; + + memory_.clear_func_offset(); + memory_.clear_text_offset(); + memory_.set_data_offset(offset); + memory_.set_cur_offset(offset); + + return CreateSortedFdeList(); +} + +template <typename AddressType> +bool DwarfDebugFrame<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) { + uint8_t version; + if (!memory_.ReadBytes(&version, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + // Read the augmentation string. + std::vector<char> aug_string; + char aug_value; + bool get_encoding = false; + do { + if (!memory_.ReadBytes(&aug_value, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + if (aug_value == 'R') { + get_encoding = true; + } + aug_string.push_back(aug_value); + } while (aug_value != '\0'); + + if (version == 4) { + // Skip the Address Size field. + memory_.set_cur_offset(memory_.cur_offset() + 1); + + // Read the segment size. + if (!memory_.ReadBytes(segment_size, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + } else { + *segment_size = 0; + } + + if (aug_string[0] != 'z' || !get_encoding) { + // No encoding + return true; + } + + // Skip code alignment factor + uint8_t value; + do { + if (!memory_.ReadBytes(&value, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + } while (value & 0x80); + + // Skip data alignment factor + do { + if (!memory_.ReadBytes(&value, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + } while (value & 0x80); + + if (version == 1) { + // Skip return address register. + memory_.set_cur_offset(memory_.cur_offset() + 1); + } else { + // Skip return address register. + do { + if (!memory_.ReadBytes(&value, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + } while (value & 0x80); + } + + // Skip the augmentation length. + do { + if (!memory_.ReadBytes(&value, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + } while (value & 0x80); + + for (size_t i = 1; i < aug_string.size(); i++) { + if (aug_string[i] == 'R') { + if (!memory_.ReadBytes(encoding, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + // Got the encoding, that's all we are looking for. + return true; + } else if (aug_string[i] == 'L') { + memory_.set_cur_offset(memory_.cur_offset() + 1); + } else if (aug_string[i] == 'P') { + uint8_t encoding; + if (!memory_.ReadBytes(&encoding, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + uint64_t value; + if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + } + } + + // It should be impossible to get here. + abort(); +} + +template <typename AddressType> +bool DwarfDebugFrame<AddressType>::AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, + uint8_t encoding) { + if (segment_size != 0) { + memory_.set_cur_offset(memory_.cur_offset() + 1); + } + + uint64_t start; + if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + uint64_t length; + if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + if (length != 0) { + fdes_.emplace_back(entry_offset, start, length); + } + + return true; +} + +template <typename AddressType> +bool DwarfDebugFrame<AddressType>::CreateSortedFdeList() { + memory_.set_cur_offset(offset_); + + // Loop through all of the entries and read just enough to create + // a sorted list of pcs. + // This code assumes that first comes the cie, then the fdes that + // it applies to. + uint64_t cie_offset = 0; + uint8_t address_encoding; + uint8_t segment_size; + while (memory_.cur_offset() < end_offset_) { + uint64_t cur_entry_offset = memory_.cur_offset(); + + // Figure out the entry length and type. + uint32_t value32; + if (!memory_.ReadBytes(&value32, sizeof(value32))) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + uint64_t next_entry_offset; + if (value32 == static_cast<uint32_t>(-1)) { + uint64_t value64; + if (!memory_.ReadBytes(&value64, sizeof(value64))) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + next_entry_offset = memory_.cur_offset() + value64; + + // Read the Cie Id of a Cie or the pointer of the Fde. + if (!memory_.ReadBytes(&value64, sizeof(value64))) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + if (value64 == static_cast<uint64_t>(-1)) { + // Cie 64 bit + address_encoding = DW_EH_PE_sdata8; + if (!GetCieInfo(&segment_size, &address_encoding)) { + return false; + } + cie_offset = cur_entry_offset; + } else { + if (offset_ + value64 != cie_offset) { + // This means that this Fde is not following the Cie. + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + + // Fde 64 bit + if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) { + return false; + } + } + } else { + next_entry_offset = memory_.cur_offset() + value32; + + // Read the Cie Id of a Cie or the pointer of the Fde. + if (!memory_.ReadBytes(&value32, sizeof(value32))) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + if (value32 == static_cast<uint32_t>(-1)) { + // Cie 32 bit + address_encoding = DW_EH_PE_sdata4; + if (!GetCieInfo(&segment_size, &address_encoding)) { + return false; + } + cie_offset = cur_entry_offset; + } else { + if (offset_ + value32 != cie_offset) { + // This means that this Fde is not following the Cie. + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + + // Fde 32 bit + if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) { + return false; + } + } + } + + if (next_entry_offset < memory_.cur_offset()) { + // This indicates some kind of corruption, or malformed section data. + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + memory_.set_cur_offset(next_entry_offset); + } + + // Sort the entries. + std::sort(fdes_.begin(), fdes_.end(), [](const FdeInfo& a, const FdeInfo& b) { + if (a.start == b.start) return a.end < b.end; + return a.start < b.start; + }); + + fde_count_ = fdes_.size(); + + return true; +} + +template <typename AddressType> +bool DwarfDebugFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) { + if (fde_count_ == 0) { + return false; + } + + size_t first = 0; + size_t last = fde_count_; + while (first < last) { + size_t current = (first + last) / 2; + const FdeInfo* info = &fdes_[current]; + if (pc >= info->start && pc <= info->end) { + *fde_offset = info->offset; + return true; + } + + if (pc < info->start) { + last = current; + } else { + first = current + 1; + } + } + return false; +} + +template <typename AddressType> +const DwarfFde* DwarfDebugFrame<AddressType>::GetFdeFromIndex(size_t index) { + if (index >= fdes_.size()) { + return nullptr; + } + return this->GetFdeFromOffset(fdes_[index].offset); +} + +// Explicitly instantiate DwarfDebugFrame. +template class DwarfDebugFrame<uint32_t>; +template class DwarfDebugFrame<uint64_t>; + +} // namespace unwindstack
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h new file mode 100644 index 0000000..6a6178e --- /dev/null +++ b/libunwindstack/DwarfDebugFrame.h
@@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H +#define _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H + +#include <stdint.h> + +#include <vector> + +#include <unwindstack/DwarfSection.h> + +namespace unwindstack { + +template <typename AddressType> +class DwarfDebugFrame : public DwarfSectionImpl<AddressType> { + public: + // Add these so that the protected members of DwarfSectionImpl + // can be accessed without needing a this->. + using DwarfSectionImpl<AddressType>::memory_; + using DwarfSectionImpl<AddressType>::fde_count_; + using DwarfSectionImpl<AddressType>::last_error_; + + struct FdeInfo { + FdeInfo(uint64_t offset, uint64_t start, uint64_t length) + : offset(offset), start(start), end(start + length) {} + + uint64_t offset; + AddressType start; + AddressType end; + }; + + DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {} + virtual ~DwarfDebugFrame() = default; + + bool Init(uint64_t offset, uint64_t size) override; + + bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override; + + const DwarfFde* GetFdeFromIndex(size_t index) override; + + bool IsCie32(uint32_t value32) override { return value32 == static_cast<uint32_t>(-1); } + + bool IsCie64(uint64_t value64) override { return value64 == static_cast<uint64_t>(-1); } + + uint64_t GetCieOffsetFromFde32(uint32_t pointer) override { return offset_ + pointer; } + + uint64_t GetCieOffsetFromFde64(uint64_t pointer) override { return offset_ + pointer; } + + uint64_t AdjustPcFromFde(uint64_t pc) override { return pc; } + + bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding); + + bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding); + + bool CreateSortedFdeList(); + + protected: + uint64_t offset_; + uint64_t end_offset_; + + std::vector<FdeInfo> fdes_; +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrame.cpp new file mode 100644 index 0000000..d0b35c3 --- /dev/null +++ b/libunwindstack/DwarfEhFrame.cpp
@@ -0,0 +1,217 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> + +#include <unwindstack/DwarfStructs.h> +#include <unwindstack/Memory.h> + +#include "Check.h" +#include "DwarfEhFrame.h" +#include "DwarfError.h" + +namespace unwindstack { + +template <typename AddressType> +bool DwarfEhFrame<AddressType>::Init(uint64_t offset, uint64_t size) { + uint8_t data[4]; + + memory_.clear_func_offset(); + memory_.clear_text_offset(); + memory_.set_data_offset(offset); + memory_.set_cur_offset(offset); + + // Read the first four bytes all at once. + if (!memory_.ReadBytes(data, 4)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + version_ = data[0]; + if (version_ != 1) { + // Unknown version. + last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION; + return false; + } + + ptr_encoding_ = data[1]; + uint8_t fde_count_encoding = data[2]; + table_encoding_ = data[3]; + table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_); + + memory_.set_pc_offset(memory_.cur_offset()); + if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + memory_.set_pc_offset(memory_.cur_offset()); + if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + entries_offset_ = memory_.cur_offset(); + entries_end_ = offset + size; + entries_data_offset_ = offset; + cur_entries_offset_ = entries_offset_; + + return true; +} + +template <typename AddressType> +const DwarfFde* DwarfEhFrame<AddressType>::GetFdeFromIndex(size_t index) { + const FdeInfo* info = GetFdeInfoFromIndex(index); + if (info == nullptr) { + return nullptr; + } + return this->GetFdeFromOffset(info->offset); +} + +template <typename AddressType> +const typename DwarfEhFrame<AddressType>::FdeInfo* DwarfEhFrame<AddressType>::GetFdeInfoFromIndex( + size_t index) { + auto entry = fde_info_.find(index); + if (entry != fde_info_.end()) { + return &fde_info_[index]; + } + FdeInfo* info = &fde_info_[index]; + + memory_.set_data_offset(entries_data_offset_); + memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_); + memory_.set_pc_offset(memory_.cur_offset()); + uint64_t value; + if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) || + !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + fde_info_.erase(index); + return nullptr; + } + info->pc = value; + return info; +} + +template <typename AddressType> +bool DwarfEhFrame<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, + uint64_t total_entries) { + CHECK(fde_count_ > 0); + CHECK(total_entries <= fde_count_); + + size_t first = 0; + size_t last = total_entries; + while (first < last) { + size_t current = (first + last) / 2; + const FdeInfo* info = GetFdeInfoFromIndex(current); + if (pc == info->pc) { + *fde_offset = info->offset; + return true; + } + if (pc < info->pc) { + last = current; + } else { + first = current + 1; + } + } + if (last != 0) { + const FdeInfo* info = GetFdeInfoFromIndex(last - 1); + *fde_offset = info->offset; + return true; + } + return false; +} + +template <typename AddressType> +bool DwarfEhFrame<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) { + CHECK(fde_count_ != 0); + last_error_ = DWARF_ERROR_NONE; + // We can do a binary search if the pc is in the range of the elements + // that have already been cached. + if (!fde_info_.empty()) { + const FdeInfo* info = &fde_info_[fde_info_.size() - 1]; + if (pc >= info->pc) { + *fde_offset = info->offset; + return true; + } + if (pc < info->pc) { + return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size()); + } + } + + if (cur_entries_offset_ == 0) { + // All entries read, or error encountered. + return false; + } + + memory_.set_data_offset(entries_data_offset_); + memory_.set_cur_offset(cur_entries_offset_); + cur_entries_offset_ = 0; + + FdeInfo* prev_info = nullptr; + for (size_t current = fde_info_.size(); + current < fde_count_ && memory_.cur_offset() < entries_end_; current++) { + memory_.set_pc_offset(memory_.cur_offset()); + uint64_t value; + if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + FdeInfo* info = &fde_info_[current]; + if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) { + fde_info_.erase(current); + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + info->pc = value; + + if (pc < info->pc) { + if (prev_info == nullptr) { + return false; + } + cur_entries_offset_ = memory_.cur_offset(); + *fde_offset = prev_info->offset; + return true; + } + prev_info = info; + } + + if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) { + *fde_offset = prev_info->offset; + return true; + } + return false; +} + +template <typename AddressType> +bool DwarfEhFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) { + if (fde_count_ == 0) { + return false; + } + + if (table_entry_size_ > 0) { + // Do a binary search since the size of each table entry is fixed. + return GetFdeOffsetBinary(pc, fde_offset, fde_count_); + } else { + // Do a sequential search since each table entry size is variable. + return GetFdeOffsetSequential(pc, fde_offset); + } +} + +// Explicitly instantiate DwarfEhFrame. +template class DwarfEhFrame<uint32_t>; +template class DwarfEhFrame<uint64_t>; + +} // namespace unwindstack
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h new file mode 100644 index 0000000..4207b42 --- /dev/null +++ b/libunwindstack/DwarfEhFrame.h
@@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_H +#define _LIBUNWINDSTACK_DWARF_EH_FRAME_H + +#include <stdint.h> + +#include <unwindstack/DwarfSection.h> + +namespace unwindstack { + +// Forward declarations. +class Memory; + +template <typename AddressType> +class DwarfEhFrame : public DwarfSectionImpl<AddressType> { + public: + // Add these so that the protected members of DwarfSectionImpl + // can be accessed without needing a this->. + using DwarfSectionImpl<AddressType>::memory_; + using DwarfSectionImpl<AddressType>::fde_count_; + using DwarfSectionImpl<AddressType>::last_error_; + + struct FdeInfo { + AddressType pc; + uint64_t offset; + }; + + DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {} + virtual ~DwarfEhFrame() = default; + + bool Init(uint64_t offset, uint64_t size) override; + + bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override; + + const DwarfFde* GetFdeFromIndex(size_t index) override; + + bool IsCie32(uint32_t value32) override { return value32 == 0; } + + bool IsCie64(uint64_t value64) override { return value64 == 0; } + + uint64_t GetCieOffsetFromFde32(uint32_t pointer) override { + return memory_.cur_offset() - pointer - 4; + } + + uint64_t GetCieOffsetFromFde64(uint64_t pointer) override { + return memory_.cur_offset() - pointer - 8; + } + + uint64_t AdjustPcFromFde(uint64_t pc) override { + // The eh_frame uses relative pcs. + return pc + memory_.cur_offset(); + } + + const FdeInfo* GetFdeInfoFromIndex(size_t index); + + bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset); + + bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries); + + protected: + uint8_t version_; + uint8_t ptr_encoding_; + uint8_t table_encoding_; + size_t table_entry_size_; + + uint64_t ptr_offset_; + + uint64_t entries_offset_; + uint64_t entries_end_; + uint64_t entries_data_offset_; + uint64_t cur_entries_offset_ = 0; + + std::unordered_map<uint64_t, FdeInfo> fde_info_; +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h index 0ff3b8c..20db222 100644 --- a/libunwindstack/DwarfEncoding.h +++ b/libunwindstack/DwarfEncoding.h
@@ -19,6 +19,8 @@ #include <stdint.h> +namespace unwindstack { + enum DwarfEncoding : uint8_t { DW_EH_PE_omit = 0xff, @@ -44,4 +46,6 @@ DW_EH_PE_block = 0x0f, }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/libunwindstack/DwarfError.h b/libunwindstack/DwarfError.h index c00f17d..54199b8 100644 --- a/libunwindstack/DwarfError.h +++ b/libunwindstack/DwarfError.h
@@ -19,6 +19,8 @@ #include <stdint.h> +namespace unwindstack { + enum DwarfError : uint8_t { DWARF_ERROR_NONE, DWARF_ERROR_MEMORY_INVALID, @@ -31,4 +33,6 @@ DWARF_ERROR_UNSUPPORTED_VERSION, }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp index 11806ea..b6e0412 100644 --- a/libunwindstack/DwarfMemory.cpp +++ b/libunwindstack/DwarfMemory.cpp
@@ -14,14 +14,17 @@ * limitations under the License. */ -#include <assert.h> #include <stdint.h> #include <string> +#include <unwindstack/DwarfMemory.h> +#include <unwindstack/Memory.h> + +#include "Check.h" #include "DwarfEncoding.h" -#include "DwarfMemory.h" -#include "Memory.h" + +namespace unwindstack { bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) { if (!memory_->Read(cur_offset_, dst, num_bytes)) { @@ -100,8 +103,8 @@ } bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) { - assert((encoding & 0x0f) == 0); - assert(encoding != DW_EH_PE_aligned); + CHECK((encoding & 0x0f) == 0); + CHECK(encoding != DW_EH_PE_aligned); // Handle the encoding. switch (encoding) { @@ -246,3 +249,5 @@ template bool DwarfMemory::ReadEncodedValue<uint32_t>(uint8_t, uint64_t*); template bool DwarfMemory::ReadEncodedValue<uint64_t>(uint8_t, uint64_t*); + +} // namespace unwindstack
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp index 507ca08..b3fd0df 100644 --- a/libunwindstack/DwarfOp.cpp +++ b/libunwindstack/DwarfOp.cpp
@@ -22,12 +22,15 @@ #include <android-base/stringprintf.h> +#include <unwindstack/DwarfMemory.h> +#include <unwindstack/Log.h> +#include <unwindstack/Memory.h> +#include <unwindstack/Regs.h> + #include "DwarfError.h" -#include "DwarfMemory.h" #include "DwarfOp.h" -#include "Log.h" -#include "Memory.h" -#include "Regs.h" + +namespace unwindstack { template <typename AddressType> constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256]; @@ -460,3 +463,5 @@ // Explicitly instantiate DwarfOp. template class DwarfOp<uint32_t>; template class DwarfOp<uint64_t>; + +} // namespace unwindstack
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h index 0f4b36d..c29bf35 100644 --- a/libunwindstack/DwarfOp.h +++ b/libunwindstack/DwarfOp.h
@@ -25,6 +25,9 @@ #include <vector> #include "DwarfEncoding.h" +#include "DwarfError.h" + +namespace unwindstack { enum DwarfVersion : uint8_t { DWARF_VERSION_2 = 2, @@ -1633,4 +1636,6 @@ }; }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp index 4c98aa3..1234eb1 100644 --- a/libunwindstack/DwarfSection.cpp +++ b/libunwindstack/DwarfSection.cpp
@@ -16,16 +16,22 @@ #include <stdint.h> +#include <unwindstack/DwarfLocation.h> +#include <unwindstack/DwarfMemory.h> +#include <unwindstack/DwarfSection.h> +#include <unwindstack/DwarfStructs.h> +#include <unwindstack/Log.h> +#include <unwindstack/Memory.h> +#include <unwindstack/Regs.h> + #include "DwarfCfa.h" +#include "DwarfEncoding.h" #include "DwarfError.h" -#include "DwarfLocation.h" -#include "DwarfMemory.h" #include "DwarfOp.h" -#include "DwarfSection.h" -#include "DwarfStructs.h" -#include "Log.h" -#include "Memory.h" -#include "Regs.h" + +namespace unwindstack { + +DwarfSection::DwarfSection(Memory* memory) : memory_(memory), last_error_(DWARF_ERROR_NONE) {} const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) { uint64_t fde_offset; @@ -42,6 +48,7 @@ } bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory) { + last_error_ = DWARF_ERROR_NONE; const DwarfFde* fde = GetFdeFromPc(pc); if (fde == nullptr || fde->cie == nullptr) { last_error_ = DWARF_ERROR_ILLEGAL_STATE; @@ -248,6 +255,9 @@ last_error_ = DWARF_ERROR_MEMORY_INVALID; return false; } + // Set the default for the lsda encoding. + cie->lsda_encoding = DW_EH_PE_omit; + if (length32 == static_cast<uint32_t>(-1)) { // 64 bit Cie uint64_t length64; @@ -541,3 +551,5 @@ // Explicitly instantiate DwarfSectionImpl template class DwarfSectionImpl<uint32_t>; template class DwarfSectionImpl<uint64_t>; + +} // namespace unwindstack
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp index 272b5f0..4fc7c67 100644 --- a/libunwindstack/Elf.cpp +++ b/libunwindstack/Elf.cpp
@@ -23,12 +23,17 @@ #define LOG_TAG "unwind" #include <log/log.h> -#include "Elf.h" -#include "ElfInterface.h" +#include <unwindstack/Elf.h> +#include <unwindstack/ElfInterface.h> +#include <unwindstack/MapInfo.h> +#include <unwindstack/Memory.h> +#include <unwindstack/Regs.h> + #include "ElfInterfaceArm.h" #include "Machine.h" -#include "Memory.h" -#include "Regs.h" +#include "Symbols.h" + +namespace unwindstack { bool Elf::Init() { if (!memory_) { @@ -49,6 +54,59 @@ return valid_; } +// It is expensive to initialize the .gnu_debugdata section. Provide a method +// to initialize this data separately. +void Elf::InitGnuDebugdata() { + if (!valid_ || interface_->gnu_debugdata_offset() == 0) { + return; + } + + gnu_debugdata_memory_.reset(interface_->CreateGnuDebugdataMemory()); + gnu_debugdata_interface_.reset(CreateInterfaceFromMemory(gnu_debugdata_memory_.get())); + ElfInterface* gnu = gnu_debugdata_interface_.get(); + if (gnu == nullptr) { + return; + } + if (gnu->Init()) { + gnu->InitHeaders(); + } else { + // Free all of the memory associated with the gnu_debugdata section. + gnu_debugdata_memory_.reset(nullptr); + gnu_debugdata_interface_.reset(nullptr); + } +} + +bool Elf::GetSoname(std::string* name) { + return valid_ && interface_->GetSoname(name); +} + +uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) { + uint64_t load_bias = 0; + if (valid()) { + load_bias = interface_->load_bias(); + } + + return pc - map_info->start + load_bias + map_info->elf_offset; +} + +bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) { + return valid_ && (interface_->GetFunctionName(addr, name, func_offset) || + (gnu_debugdata_interface_ && + gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset))); +} + +bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) { + return valid_ && (regs->StepIfSignalHandler(rel_pc, this, process_memory) || + interface_->Step(rel_pc, regs, process_memory) || + (gnu_debugdata_interface_ && + gnu_debugdata_interface_->Step(rel_pc, regs, process_memory))); +} + +uint64_t Elf::GetLoadBias() { + if (!valid_) return 0; + return interface_->load_bias(); +} + bool Elf::IsValidElf(Memory* memory) { if (memory == nullptr) { return false; @@ -90,24 +148,27 @@ machine_type_ = e_machine; if (e_machine == EM_ARM) { interface.reset(new ElfInterfaceArm(memory)); - } else { + } else if (e_machine == EM_386) { interface.reset(new ElfInterface32(memory)); + } else { + ALOGI("32 bit elf that is neither arm nor x86: e_machine = %d\n", e_machine); + return nullptr; } } else if (class_type_ == ELFCLASS64) { Elf64_Half e_machine; if (!memory->Read(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) { return nullptr; } - if (e_machine != EM_AARCH64 && e_machine != EM_X86_64) { // Unsupported. ALOGI("64 bit elf that is neither aarch64 nor x86_64: e_machine = %d\n", e_machine); return nullptr; } - machine_type_ = e_machine; interface.reset(new ElfInterface64(memory)); } return interface.release(); } + +} // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp index 087457c..75abc85 100644 --- a/libunwindstack/ElfInterface.cpp +++ b/libunwindstack/ElfInterface.cpp
@@ -20,9 +20,102 @@ #include <memory> #include <string> -#include "ElfInterface.h" -#include "Memory.h" -#include "Regs.h" +#include <7zCrc.h> +#include <Xz.h> +#include <XzCrc64.h> + +#include <unwindstack/DwarfSection.h> +#include <unwindstack/ElfInterface.h> +#include <unwindstack/Log.h> +#include <unwindstack/Memory.h> +#include <unwindstack/Regs.h> + +#include "DwarfDebugFrame.h" +#include "DwarfEhFrame.h" +#include "Symbols.h" + +namespace unwindstack { + +ElfInterface::~ElfInterface() { + for (auto symbol : symbols_) { + delete symbol; + } +} + +Memory* ElfInterface::CreateGnuDebugdataMemory() { + if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) { + return nullptr; + } + + // TODO: Only call these initialization functions once. + CrcGenerateTable(); + Crc64GenerateTable(); + + std::vector<uint8_t> src(gnu_debugdata_size_); + if (!memory_->Read(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) { + gnu_debugdata_offset_ = 0; + gnu_debugdata_size_ = static_cast<uint64_t>(-1); + return nullptr; + } + + ISzAlloc alloc; + CXzUnpacker state; + alloc.Alloc = [](void*, size_t size) { return malloc(size); }; + alloc.Free = [](void*, void* ptr) { return free(ptr); }; + + XzUnpacker_Construct(&state, &alloc); + + std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer); + int return_val; + size_t src_offset = 0; + size_t dst_offset = 0; + ECoderStatus status; + dst->Resize(5 * gnu_debugdata_size_); + do { + size_t src_remaining = src.size() - src_offset; + size_t dst_remaining = dst->Size() - dst_offset; + if (dst_remaining < 2 * gnu_debugdata_size_) { + dst->Resize(dst->Size() + 2 * gnu_debugdata_size_); + dst_remaining += 2 * gnu_debugdata_size_; + } + return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset], + &src_remaining, CODER_FINISH_ANY, &status); + src_offset += src_remaining; + dst_offset += dst_remaining; + } while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED); + XzUnpacker_Free(&state); + if (return_val != SZ_OK || !XzUnpacker_IsStreamWasFinished(&state)) { + gnu_debugdata_offset_ = 0; + gnu_debugdata_size_ = static_cast<uint64_t>(-1); + return nullptr; + } + + // Shrink back down to the exact size. + dst->Resize(dst_offset); + + return dst.release(); +} + +template <typename AddressType> +void ElfInterface::InitHeadersWithTemplate() { + if (eh_frame_offset_ != 0) { + eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_)); + if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) { + eh_frame_.reset(nullptr); + eh_frame_offset_ = 0; + eh_frame_size_ = static_cast<uint64_t>(-1); + } + } + + if (debug_frame_offset_ != 0) { + debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_)); + if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_)) { + debug_frame_.reset(nullptr); + debug_frame_offset_ = 0; + debug_frame_size_ = static_cast<uint64_t>(-1); + } + } +} template <typename EhdrType, typename PhdrType, typename ShdrType> bool ElfInterface::ReadAllHeaders() { @@ -34,7 +127,13 @@ if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr)) { return false; } - return ReadSectionHeaders<EhdrType, ShdrType>(ehdr); + + // We could still potentially unwind without the section header + // information, so ignore any errors. + if (!ReadSectionHeaders<EhdrType, ShdrType>(ehdr)) { + log(0, "Malformed section header found, ignoring..."); + } + return true; } template <typename EhdrType, typename PhdrType> @@ -124,12 +223,39 @@ } // Skip the first header, it's always going to be NULL. + offset += ehdr.e_shentsize; for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) { if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) { return false; } - if (shdr.sh_type == SHT_PROGBITS) { + if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) { + if (!memory_->Read(offset, &shdr, sizeof(shdr))) { + return false; + } + // Need to go get the information about the section that contains + // the string terminated names. + ShdrType str_shdr; + if (shdr.sh_link >= ehdr.e_shnum) { + return false; + } + uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize; + if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_type, sizeof(str_shdr.sh_type))) { + return false; + } + if (str_shdr.sh_type != SHT_STRTAB) { + return false; + } + if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_offset, + sizeof(str_shdr.sh_offset))) { + return false; + } + if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_size, sizeof(str_shdr.sh_size))) { + return false; + } + symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize, + str_shdr.sh_offset, str_shdr.sh_size)); + } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) { // Look for the .debug_frame and .gnu_debugdata. if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) { return false; @@ -137,18 +263,20 @@ if (shdr.sh_name < sec_size) { std::string name; if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) { + uint64_t* offset_ptr = nullptr; + uint64_t* size_ptr = nullptr; if (name == ".debug_frame") { - if (memory_->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; - } + offset_ptr = &debug_frame_offset_; + size_ptr = &debug_frame_size_; } else if (name == ".gnu_debugdata") { - 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; - } + offset_ptr = &gnu_debugdata_offset_; + size_ptr = &gnu_debugdata_size_; + } + if (offset_ptr != nullptr && + memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && + memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { + *offset_ptr = shdr.sh_offset; + *size_ptr = shdr.sh_size; } } } @@ -205,11 +333,47 @@ return true; } -bool ElfInterface::Step(uint64_t, Regs*, Memory*) { +template <typename SymType> +bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name, + uint64_t* func_offset) { + if (symbols_.empty()) { + return false; + } + + for (const auto symbol : symbols_) { + if (symbol->GetName<SymType>(addr, load_bias_, memory_, name, func_offset)) { + return true; + } + } + return false; +} + +bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory) { + // Need to subtract off the load_bias to get the correct pc. + if (pc < load_bias_) { + return false; + } + pc -= load_bias_; + + // Try the eh_frame first. + DwarfSection* eh_frame = eh_frame_.get(); + if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory)) { + return true; + } + + // Try the debug_frame next. + DwarfSection* debug_frame = debug_frame_.get(); + if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory)) { + return true; + } + return false; } // Instantiate all of the needed template functions. +template void ElfInterface::InitHeadersWithTemplate<uint32_t>(); +template void ElfInterface::InitHeadersWithTemplate<uint64_t>(); + template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(); template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(); @@ -221,3 +385,10 @@ template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*); template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*); + +template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*, + uint64_t*); +template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*, + uint64_t*); + +} // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp index bab84cc..66bc51f 100644 --- a/libunwindstack/ElfInterfaceArm.cpp +++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -17,12 +17,14 @@ #include <elf.h> #include <stdint.h> +#include <unwindstack/Memory.h> +#include <unwindstack/Regs.h> + #include "ArmExidx.h" -#include "ElfInterface.h" #include "ElfInterfaceArm.h" #include "Machine.h" -#include "Memory.h" -#include "Regs.h" + +namespace unwindstack { bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) { if (start_offset_ == 0 || total_entries_ == 0) { @@ -127,3 +129,5 @@ } return false; } + +} // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h index ece694f..1f4e8cb 100644 --- a/libunwindstack/ElfInterfaceArm.h +++ b/libunwindstack/ElfInterfaceArm.h
@@ -23,8 +23,10 @@ #include <iterator> #include <unordered_map> -#include "ElfInterface.h" -#include "Memory.h" +#include <unwindstack/ElfInterface.h> +#include <unwindstack/Memory.h> + +namespace unwindstack { class ElfInterfaceArm : public ElfInterface32 { public: @@ -87,4 +89,6 @@ std::unordered_map<size_t, uint32_t> addrs_; }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
diff --git a/libunwindstack/Log.cpp b/libunwindstack/Log.cpp index faeb66c..436e23c 100644 --- a/libunwindstack/Log.cpp +++ b/libunwindstack/Log.cpp
@@ -25,7 +25,9 @@ #include <android-base/stringprintf.h> -#include "Log.h" +#include <unwindstack/Log.h> + +namespace unwindstack { static bool g_print_to_stdout = false; @@ -51,3 +53,5 @@ } va_end(args); } + +} // namespace unwindstack
diff --git a/libunwindstack/Machine.h b/libunwindstack/Machine.h index 323ce80..1fb9309 100644 --- a/libunwindstack/Machine.h +++ b/libunwindstack/Machine.h
@@ -19,6 +19,8 @@ #include <stdint.h> +namespace unwindstack { + enum ArmReg : uint16_t { ARM_REG_R0 = 0, ARM_REG_R1, @@ -83,51 +85,57 @@ ARM64_REG_LR = ARM64_REG_R30, }; +// Matches the numbers for the registers as generated by compilers. +// If this is changed, then unwinding will fail. enum X86Reg : uint16_t { X86_REG_EAX = 0, - X86_REG_ECX, - X86_REG_EDX, - X86_REG_EBX, - X86_REG_ESP, - X86_REG_EBP, - X86_REG_ESI, - X86_REG_EDI, - X86_REG_EIP, - X86_REG_EFL, - X86_REG_CS, - X86_REG_SS, - X86_REG_DS, - X86_REG_ES, - X86_REG_FS, - X86_REG_GS, + X86_REG_ECX = 1, + X86_REG_EDX = 2, + X86_REG_EBX = 3, + X86_REG_ESP = 4, + X86_REG_EBP = 5, + X86_REG_ESI = 6, + X86_REG_EDI = 7, + X86_REG_EIP = 8, + X86_REG_EFL = 9, + X86_REG_CS = 10, + X86_REG_SS = 11, + X86_REG_DS = 12, + X86_REG_ES = 13, + X86_REG_FS = 14, + X86_REG_GS = 15, X86_REG_LAST, X86_REG_SP = X86_REG_ESP, X86_REG_PC = X86_REG_EIP, }; +// Matches the numbers for the registers as generated by compilers. +// If this is changed, then unwinding will fail. enum X86_64Reg : uint16_t { X86_64_REG_RAX = 0, - X86_64_REG_RDX, - X86_64_REG_RCX, - X86_64_REG_RBX, - X86_64_REG_RSI, - X86_64_REG_RDI, - X86_64_REG_RBP, - X86_64_REG_RSP, - X86_64_REG_R8, - X86_64_REG_R9, - X86_64_REG_R10, - X86_64_REG_R11, - X86_64_REG_R12, - X86_64_REG_R13, - X86_64_REG_R14, - X86_64_REG_R15, - X86_64_REG_RIP, + X86_64_REG_RDX = 1, + X86_64_REG_RCX = 2, + X86_64_REG_RBX = 3, + X86_64_REG_RSI = 4, + X86_64_REG_RDI = 5, + X86_64_REG_RBP = 6, + X86_64_REG_RSP = 7, + X86_64_REG_R8 = 8, + X86_64_REG_R9 = 9, + X86_64_REG_R10 = 10, + X86_64_REG_R11 = 11, + X86_64_REG_R12 = 12, + X86_64_REG_R13 = 13, + X86_64_REG_R14 = 14, + X86_64_REG_R15 = 15, + X86_64_REG_RIP = 16, X86_64_REG_LAST, X86_64_REG_SP = X86_64_REG_RSP, X86_64_REG_PC = X86_64_REG_RIP, }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_MACHINE_H
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp index 051f700..d0e1216 100644 --- a/libunwindstack/MapInfo.cpp +++ b/libunwindstack/MapInfo.cpp
@@ -20,10 +20,12 @@ #include <memory> #include <string> -#include "Elf.h" -#include "MapInfo.h" -#include "Maps.h" -#include "Memory.h" +#include <unwindstack/Elf.h> +#include <unwindstack/MapInfo.h> +#include <unwindstack/Maps.h> +#include <unwindstack/Memory.h> + +namespace unwindstack { Memory* MapInfo::CreateMemory(pid_t pid) { if (end <= start) { @@ -73,14 +75,18 @@ return new MemoryRange(memory, start, end); } -Elf* MapInfo::GetElf(pid_t pid, bool) { +Elf* MapInfo::GetElf(pid_t pid, bool init_gnu_debugdata) { if (elf) { return elf; } elf = new Elf(CreateMemory(pid)); - elf->Init(); + if (elf->Init() && init_gnu_debugdata) { + elf->InitGnuDebugdata(); + } // If the init fails, keep the elf around as an invalid object so we // don't try to reinit the object. return elf; } + +} // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp index b369c43..8a90423 100644 --- a/libunwindstack/Maps.cpp +++ b/libunwindstack/Maps.cpp
@@ -29,7 +29,11 @@ #include <string> #include <vector> -#include "Maps.h" +#include <unwindstack/Elf.h> +#include <unwindstack/Maps.h> +#include <unwindstack/Memory.h> + +namespace unwindstack { MapInfo* Maps::Find(uint64_t pc) { if (maps_.empty()) { @@ -71,15 +75,17 @@ 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; + if (line[name_pos] != '\0') { + 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.substr(0, 5) == "/dev/" && map_info->name.substr(5, 7) != "ashmem/") { + map_info->flags |= MAPS_FLAGS_DEVICE_MAP; + } } return true; @@ -194,3 +200,5 @@ } return true; } + +} // namespace unwindstack
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp index 9e46509..8c36055 100644 --- a/libunwindstack/Memory.cpp +++ b/libunwindstack/Memory.cpp
@@ -28,7 +28,11 @@ #include <android-base/unique_fd.h> -#include "Memory.h" +#include <unwindstack/Memory.h> + +#include "Check.h" + +namespace unwindstack { bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) { string->clear(); @@ -245,6 +249,11 @@ return true; } +MemoryRange::MemoryRange(Memory* memory, uint64_t begin, uint64_t end) + : memory_(memory), begin_(begin), length_(end - begin) { + CHECK(end > begin); +} + bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) { uint64_t max_read; if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) { @@ -253,3 +262,5 @@ // The check above guarantees that addr + begin_ will not overflow. return memory_->Read(addr + begin_, dst, size); } + +} // namespace unwindstack
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp index e7d10b2..dea7b87 100644 --- a/libunwindstack/Regs.cpp +++ b/libunwindstack/Regs.cpp
@@ -14,7 +14,6 @@ * limitations under the License. */ -#include <assert.h> #include <elf.h> #include <stdint.h> #include <sys/ptrace.h> @@ -22,28 +21,23 @@ #include <vector> -#include "Elf.h" -#include "ElfInterface.h" +#include <unwindstack/Elf.h> +#include <unwindstack/MapInfo.h> +#include <unwindstack/Memory.h> +#include <unwindstack/Regs.h> + +#include "Check.h" #include "Machine.h" -#include "MapInfo.h" -#include "Regs.h" +#include "Ucontext.h" #include "User.h" -template <typename AddressType> -uint64_t RegsImpl<AddressType>::GetRelPc(Elf* elf, const MapInfo* map_info) { - uint64_t load_bias = 0; - if (elf->valid()) { - load_bias = elf->interface()->load_bias(); - } - - return pc_ - map_info->start + load_bias + map_info->elf_offset; -} +namespace unwindstack { template <typename AddressType> bool RegsImpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) { switch (return_loc_.type) { case LOCATION_REGISTER: - assert(return_loc_.value < total_regs_); + CHECK(return_loc_.value < total_regs_); *value = regs_[return_loc_.value]; return true; case LOCATION_SP_OFFSET: @@ -88,6 +82,11 @@ return rel_pc - 4; } +void RegsArm::SetFromRaw() { + set_pc(regs_[ARM_REG_PC]); + set_sp(regs_[ARM_REG_SP]); +} + RegsArm64::RegsArm64() : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {} @@ -102,6 +101,11 @@ return rel_pc - 4; } +void RegsArm64::SetFromRaw() { + set_pc(regs_[ARM64_REG_PC]); + set_sp(regs_[ARM64_REG_SP]); +} + RegsX86::RegsX86() : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {} @@ -116,6 +120,11 @@ return rel_pc - 1; } +void RegsX86::SetFromRaw() { + set_pc(regs_[X86_REG_PC]); + set_sp(regs_[X86_REG_SP]); +} + RegsX86_64::RegsX86_64() : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {} @@ -131,15 +140,17 @@ return rel_pc - 1; } +void RegsX86_64::SetFromRaw() { + set_pc(regs_[X86_64_REG_PC]); + set_sp(regs_[X86_64_REG_SP]); +} + static Regs* ReadArm(void* remote_data) { arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data); RegsArm* regs = new RegsArm(); memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t)); - - regs->set_pc(user->regs[ARM_REG_PC]); - regs->set_sp(user->regs[ARM_REG_SP]); - + regs->SetFromRaw(); return regs; } @@ -148,9 +159,10 @@ RegsArm64* regs = new RegsArm64(); memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t)); - regs->set_pc(user->pc); - regs->set_sp(user->sp); - + uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData()); + reg_data[ARM64_REG_PC] = user->pc; + reg_data[ARM64_REG_SP] = user->sp; + regs->SetFromRaw(); return regs; } @@ -168,9 +180,7 @@ (*regs)[X86_REG_ESP] = user->esp; (*regs)[X86_REG_EIP] = user->eip; - regs->set_pc(user->eip); - regs->set_sp(user->esp); - + regs->SetFromRaw(); return regs; } @@ -196,9 +206,7 @@ (*regs)[X86_64_REG_RSP] = user->rsp; (*regs)[X86_64_REG_RIP] = user->rip; - regs->set_pc(user->rip); - regs->set_sp(user->rsp); - + regs->SetFromRaw(); return regs; } @@ -231,3 +239,302 @@ } return nullptr; } + +static Regs* CreateFromArmUcontext(void* ucontext) { + arm_ucontext_t* arm_ucontext = reinterpret_cast<arm_ucontext_t*>(ucontext); + + RegsArm* regs = new RegsArm(); + memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t)); + regs->SetFromRaw(); + return regs; +} + +static Regs* CreateFromArm64Ucontext(void* ucontext) { + arm64_ucontext_t* arm64_ucontext = reinterpret_cast<arm64_ucontext_t*>(ucontext); + + RegsArm64* regs = new RegsArm64(); + memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t)); + regs->SetFromRaw(); + return regs; +} + +void RegsX86::SetFromUcontext(x86_ucontext_t* ucontext) { + // Put the registers in the expected order. + regs_[X86_REG_EDI] = ucontext->uc_mcontext.edi; + regs_[X86_REG_ESI] = ucontext->uc_mcontext.esi; + regs_[X86_REG_EBP] = ucontext->uc_mcontext.ebp; + regs_[X86_REG_ESP] = ucontext->uc_mcontext.esp; + regs_[X86_REG_EBX] = ucontext->uc_mcontext.ebx; + regs_[X86_REG_EDX] = ucontext->uc_mcontext.edx; + regs_[X86_REG_ECX] = ucontext->uc_mcontext.ecx; + regs_[X86_REG_EAX] = ucontext->uc_mcontext.eax; + regs_[X86_REG_EIP] = ucontext->uc_mcontext.eip; + SetFromRaw(); +} + +static Regs* CreateFromX86Ucontext(void* ucontext) { + x86_ucontext_t* x86_ucontext = reinterpret_cast<x86_ucontext_t*>(ucontext); + + RegsX86* regs = new RegsX86(); + regs->SetFromUcontext(x86_ucontext); + return regs; +} + +void RegsX86_64::SetFromUcontext(x86_64_ucontext_t* ucontext) { + // R8-R15 + memcpy(®s_[X86_64_REG_R8], &ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t)); + + // Rest of the registers. + regs_[X86_64_REG_RDI] = ucontext->uc_mcontext.rdi; + regs_[X86_64_REG_RSI] = ucontext->uc_mcontext.rsi; + regs_[X86_64_REG_RBP] = ucontext->uc_mcontext.rbp; + regs_[X86_64_REG_RBX] = ucontext->uc_mcontext.rbx; + regs_[X86_64_REG_RDX] = ucontext->uc_mcontext.rdx; + regs_[X86_64_REG_RAX] = ucontext->uc_mcontext.rax; + regs_[X86_64_REG_RCX] = ucontext->uc_mcontext.rcx; + regs_[X86_64_REG_RSP] = ucontext->uc_mcontext.rsp; + regs_[X86_64_REG_RIP] = ucontext->uc_mcontext.rip; + + SetFromRaw(); +} + +static Regs* CreateFromX86_64Ucontext(void* ucontext) { + x86_64_ucontext_t* x86_64_ucontext = reinterpret_cast<x86_64_ucontext_t*>(ucontext); + + RegsX86_64* regs = new RegsX86_64(); + regs->SetFromUcontext(x86_64_ucontext); + return regs; +} + +Regs* Regs::CreateFromUcontext(uint32_t machine_type, void* ucontext) { + switch (machine_type) { + case EM_386: + return CreateFromX86Ucontext(ucontext); + case EM_X86_64: + return CreateFromX86_64Ucontext(ucontext); + case EM_ARM: + return CreateFromArmUcontext(ucontext); + case EM_AARCH64: + return CreateFromArm64Ucontext(ucontext); + } + return nullptr; +} + +uint32_t Regs::GetMachineType() { +#if defined(__arm__) + return EM_ARM; +#elif defined(__aarch64__) + return EM_AARCH64; +#elif defined(__i386__) + return EM_386; +#elif defined(__x86_64__) + return EM_X86_64; +#else + abort(); +#endif +} + +Regs* Regs::CreateFromLocal() { + Regs* regs; +#if defined(__arm__) + regs = new RegsArm(); +#elif defined(__aarch64__) + regs = new RegsArm64(); +#elif defined(__i386__) + regs = new RegsX86(); +#elif defined(__x86_64__) + regs = new RegsX86_64(); +#else + abort(); +#endif + return regs; +} + +bool RegsArm::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) { + uint32_t data; + Memory* elf_memory = elf->memory(); + // Read from elf memory since it is usually more expensive to read from + // process memory. + if (!elf_memory->Read(rel_pc, &data, sizeof(data))) { + return false; + } + + uint64_t offset = 0; + if (data == 0xe3a07077 || data == 0xef900077 || data == 0xdf002777) { + // non-RT sigreturn call. + // __restore: + // + // Form 1 (arm): + // 0x77 0x70 mov r7, #0x77 + // 0xa0 0xe3 svc 0x00000000 + // + // Form 2 (arm): + // 0x77 0x00 0x90 0xef svc 0x00900077 + // + // Form 3 (thumb): + // 0x77 0x27 movs r7, #77 + // 0x00 0xdf svc 0 + if (!process_memory->Read(sp(), &data, sizeof(data))) { + return false; + } + if (data == 0x5ac3c35a) { + // SP + uc_mcontext offset + r0 offset. + offset = sp() + 0x14 + 0xc; + } else { + // SP + r0 offset + offset = sp() + 0xc; + } + } else if (data == 0xe3a070ad || data == 0xef9000ad || data == 0xdf0027ad) { + // RT sigreturn call. + // __restore_rt: + // + // Form 1 (arm): + // 0xad 0x70 mov r7, #0xad + // 0xa0 0xe3 svc 0x00000000 + // + // Form 2 (arm): + // 0xad 0x00 0x90 0xef svc 0x009000ad + // + // Form 3 (thumb): + // 0xad 0x27 movs r7, #ad + // 0x00 0xdf svc 0 + if (!process_memory->Read(sp(), &data, sizeof(data))) { + return false; + } + if (data == sp() + 8) { + // SP + 8 + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset + offset = sp() + 8 + 0x80 + 0x14 + 0xc; + } else { + // SP + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset + offset = sp() + 0x80 + 0x14 + 0xc; + } + } + if (offset == 0) { + return false; + } + + if (!process_memory->Read(offset, regs_.data(), sizeof(uint32_t) * ARM_REG_LAST)) { + return false; + } + SetFromRaw(); + return true; +} + +bool RegsArm64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) { + uint64_t data; + Memory* elf_memory = elf->memory(); + // Read from elf memory since it is usually more expensive to read from + // process memory. + if (!elf_memory->Read(rel_pc, &data, sizeof(data))) { + return false; + } + + // Look for the kernel sigreturn function. + // __kernel_rt_sigreturn: + // 0xd2801168 mov x8, #0x8b + // 0xd4000001 svc #0x0 + if (data != 0xd4000001d2801168ULL) { + return false; + } + + // SP + sizeof(siginfo_t) + uc_mcontext offset + X0 offset. + if (!process_memory->Read(sp() + 0x80 + 0xb0 + 0x08, regs_.data(), + sizeof(uint64_t) * ARM64_REG_LAST)) { + return false; + } + + SetFromRaw(); + return true; +} + +bool RegsX86::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) { + uint64_t data; + Memory* elf_memory = elf->memory(); + // Read from elf memory since it is usually more expensive to read from + // process memory. + if (!elf_memory->Read(rel_pc, &data, sizeof(data))) { + return false; + } + + if (data == 0x80cd00000077b858ULL) { + // Without SA_SIGINFO set, the return sequence is: + // + // __restore: + // 0x58 pop %eax + // 0xb8 0x77 0x00 0x00 0x00 movl 0x77,%eax + // 0xcd 0x80 int 0x80 + // + // SP points at arguments: + // int signum + // struct sigcontext (same format as mcontext) + struct x86_mcontext_t context; + if (!process_memory->Read(sp() + 4, &context, sizeof(context))) { + return false; + } + regs_[X86_REG_EBP] = context.ebp; + regs_[X86_REG_ESP] = context.esp; + regs_[X86_REG_EBX] = context.ebx; + regs_[X86_REG_EDX] = context.edx; + regs_[X86_REG_ECX] = context.ecx; + regs_[X86_REG_EAX] = context.eax; + regs_[X86_REG_EIP] = context.eip; + SetFromRaw(); + return true; + } else if ((data & 0x00ffffffffffffffULL) == 0x0080cd000000adb8ULL) { + // With SA_SIGINFO set, the return sequence is: + // + // __restore_rt: + // 0xb8 0xad 0x00 0x00 0x00 movl 0xad,%eax + // 0xcd 0x80 int 0x80 + // + // SP points at arguments: + // int signum + // siginfo* + // ucontext* + + // Get the location of the sigcontext data. + uint32_t ptr; + if (!process_memory->Read(sp() + 8, &ptr, sizeof(ptr))) { + return false; + } + // Only read the portion of the data structure we care about. + x86_ucontext_t x86_ucontext; + if (!process_memory->Read(ptr + 0x14, &x86_ucontext.uc_mcontext, sizeof(x86_mcontext_t))) { + return false; + } + SetFromUcontext(&x86_ucontext); + return true; + } + return false; +} + +bool RegsX86_64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) { + uint64_t data; + Memory* elf_memory = elf->memory(); + // Read from elf memory since it is usually more expensive to read from + // process memory. + if (!elf_memory->Read(rel_pc, &data, sizeof(data)) || data != 0x0f0000000fc0c748) { + return false; + } + + uint16_t data2; + if (!elf_memory->Read(rel_pc + 8, &data2, sizeof(data2)) || data2 != 0x0f05) { + return false; + } + + // __restore_rt: + // 0x48 0xc7 0xc0 0x0f 0x00 0x00 0x00 mov $0xf,%rax + // 0x0f 0x05 syscall + // 0x0f nopl 0x0($rax) + + // Read the mcontext data from the stack. + // sp points to the ucontext data structure, read only the mcontext part. + x86_64_ucontext_t x86_64_ucontext; + if (!process_memory->Read(sp() + 0x28, &x86_64_ucontext.uc_mcontext, sizeof(x86_64_mcontext_t))) { + return false; + } + SetFromUcontext(&x86_64_ucontext); + return true; +} + +} // namespace unwindstack
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp index 86c1233..42d816a 100644 --- a/libunwindstack/Symbols.cpp +++ b/libunwindstack/Symbols.cpp
@@ -14,15 +14,18 @@ * limitations under the License. */ -#include <assert.h> #include <elf.h> #include <stdint.h> #include <string> -#include "Memory.h" +#include <unwindstack/Memory.h> + +#include "Check.h" #include "Symbols.h" +namespace unwindstack { + Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset, uint64_t str_size) : cur_offset_(offset), @@ -58,7 +61,7 @@ if (symbols_.size() != 0) { const Info* info = GetInfoFromCache(addr); if (info) { - assert(addr >= info->start_offset && addr <= info->end_offset); + CHECK(addr >= info->start_offset && addr <= info->end_offset); *func_offset = addr - info->start_offset; return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset); } @@ -108,3 +111,5 @@ // Instantiate all of the needed template functions. template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*); template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*); + +} // namespace unwindstack
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h index 3c0d033..689144b 100644 --- a/libunwindstack/Symbols.h +++ b/libunwindstack/Symbols.h
@@ -22,6 +22,8 @@ #include <string> #include <vector> +namespace unwindstack { + // Forward declaration. class Memory; @@ -61,4 +63,6 @@ std::vector<Info> symbols_; }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/Ucontext.h b/libunwindstack/Ucontext.h new file mode 100644 index 0000000..22f6a89 --- /dev/null +++ b/libunwindstack/Ucontext.h
@@ -0,0 +1,184 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBUNWINDSTACK_UCONTEXT_H +#define _LIBUNWINDSTACK_UCONTEXT_H + +#include <stdint.h> + +namespace unwindstack { + +//------------------------------------------------------------------- +// ARM ucontext structures +//------------------------------------------------------------------- +struct arm_stack_t { + uint32_t ss_sp; // void __user* + int32_t ss_flags; // int + uint32_t ss_size; // size_t +}; + +struct arm_mcontext_t { + uint32_t trap_no; // unsigned long + uint32_t error_code; // unsigned long + uint32_t oldmask; // unsigned long + uint32_t regs[ARM_REG_LAST]; // unsigned long + uint32_t cpsr; // unsigned long + uint32_t fault_address; // unsigned long +}; + +struct arm_ucontext_t { + uint32_t uc_flags; // unsigned long + uint32_t uc_link; // struct ucontext* + arm_stack_t uc_stack; + arm_mcontext_t uc_mcontext; + // Nothing else is used, so don't define it. +}; +//------------------------------------------------------------------- + +//------------------------------------------------------------------- +// ARM64 ucontext structures +//------------------------------------------------------------------- +struct arm64_stack_t { + uint64_t ss_sp; // void __user* + int32_t ss_flags; // int + uint64_t ss_size; // size_t +}; + +struct arm64_sigset_t { + uint64_t sig; // unsigned long +}; + +struct arm64_mcontext_t { + uint64_t fault_address; // __u64 + uint64_t regs[ARM64_REG_LAST]; // __u64 + uint64_t pstate; // __u64 + // Nothing else is used, so don't define it. +}; + +struct arm64_ucontext_t { + uint64_t uc_flags; // unsigned long + uint64_t uc_link; // struct ucontext* + arm64_stack_t uc_stack; + arm64_sigset_t uc_sigmask; + // The kernel adds extra padding after uc_sigmask to match glibc sigset_t on ARM64. + char __padding[128 - sizeof(arm64_sigset_t)]; + // The full structure requires 16 byte alignment, but our partial structure + // doesn't, so force the alignment. + arm64_mcontext_t uc_mcontext __attribute__((aligned(16))); +}; +//------------------------------------------------------------------- + +//------------------------------------------------------------------- +// X86 ucontext structures +//------------------------------------------------------------------- +struct x86_stack_t { + uint32_t ss_sp; // void __user* + int32_t ss_flags; // int + uint32_t ss_size; // size_t +}; + +struct x86_mcontext_t { + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + uint32_t trapno; + uint32_t err; + uint32_t eip; + uint32_t cs; + uint32_t efl; + uint32_t uesp; + uint32_t ss; + // Only care about the registers, skip everything else. +}; + +struct x86_ucontext_t { + uint32_t uc_flags; // unsigned long + uint32_t uc_link; // struct ucontext* + x86_stack_t uc_stack; + x86_mcontext_t uc_mcontext; + // Nothing else is used, so don't define it. +}; +//------------------------------------------------------------------- + +//------------------------------------------------------------------- +// X86_64 ucontext structures +//------------------------------------------------------------------- +struct x86_64_stack_t { + uint64_t ss_sp; // void __user* + int32_t ss_flags; // int + uint64_t ss_size; // size_t +}; + +struct x86_64_mcontext_t { + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rbx; + uint64_t rdx; + uint64_t rax; + uint64_t rcx; + uint64_t rsp; + uint64_t rip; + uint64_t efl; + uint64_t csgsfs; + uint64_t err; + uint64_t trapno; + uint64_t oldmask; + uint64_t cr2; + // Only care about the registers, skip everything else. +}; + +struct x86_64_ucontext_t { + uint64_t uc_flags; // unsigned long + uint64_t uc_link; // struct ucontext* + x86_64_stack_t uc_stack; + x86_64_mcontext_t uc_mcontext; + // Nothing else is used, so don't define it. +}; +//------------------------------------------------------------------- + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_UCONTEXT_H
diff --git a/libunwindstack/User.h b/libunwindstack/User.h index a695467..53f7e50 100644 --- a/libunwindstack/User.h +++ b/libunwindstack/User.h
@@ -29,6 +29,8 @@ #ifndef _LIBUNWINDSTACK_USER_H #define _LIBUNWINDSTACK_USER_H +namespace unwindstack { + struct x86_user_regs { uint32_t ebx; uint32_t ecx; @@ -93,4 +95,6 @@ // The largest user structure. constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_USER_H
diff --git a/libunwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h similarity index 95% rename from libunwindstack/DwarfLocation.h rename to libunwindstack/include/unwindstack/DwarfLocation.h index 062d125..3467e6a 100644 --- a/libunwindstack/DwarfLocation.h +++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -21,6 +21,8 @@ #include <unordered_map> +namespace unwindstack { + enum DwarfLocationEnum : uint8_t { DWARF_LOCATION_INVALID = 0, DWARF_LOCATION_UNDEFINED, @@ -38,4 +40,6 @@ typedef std::unordered_map<uint16_t, DwarfLocation> dwarf_loc_regs_t; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/DwarfMemory.h b/libunwindstack/include/unwindstack/DwarfMemory.h similarity index 97% rename from libunwindstack/DwarfMemory.h rename to libunwindstack/include/unwindstack/DwarfMemory.h index a304dd9..8dd8d2b 100644 --- a/libunwindstack/DwarfMemory.h +++ b/libunwindstack/include/unwindstack/DwarfMemory.h
@@ -19,6 +19,8 @@ #include <stdint.h> +namespace unwindstack { + // Forward declarations. class Memory; @@ -69,4 +71,6 @@ uint64_t text_offset_ = static_cast<uint64_t>(-1); }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h similarity index 93% rename from libunwindstack/DwarfSection.h rename to libunwindstack/include/unwindstack/DwarfSection.h index e617601..a97ca2b 100644 --- a/libunwindstack/DwarfSection.h +++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -22,18 +22,20 @@ #include <iterator> #include <unordered_map> -#include "DwarfError.h" -#include "DwarfLocation.h" -#include "DwarfMemory.h" -#include "DwarfStructs.h" +#include <unwindstack/DwarfLocation.h> +#include <unwindstack/DwarfMemory.h> +#include <unwindstack/DwarfStructs.h> + +namespace unwindstack { // Forward declarations. +enum DwarfError : uint8_t; class Memory; class Regs; class DwarfSection { public: - DwarfSection(Memory* memory) : memory_(memory) {} + DwarfSection(Memory* memory); virtual ~DwarfSection() = default; class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> { @@ -102,7 +104,7 @@ protected: DwarfMemory memory_; - DwarfError last_error_ = DWARF_ERROR_NONE; + DwarfError last_error_; uint64_t fde_count_; std::unordered_map<uint64_t, DwarfFde> fde_entries_; @@ -134,4 +136,6 @@ AddressType* value); }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_DWARF_SECTION_H
diff --git a/libunwindstack/DwarfStructs.h b/libunwindstack/include/unwindstack/DwarfStructs.h similarity index 92% rename from libunwindstack/DwarfStructs.h rename to libunwindstack/include/unwindstack/DwarfStructs.h index 87182c9..4b481f0 100644 --- a/libunwindstack/DwarfStructs.h +++ b/libunwindstack/include/unwindstack/DwarfStructs.h
@@ -21,12 +21,12 @@ #include <vector> -#include "DwarfEncoding.h" +namespace unwindstack { 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 fde_address_encoding = 0; + uint8_t lsda_encoding = 0; uint8_t segment_size = 0; std::vector<char> augmentation_string; uint64_t personality_handler = 0; @@ -49,4 +49,6 @@ constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1); +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h similarity index 74% rename from libunwindstack/Elf.h rename to libunwindstack/include/unwindstack/Elf.h index 7bf45b8..d89a746 100644 --- a/libunwindstack/Elf.h +++ b/libunwindstack/include/unwindstack/Elf.h
@@ -22,14 +22,17 @@ #include <memory> #include <string> -#include "ElfInterface.h" -#include "Memory.h" +#include <unwindstack/ElfInterface.h> +#include <unwindstack/Memory.h> #if !defined(EM_AARCH64) #define EM_AARCH64 183 #endif +namespace unwindstack { + // Forward declaration. +struct MapInfo; class Regs; class Elf { @@ -41,20 +44,18 @@ void InitGnuDebugdata(); - bool GetSoname(std::string* name) { - return valid_ && interface_->GetSoname(name); - } + bool GetSoname(std::string* name); - bool GetFunctionName(uint64_t, std::string*, uint64_t*) { - return false; - } + bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset); - bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) { - return valid_ && interface_->Step(rel_pc, regs, process_memory); - } + uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info); + + bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory); ElfInterface* CreateInterfaceFromMemory(Memory* memory); + uint64_t GetLoadBias(); + bool valid() { return valid_; } uint32_t machine_type() { return machine_type_; } @@ -65,6 +66,8 @@ ElfInterface* interface() { return interface_.get(); } + ElfInterface* gnu_debugdata_interface() { return gnu_debugdata_interface_.get(); } + static bool IsValidElf(Memory* memory); protected: @@ -73,6 +76,11 @@ std::unique_ptr<Memory> memory_; uint32_t machine_type_; uint8_t class_type_; + + std::unique_ptr<Memory> gnu_debugdata_memory_; + std::unique_ptr<ElfInterface> gnu_debugdata_interface_; }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_ELF_H
diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h similarity index 75% rename from libunwindstack/ElfInterface.h rename to libunwindstack/include/unwindstack/ElfInterface.h index 944146c..5cac0d3 100644 --- a/libunwindstack/ElfInterface.h +++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -25,9 +25,14 @@ #include <unordered_map> #include <vector> +#include <unwindstack/DwarfSection.h> + +namespace unwindstack { + // Forward declarations. class Memory; class Regs; +class Symbols; struct LoadInfo { uint64_t offset; @@ -44,7 +49,7 @@ class ElfInterface { public: ElfInterface(Memory* memory) : memory_(memory) {} - virtual ~ElfInterface() = default; + virtual ~ElfInterface(); virtual bool Init() = 0; @@ -68,10 +73,18 @@ uint64_t dynamic_size() { return dynamic_size_; } uint64_t eh_frame_offset() { return eh_frame_offset_; } uint64_t eh_frame_size() { return eh_frame_size_; } + uint64_t debug_frame_offset() { return debug_frame_offset_; } + uint64_t debug_frame_size() { return debug_frame_size_; } uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; } uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; } + DwarfSection* eh_frame() { return eh_frame_.get(); } + DwarfSection* debug_frame() { return debug_frame_.get(); } + protected: + template <typename AddressType> + void InitHeadersWithTemplate(); + template <typename EhdrType, typename PhdrType, typename ShdrType> bool ReadAllHeaders(); @@ -84,6 +97,9 @@ template <typename DynType> bool GetSonameWithTemplate(std::string* soname); + template <typename SymType> + bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset); + virtual bool HandleType(uint64_t, uint32_t) { return false; } Memory* memory_; @@ -105,6 +121,11 @@ uint8_t soname_type_ = SONAME_UNKNOWN; std::string soname_; + + std::unique_ptr<DwarfSection> eh_frame_; + std::unique_ptr<DwarfSection> debug_frame_; + + std::vector<Symbols*> symbols_; }; class ElfInterface32 : public ElfInterface { @@ -116,15 +137,14 @@ return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(); } - void InitHeaders() override { - } + void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); } bool GetSoname(std::string* soname) override { return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname); } - bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { - return false; + bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override { + return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset); } }; @@ -137,16 +157,17 @@ return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(); } - void InitHeaders() override { - } + void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); } bool GetSoname(std::string* soname) override { return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname); } - bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { - return false; + bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override { + return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset); } }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_ELF_INTERFACE_H
diff --git a/libunwindstack/Log.h b/libunwindstack/include/unwindstack/Log.h similarity index 93% rename from libunwindstack/Log.h rename to libunwindstack/include/unwindstack/Log.h index 2d01aa8..aa1219c 100644 --- a/libunwindstack/Log.h +++ b/libunwindstack/include/unwindstack/Log.h
@@ -19,7 +19,11 @@ #include <stdint.h> +namespace unwindstack { + void log_to_stdout(bool enable); void log(uint8_t indent, const char* format, ...); +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_LOG_H
diff --git a/libunwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h similarity index 95% rename from libunwindstack/MapInfo.h rename to libunwindstack/include/unwindstack/MapInfo.h index 79a2ada..2a97dde 100644 --- a/libunwindstack/MapInfo.h +++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -21,6 +21,8 @@ #include <string> +namespace unwindstack { + // Forward declarations. class Elf; class Memory; @@ -42,4 +44,6 @@ Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false); }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h similarity index 96% rename from libunwindstack/Maps.h rename to libunwindstack/include/unwindstack/Maps.h index 239b64a..0b02739 100644 --- a/libunwindstack/Maps.h +++ b/libunwindstack/include/unwindstack/Maps.h
@@ -23,8 +23,9 @@ #include <string> #include <vector> -#include "Elf.h" -#include "MapInfo.h" +#include <unwindstack/MapInfo.h> + +namespace unwindstack { // Special flag to indicate a map is in /dev/. However, a map in // /dev/ashmem/... does not set this flag. @@ -104,4 +105,6 @@ bool Parse() override; }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h similarity index 89% rename from libunwindstack/Memory.h rename to libunwindstack/include/unwindstack/Memory.h index f9f6d56..0c05266 100644 --- a/libunwindstack/Memory.h +++ b/libunwindstack/include/unwindstack/Memory.h
@@ -17,7 +17,6 @@ #ifndef _LIBUNWINDSTACK_MEMORY_H #define _LIBUNWINDSTACK_MEMORY_H -#include <assert.h> #include <stdint.h> #include <sys/types.h> #include <unistd.h> @@ -25,6 +24,8 @@ #include <string> #include <vector> +namespace unwindstack { + class Memory { public: Memory() = default; @@ -46,13 +47,9 @@ return Read(offset, field, size); } - inline bool Read32(uint64_t addr, uint32_t* dst) { - return Read(addr, dst, sizeof(uint32_t)); - } + inline bool Read32(uint64_t addr, uint32_t* dst) { return Read(addr, dst, sizeof(uint32_t)); } - inline bool Read64(uint64_t addr, uint64_t* dst) { - return Read(addr, dst, sizeof(uint64_t)); - } + inline bool Read64(uint64_t addr, uint64_t* dst) { return Read(addr, dst, sizeof(uint64_t)); } }; class MemoryBuffer : public Memory { @@ -128,10 +125,7 @@ class MemoryRange : public Memory { public: - MemoryRange(Memory* memory, uint64_t begin, uint64_t end) - : memory_(memory), begin_(begin), length_(end - begin) { - assert(end > begin); - } + MemoryRange(Memory* memory, uint64_t begin, uint64_t end); virtual ~MemoryRange() { delete memory_; } bool Read(uint64_t addr, void* dst, size_t size) override; @@ -142,4 +136,6 @@ uint64_t length_; }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h similarity index 76% rename from libunwindstack/Regs.h rename to libunwindstack/include/unwindstack/Regs.h index 8f5a721..78e2c0d 100644 --- a/libunwindstack/Regs.h +++ b/libunwindstack/include/unwindstack/Regs.h
@@ -21,9 +21,14 @@ #include <vector> +namespace unwindstack { + // Forward declarations. class Elf; struct MapInfo; +class Memory; +struct x86_ucontext_t; +struct x86_64_ucontext_t; class Regs { public: @@ -50,14 +55,19 @@ virtual bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) = 0; - virtual uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) = 0; - virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0; + virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0; + + virtual void SetFromRaw() = 0; + uint16_t sp_reg() { return sp_reg_; } uint16_t total_regs() { return total_regs_; } + static uint32_t GetMachineType(); static Regs* RemoteGet(pid_t pid, uint32_t* machine_type); + static Regs* CreateFromUcontext(uint32_t machine_type, void* ucontext); + static Regs* CreateFromLocal(); protected: uint16_t total_regs_; @@ -72,8 +82,6 @@ : Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {} virtual ~RegsImpl() = default; - uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) override; - bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) override; uint64_t pc() override { return pc_; } @@ -98,6 +106,10 @@ virtual ~RegsArm() = default; uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; + + void SetFromRaw() override; + + bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override; }; class RegsArm64 : public RegsImpl<uint64_t> { @@ -106,6 +118,10 @@ virtual ~RegsArm64() = default; uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; + + void SetFromRaw() override; + + bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override; }; class RegsX86 : public RegsImpl<uint32_t> { @@ -114,6 +130,12 @@ virtual ~RegsX86() = default; uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; + + void SetFromRaw() override; + + bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override; + + void SetFromUcontext(x86_ucontext_t* ucontext); }; class RegsX86_64 : public RegsImpl<uint64_t> { @@ -122,6 +144,14 @@ virtual ~RegsX86_64() = default; uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; + + void SetFromRaw() override; + + bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override; + + void SetFromUcontext(x86_64_ucontext_t* ucontext); }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h new file mode 100644 index 0000000..ffec213 --- /dev/null +++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -0,0 +1,104 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBUNWINDSTACK_REGS_GET_LOCAL_H +#define _LIBUNWINDSTACK_REGS_GET_LOCAL_H + +namespace unwindstack { + +#if defined(__arm__) + +inline void RegsGetLocal(Regs* regs) { + void* reg_data = regs->RawData(); + asm volatile( + ".align 2\n" + "bx pc\n" + "nop\n" + ".code 32\n" + "stmia %[base], {r0-r12}\n" + "add %[base], #52\n" + "mov r1, r13\n" + "mov r2, r14\n" + "mov r3, r15\n" + "stmia %[base], {r1-r3}\n" + "orr %[base], pc, #1\n" + "bx %[base]\n" + : [base] "+r"(reg_data) + : + : "memory"); + + regs->SetFromRaw(); +} + +#elif defined(__aarch64__) + +inline void RegsGetLocal(Regs* regs) { + void* reg_data = regs->RawData(); + asm volatile( + "1:\n" + "stp x0, x1, [%[base], #0]\n" + "stp x2, x3, [%[base], #16]\n" + "stp x4, x5, [%[base], #32]\n" + "stp x6, x7, [%[base], #48]\n" + "stp x8, x9, [%[base], #64]\n" + "stp x10, x11, [%[base], #80]\n" + "stp x12, x13, [%[base], #96]\n" + "stp x14, x15, [%[base], #112]\n" + "stp x16, x17, [%[base], #128]\n" + "stp x18, x19, [%[base], #144]\n" + "stp x20, x21, [%[base], #160]\n" + "stp x22, x23, [%[base], #176]\n" + "stp x24, x25, [%[base], #192]\n" + "stp x26, x27, [%[base], #208]\n" + "stp x28, x29, [%[base], #224]\n" + "str x30, [%[base], #240]\n" + "mov x12, sp\n" + "adr x13, 1b\n" + "stp x12, x13, [%[base], #248]\n" + : [base] "+r"(reg_data) + : + : "x12", "x13", "memory"); + + regs->SetFromRaw(); +} + +#elif defined(__i386__) || defined(__x86_64__) + +extern "C" void AsmGetRegs(void* regs); + +inline void RegsGetLocal(Regs* regs) { + AsmGetRegs(regs->RawData()); + + regs->SetFromRaw(); +} + +#endif + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_REGS_GET_LOCAL_H
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp index 4fff48e..94cb493 100644 --- a/libunwindstack/tests/ArmExidxDecodeTest.cpp +++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -23,13 +23,16 @@ #include <gtest/gtest.h> +#include <unwindstack/Log.h> +#include <unwindstack/Regs.h> + #include "ArmExidx.h" -#include "Regs.h" -#include "Log.h" #include "LogFake.h" #include "MemoryFake.h" +namespace unwindstack { + class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> { protected: void Init(Memory* process_memory = nullptr) { @@ -1092,3 +1095,5 @@ } INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging")); + +} // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp index aed75bf..caad131 100644 --- a/libunwindstack/tests/ArmExidxExtractTest.cpp +++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -21,12 +21,15 @@ #include <gtest/gtest.h> +#include <unwindstack/Log.h> + #include "ArmExidx.h" -#include "Log.h" #include "LogFake.h" #include "MemoryFake.h" +namespace unwindstack { + class ArmExidxExtractTest : public ::testing::Test { protected: void SetUp() override { @@ -329,3 +332,5 @@ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ("4 unwind Raw Data: 0x11 0x22 0x33 0xb0\n", GetFakeLogPrint()); } + +} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp index 967c2c2..b17ca33 100644 --- a/libunwindstack/tests/DwarfCfaLogTest.cpp +++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -23,15 +23,18 @@ #include <android-base/stringprintf.h> #include <gtest/gtest.h> +#include <unwindstack/DwarfLocation.h> +#include <unwindstack/DwarfMemory.h> +#include <unwindstack/DwarfStructs.h> +#include <unwindstack/Log.h> + #include "DwarfCfa.h" -#include "DwarfLocation.h" -#include "DwarfMemory.h" -#include "DwarfStructs.h" -#include "Log.h" #include "LogFake.h" #include "MemoryFake.h" +namespace unwindstack { + template <typename TypeParam> class DwarfCfaLogTest : public ::testing::Test { protected: @@ -812,3 +815,5 @@ typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes; INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes); + +} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp index 687af7d..73a67ac 100644 --- a/libunwindstack/tests/DwarfCfaTest.cpp +++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -21,15 +21,19 @@ #include <gtest/gtest.h> +#include <unwindstack/DwarfLocation.h> +#include <unwindstack/DwarfMemory.h> +#include <unwindstack/DwarfStructs.h> +#include <unwindstack/Log.h> + #include "DwarfCfa.h" -#include "DwarfLocation.h" -#include "DwarfMemory.h" -#include "DwarfStructs.h" -#include "Log.h" +#include "DwarfError.h" #include "LogFake.h" #include "MemoryFake.h" +namespace unwindstack { + template <typename TypeParam> class DwarfCfaTest : public ::testing::Test { protected: @@ -957,3 +961,5 @@ typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes; INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes); + +} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp new file mode 100644 index 0000000..69813e5 --- /dev/null +++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -0,0 +1,461 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "DwarfDebugFrame.h" +#include "DwarfEncoding.h" +#include "DwarfError.h" + +#include "LogFake.h" +#include "MemoryFake.h" +#include "RegsFake.h" + +namespace unwindstack { + +template <typename TypeParam> +class MockDwarfDebugFrame : public DwarfDebugFrame<TypeParam> { + public: + MockDwarfDebugFrame(Memory* memory) : DwarfDebugFrame<TypeParam>(memory) {} + ~MockDwarfDebugFrame() = default; + + void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; } + void TestSetOffset(uint64_t offset) { this->offset_ = offset; } + void TestSetEndOffset(uint64_t offset) { this->end_offset_ = offset; } + void TestPushFdeInfo(const typename DwarfDebugFrame<TypeParam>::FdeInfo& info) { + this->fdes_.push_back(info); + } + + uint64_t TestGetFdeCount() { return this->fde_count_; } + uint8_t TestGetOffset() { return this->offset_; } + uint8_t TestGetEndOffset() { return this->end_offset_; } + void TestGetFdeInfo(size_t index, typename DwarfDebugFrame<TypeParam>::FdeInfo* info) { + *info = this->fdes_[index]; + } +}; + +template <typename TypeParam> +class DwarfDebugFrameTest : public ::testing::Test { + protected: + void SetUp() override { + memory_.Clear(); + debug_frame_ = new MockDwarfDebugFrame<TypeParam>(&memory_); + ResetLogs(); + } + + void TearDown() override { delete debug_frame_; } + + MemoryFake memory_; + MockDwarfDebugFrame<TypeParam>* debug_frame_ = nullptr; +}; +TYPED_TEST_CASE_P(DwarfDebugFrameTest); + +// NOTE: All test class variables need to be referenced as this->. + +TYPED_TEST_P(DwarfDebugFrameTest, Init32) { + // CIE 32 information. + this->memory_.SetData32(0x5000, 0xfc); + this->memory_.SetData32(0x5004, 0xffffffff); + this->memory_.SetData8(0x5008, 1); + this->memory_.SetData8(0x5009, '\0'); + + // FDE 32 information. + this->memory_.SetData32(0x5100, 0xfc); + this->memory_.SetData32(0x5104, 0); + this->memory_.SetData32(0x5108, 0x1500); + this->memory_.SetData32(0x510c, 0x200); + + this->memory_.SetData32(0x5200, 0xfc); + this->memory_.SetData32(0x5204, 0); + this->memory_.SetData32(0x5208, 0x2500); + this->memory_.SetData32(0x520c, 0x300); + + // CIE 32 information. + this->memory_.SetData32(0x5300, 0xfc); + this->memory_.SetData32(0x5304, 0xffffffff); + this->memory_.SetData8(0x5308, 1); + this->memory_.SetData8(0x5309, '\0'); + + // FDE 32 information. + this->memory_.SetData32(0x5400, 0xfc); + this->memory_.SetData32(0x5404, 0x300); + this->memory_.SetData32(0x5408, 0x3500); + this->memory_.SetData32(0x540c, 0x400); + + this->memory_.SetData32(0x5500, 0xfc); + this->memory_.SetData32(0x5504, 0x300); + this->memory_.SetData32(0x5508, 0x4500); + this->memory_.SetData32(0x550c, 0x500); + + ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600)); + ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount()); + + typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0); + + this->debug_frame_->TestGetFdeInfo(0, &info); + EXPECT_EQ(0x5100U, info.offset); + EXPECT_EQ(0x1500U, info.start); + EXPECT_EQ(0x1700U, info.end); + + this->debug_frame_->TestGetFdeInfo(1, &info); + EXPECT_EQ(0x5200U, info.offset); + EXPECT_EQ(0x2500U, info.start); + EXPECT_EQ(0x2800U, info.end); + + this->debug_frame_->TestGetFdeInfo(2, &info); + EXPECT_EQ(0x5400U, info.offset); + EXPECT_EQ(0x3500U, info.start); + EXPECT_EQ(0x3900U, info.end); + + this->debug_frame_->TestGetFdeInfo(3, &info); + EXPECT_EQ(0x5500U, info.offset); + EXPECT_EQ(0x4500U, info.start); + EXPECT_EQ(0x4a00U, info.end); +} + +TYPED_TEST_P(DwarfDebugFrameTest, Init32_fde_not_following_cie) { + // CIE 32 information. + this->memory_.SetData32(0x5000, 0xfc); + this->memory_.SetData32(0x5004, 0xffffffff); + this->memory_.SetData8(0x5008, 1); + this->memory_.SetData8(0x5009, '\0'); + + // FDE 32 information. + this->memory_.SetData32(0x5100, 0xfc); + this->memory_.SetData32(0x5104, 0x1000); + this->memory_.SetData32(0x5108, 0x1500); + this->memory_.SetData32(0x510c, 0x200); + + ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error()); +} + +TYPED_TEST_P(DwarfDebugFrameTest, Init64) { + // CIE 64 information. + this->memory_.SetData32(0x5000, 0xffffffff); + this->memory_.SetData64(0x5004, 0xf4); + this->memory_.SetData64(0x500c, 0xffffffffffffffffULL); + this->memory_.SetData8(0x5014, 1); + this->memory_.SetData8(0x5015, '\0'); + + // FDE 64 information. + this->memory_.SetData32(0x5100, 0xffffffff); + this->memory_.SetData64(0x5104, 0xf4); + this->memory_.SetData64(0x510c, 0); + this->memory_.SetData64(0x5114, 0x1500); + this->memory_.SetData64(0x511c, 0x200); + + this->memory_.SetData32(0x5200, 0xffffffff); + this->memory_.SetData64(0x5204, 0xf4); + this->memory_.SetData64(0x520c, 0); + this->memory_.SetData64(0x5214, 0x2500); + this->memory_.SetData64(0x521c, 0x300); + + // CIE 64 information. + this->memory_.SetData32(0x5300, 0xffffffff); + this->memory_.SetData64(0x5304, 0xf4); + this->memory_.SetData64(0x530c, 0xffffffffffffffffULL); + this->memory_.SetData8(0x5314, 1); + this->memory_.SetData8(0x5315, '\0'); + + // FDE 64 information. + this->memory_.SetData32(0x5400, 0xffffffff); + this->memory_.SetData64(0x5404, 0xf4); + this->memory_.SetData64(0x540c, 0x300); + this->memory_.SetData64(0x5414, 0x3500); + this->memory_.SetData64(0x541c, 0x400); + + this->memory_.SetData32(0x5500, 0xffffffff); + this->memory_.SetData64(0x5504, 0xf4); + this->memory_.SetData64(0x550c, 0x300); + this->memory_.SetData64(0x5514, 0x4500); + this->memory_.SetData64(0x551c, 0x500); + + ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600)); + ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount()); + + typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0); + + this->debug_frame_->TestGetFdeInfo(0, &info); + EXPECT_EQ(0x5100U, info.offset); + EXPECT_EQ(0x1500U, info.start); + EXPECT_EQ(0x1700U, info.end); + + this->debug_frame_->TestGetFdeInfo(1, &info); + EXPECT_EQ(0x5200U, info.offset); + EXPECT_EQ(0x2500U, info.start); + EXPECT_EQ(0x2800U, info.end); + + this->debug_frame_->TestGetFdeInfo(2, &info); + EXPECT_EQ(0x5400U, info.offset); + EXPECT_EQ(0x3500U, info.start); + EXPECT_EQ(0x3900U, info.end); + + this->debug_frame_->TestGetFdeInfo(3, &info); + EXPECT_EQ(0x5500U, info.offset); + EXPECT_EQ(0x4500U, info.start); + EXPECT_EQ(0x4a00U, info.end); +} + +TYPED_TEST_P(DwarfDebugFrameTest, Init64_fde_not_following_cie) { + // CIE 64 information. + this->memory_.SetData32(0x5000, 0xffffffff); + this->memory_.SetData64(0x5004, 0xf4); + this->memory_.SetData64(0x500c, 0xffffffffffffffffULL); + this->memory_.SetData8(0x5014, 1); + this->memory_.SetData8(0x5015, '\0'); + + // FDE 64 information. + this->memory_.SetData32(0x5100, 0xffffffff); + this->memory_.SetData64(0x5104, 0xf4); + this->memory_.SetData64(0x510c, 0x1000); + this->memory_.SetData64(0x5114, 0x1500); + this->memory_.SetData64(0x511c, 0x200); + + ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error()); +} + +TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) { + // CIE 32 information. + this->memory_.SetData32(0x5000, 0xfc); + this->memory_.SetData32(0x5004, 0xffffffff); + this->memory_.SetData8(0x5008, 1); + // Augment string. + this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'R', 'P', 'L', '\0'}); + // Code alignment factor. + this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x80, 0x00}); + // Data alignment factor. + this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00}); + // Return address register + this->memory_.SetData8(0x5014, 0x84); + // Augmentation length + this->memory_.SetMemory(0x5015, std::vector<uint8_t>{0x84, 0x00}); + // R data. + this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2); + + // FDE 32 information. + this->memory_.SetData32(0x5100, 0xfc); + this->memory_.SetData32(0x5104, 0); + this->memory_.SetData16(0x5108, 0x1500); + this->memory_.SetData16(0x510a, 0x200); + + ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200)); + ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount()); + + typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0); + this->debug_frame_->TestGetFdeInfo(0, &info); + EXPECT_EQ(0x5100U, info.offset); + EXPECT_EQ(0x1500U, info.start); + EXPECT_EQ(0x1700U, info.end); +} + +TYPED_TEST_P(DwarfDebugFrameTest, Init_version4) { + // CIE 32 information. + this->memory_.SetData32(0x5000, 0xfc); + this->memory_.SetData32(0x5004, 0xffffffff); + this->memory_.SetData8(0x5008, 4); + // Augment string. + this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'}); + // Address size. + this->memory_.SetData8(0x500e, 4); + // Segment size. + this->memory_.SetData8(0x500f, 0); + // Code alignment factor. + this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x80, 0x00}); + // Data alignment factor. + this->memory_.SetMemory(0x5012, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00}); + // Return address register + this->memory_.SetMemory(0x5016, std::vector<uint8_t>{0x85, 0x10}); + // Augmentation length + this->memory_.SetMemory(0x5018, std::vector<uint8_t>{0x84, 0x00}); + // L data. + this->memory_.SetData8(0x501a, 0x10); + // P data. + this->memory_.SetData8(0x501b, DW_EH_PE_udata4); + this->memory_.SetData32(0x501c, 0x100); + // R data. + this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2); + + // FDE 32 information. + this->memory_.SetData32(0x5100, 0xfc); + this->memory_.SetData32(0x5104, 0); + this->memory_.SetData16(0x5108, 0x1500); + this->memory_.SetData16(0x510a, 0x200); + + ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200)); + ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount()); + + typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0); + this->debug_frame_->TestGetFdeInfo(0, &info); + EXPECT_EQ(0x5100U, info.offset); + EXPECT_EQ(0x1500U, info.start); + EXPECT_EQ(0x1700U, info.end); +} + +TYPED_TEST_P(DwarfDebugFrameTest, GetFdeOffsetFromPc) { + typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0); + for (size_t i = 0; i < 9; i++) { + info.start = 0x1000 * (i + 1); + info.end = 0x1000 * (i + 2) - 0x10; + info.offset = 0x5000 + i * 0x20; + this->debug_frame_->TestPushFdeInfo(info); + } + + this->debug_frame_->TestSetFdeCount(0); + uint64_t fde_offset; + ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset)); + ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error()); + + this->debug_frame_->TestSetFdeCount(9); + ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x100, &fde_offset)); + ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error()); + // Odd number of elements. + for (size_t i = 0; i < 9; i++) { + TypeParam pc = 0x1000 * (i + 1); + ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " + << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset)) + << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset)) + << "Failed at index " << i; + ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error()); + } + + // Even number of elements. + this->debug_frame_->TestSetFdeCount(10); + info.start = 0xa000; + info.end = 0xaff0; + info.offset = 0x5120; + this->debug_frame_->TestPushFdeInfo(info); + + for (size_t i = 0; i < 10; i++) { + TypeParam pc = 0x1000 * (i + 1); + ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " + << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset)) + << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset)) + << "Failed at index " << i; + ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error()); + } +} + +TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde32) { + this->debug_frame_->TestSetOffset(0x4000); + + // CIE 32 information. + this->memory_.SetData32(0xf000, 0x100); + this->memory_.SetData32(0xf004, 0xffffffff); + this->memory_.SetData8(0xf008, 0x1); + this->memory_.SetData8(0xf009, '\0'); + this->memory_.SetData8(0xf00a, 4); + this->memory_.SetData8(0xf00b, 8); + this->memory_.SetData8(0xf00c, 0x20); + + // FDE 32 information. + this->memory_.SetData32(0x14000, 0x20); + this->memory_.SetData32(0x14004, 0xb000); + this->memory_.SetData32(0x14008, 0x9000); + this->memory_.SetData32(0x1400c, 0x100); + + const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x14000); + ASSERT_TRUE(fde != nullptr); + EXPECT_EQ(0x14010U, fde->cfa_instructions_offset); + EXPECT_EQ(0x14024U, fde->cfa_instructions_end); + EXPECT_EQ(0x9000U, fde->pc_start); + EXPECT_EQ(0x9100U, fde->pc_end); + EXPECT_EQ(0xf000U, fde->cie_offset); + EXPECT_EQ(0U, fde->lsda_address); + + ASSERT_TRUE(fde->cie != nullptr); + EXPECT_EQ(1U, fde->cie->version); + EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding); + EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding); + EXPECT_EQ(0U, fde->cie->segment_size); + EXPECT_EQ(1U, fde->cie->augmentation_string.size()); + EXPECT_EQ('\0', fde->cie->augmentation_string[0]); + EXPECT_EQ(0U, fde->cie->personality_handler); + EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset); + EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end); + EXPECT_EQ(4U, fde->cie->code_alignment_factor); + EXPECT_EQ(8, fde->cie->data_alignment_factor); + EXPECT_EQ(0x20U, fde->cie->return_address_register); +} + +TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde64) { + this->debug_frame_->TestSetOffset(0x2000); + + // CIE 64 information. + this->memory_.SetData32(0x6000, 0xffffffff); + this->memory_.SetData64(0x6004, 0x100); + this->memory_.SetData64(0x600c, 0xffffffffffffffffULL); + this->memory_.SetData8(0x6014, 0x1); + this->memory_.SetData8(0x6015, '\0'); + this->memory_.SetData8(0x6016, 4); + this->memory_.SetData8(0x6017, 8); + this->memory_.SetData8(0x6018, 0x20); + + // FDE 64 information. + this->memory_.SetData32(0x8000, 0xffffffff); + this->memory_.SetData64(0x8004, 0x200); + this->memory_.SetData64(0x800c, 0x4000); + this->memory_.SetData64(0x8014, 0x5000); + this->memory_.SetData64(0x801c, 0x300); + + const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x8000); + ASSERT_TRUE(fde != nullptr); + EXPECT_EQ(0x8024U, fde->cfa_instructions_offset); + EXPECT_EQ(0x820cU, fde->cfa_instructions_end); + EXPECT_EQ(0x5000U, fde->pc_start); + EXPECT_EQ(0x5300U, fde->pc_end); + EXPECT_EQ(0x6000U, fde->cie_offset); + EXPECT_EQ(0U, fde->lsda_address); + + ASSERT_TRUE(fde->cie != nullptr); + EXPECT_EQ(1U, fde->cie->version); + EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding); + EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding); + EXPECT_EQ(0U, fde->cie->segment_size); + EXPECT_EQ(1U, fde->cie->augmentation_string.size()); + EXPECT_EQ('\0', fde->cie->augmentation_string[0]); + EXPECT_EQ(0U, fde->cie->personality_handler); + EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset); + EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end); + EXPECT_EQ(4U, fde->cie->code_alignment_factor); + EXPECT_EQ(8, fde->cie->data_alignment_factor); + EXPECT_EQ(0x20U, fde->cie->return_address_register); +} + +REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie, Init64, + Init64_fde_not_following_cie, Init_version1, Init_version4, + GetFdeOffsetFromPc, GetCieFde32, GetCieFde64); + +typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes; +INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes); + +} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp new file mode 100644 index 0000000..e9501e3 --- /dev/null +++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -0,0 +1,414 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "DwarfEhFrame.h" +#include "DwarfEncoding.h" +#include "DwarfError.h" + +#include "LogFake.h" +#include "MemoryFake.h" +#include "RegsFake.h" + +namespace unwindstack { + +template <typename TypeParam> +class MockDwarfEhFrame : public DwarfEhFrame<TypeParam> { + public: + MockDwarfEhFrame(Memory* memory) : DwarfEhFrame<TypeParam>(memory) {} + ~MockDwarfEhFrame() = default; + + void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; } + void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; } + void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; } + void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; } + void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; } + void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; } + + void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; } + void TestSetFdeInfo(uint64_t index, const typename DwarfEhFrame<TypeParam>::FdeInfo& info) { + this->fde_info_[index] = info; + } + + uint8_t TestGetVersion() { return this->version_; } + uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; } + uint64_t TestGetPtrOffset() { return this->ptr_offset_; } + uint8_t TestGetTableEncoding() { return this->table_encoding_; } + uint64_t TestGetTableEntrySize() { return this->table_entry_size_; } + uint64_t TestGetFdeCount() { return this->fde_count_; } + uint64_t TestGetEntriesOffset() { return this->entries_offset_; } + uint64_t TestGetEntriesEnd() { return this->entries_end_; } + uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; } + uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; } +}; + +template <typename TypeParam> +class DwarfEhFrameTest : public ::testing::Test { + protected: + void SetUp() override { + memory_.Clear(); + eh_frame_ = new MockDwarfEhFrame<TypeParam>(&memory_); + ResetLogs(); + } + + void TearDown() override { delete eh_frame_; } + + MemoryFake memory_; + MockDwarfEhFrame<TypeParam>* eh_frame_ = nullptr; +}; +TYPED_TEST_CASE_P(DwarfEhFrameTest); + +// NOTE: All test class variables need to be referenced as this->. + +TYPED_TEST_P(DwarfEhFrameTest, Init) { + this->memory_.SetMemory( + 0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4}); + this->memory_.SetData16(0x1004, 0x500); + this->memory_.SetData32(0x1006, 126); + + ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100)); + EXPECT_EQ(1U, this->eh_frame_->TestGetVersion()); + EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding()); + EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding()); + EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize()); + EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount()); + EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset()); + EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset()); + EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd()); + EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset()); + EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset()); + + // Verify an unexpected version will cause a fail. + this->memory_.SetData8(0x1000, 0); + ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100)); + ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error()); + this->memory_.SetData8(0x1000, 2); + ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100)); + ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error()); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_expect_cache_fail) { + this->eh_frame_->TestSetTableEntrySize(0x10); + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4); + ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr); + ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error()); + ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr); + ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error()); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_pcrel) { + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4); + this->eh_frame_->TestSetEntriesOffset(0x1000); + this->eh_frame_->TestSetEntriesDataOffset(0x3000); + this->eh_frame_->TestSetTableEntrySize(0x10); + + this->memory_.SetData32(0x1040, 0x340); + this->memory_.SetData32(0x1044, 0x500); + + auto info = this->eh_frame_->GetFdeInfoFromIndex(2); + ASSERT_TRUE(info != nullptr); + EXPECT_EQ(0x1380U, info->pc); + EXPECT_EQ(0x1540U, info->offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_datarel) { + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4); + this->eh_frame_->TestSetEntriesOffset(0x1000); + this->eh_frame_->TestSetEntriesDataOffset(0x3000); + this->eh_frame_->TestSetTableEntrySize(0x10); + + this->memory_.SetData32(0x1040, 0x340); + this->memory_.SetData32(0x1044, 0x500); + + auto info = this->eh_frame_->GetFdeInfoFromIndex(2); + ASSERT_TRUE(info != nullptr); + EXPECT_EQ(0x3340U, info->pc); + EXPECT_EQ(0x3500U, info->offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_cached) { + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4); + this->eh_frame_->TestSetEntriesOffset(0x1000); + this->eh_frame_->TestSetTableEntrySize(0x10); + + this->memory_.SetData32(0x1040, 0x340); + this->memory_.SetData32(0x1044, 0x500); + + auto info = this->eh_frame_->GetFdeInfoFromIndex(2); + ASSERT_TRUE(info != nullptr); + EXPECT_EQ(0x340U, info->pc); + EXPECT_EQ(0x500U, info->offset); + + // Clear the memory so that this will fail if it doesn't read cached data. + this->memory_.Clear(); + + info = this->eh_frame_->GetFdeInfoFromIndex(2); + ASSERT_TRUE(info != nullptr); + EXPECT_EQ(0x340U, info->pc); + EXPECT_EQ(0x500U, info->offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetBinary_verify) { + this->eh_frame_->TestSetTableEntrySize(0x10); + this->eh_frame_->TestSetFdeCount(10); + + typename DwarfEhFrame<TypeParam>::FdeInfo info; + for (size_t i = 0; i < 10; i++) { + info.pc = 0x1000 * (i + 1); + info.offset = 0x5000 + i * 0x20; + this->eh_frame_->TestSetFdeInfo(i, info); + } + + uint64_t fde_offset; + EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10)); + // Not an error, just not found. + ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error()); + // Even number of elements. + for (size_t i = 0; i < 10; i++) { + TypeParam pc = 0x1000 * (i + 1); + EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10)) << "Failed at index " + << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10)) + << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + } + // Odd number of elements. + for (size_t i = 0; i < 9; i++) { + TypeParam pc = 0x1000 * (i + 1); + EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9)) << "Failed at index " + << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9)) + << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + } +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential) { + this->eh_frame_->TestSetFdeCount(10); + this->eh_frame_->TestSetEntriesDataOffset(0x100); + this->eh_frame_->TestSetEntriesEnd(0x2000); + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4); + + this->memory_.SetData32(0x1040, 0x340); + this->memory_.SetData32(0x1044, 0x500); + + this->memory_.SetData32(0x1048, 0x440); + this->memory_.SetData32(0x104c, 0x600); + + // Verify that if entries is zero, that it fails. + uint64_t fde_offset; + ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset)); + this->eh_frame_->TestSetCurEntriesOffset(0x1040); + + ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset)); + EXPECT_EQ(0x500U, fde_offset); + + ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset)); + EXPECT_EQ(0x600U, fde_offset); + + // Expect that the data is cached so no more memory reads will occur. + this->memory_.Clear(); + ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset)); + EXPECT_EQ(0x600U, fde_offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_last_element) { + this->eh_frame_->TestSetFdeCount(2); + this->eh_frame_->TestSetEntriesDataOffset(0x100); + this->eh_frame_->TestSetEntriesEnd(0x2000); + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4); + this->eh_frame_->TestSetCurEntriesOffset(0x1040); + + this->memory_.SetData32(0x1040, 0x340); + this->memory_.SetData32(0x1044, 0x500); + + this->memory_.SetData32(0x1048, 0x440); + this->memory_.SetData32(0x104c, 0x600); + + uint64_t fde_offset; + ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset)); + EXPECT_EQ(0x600U, fde_offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_end_check) { + this->eh_frame_->TestSetFdeCount(2); + this->eh_frame_->TestSetEntriesDataOffset(0x100); + this->eh_frame_->TestSetEntriesEnd(0x1048); + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4); + + this->memory_.SetData32(0x1040, 0x340); + this->memory_.SetData32(0x1044, 0x500); + + this->memory_.SetData32(0x1048, 0x440); + this->memory_.SetData32(0x104c, 0x600); + + uint64_t fde_offset; + ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset)); + ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error()); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_fail_fde_count) { + this->eh_frame_->TestSetFdeCount(0); + + uint64_t fde_offset; + ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset)); + ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error()); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_binary_search) { + this->eh_frame_->TestSetTableEntrySize(16); + this->eh_frame_->TestSetFdeCount(10); + + typename DwarfEhFrame<TypeParam>::FdeInfo info; + info.pc = 0x550; + info.offset = 0x10500; + this->eh_frame_->TestSetFdeInfo(5, info); + info.pc = 0x750; + info.offset = 0x10700; + this->eh_frame_->TestSetFdeInfo(7, info); + info.pc = 0x850; + info.offset = 0x10800; + this->eh_frame_->TestSetFdeInfo(8, info); + + uint64_t fde_offset; + ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x800, &fde_offset)); + EXPECT_EQ(0x10700U, fde_offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_sequential_search) { + this->eh_frame_->TestSetFdeCount(10); + this->eh_frame_->TestSetTableEntrySize(0); + + typename DwarfEhFrame<TypeParam>::FdeInfo info; + info.pc = 0x50; + info.offset = 0x10000; + this->eh_frame_->TestSetFdeInfo(0, info); + info.pc = 0x150; + info.offset = 0x10100; + this->eh_frame_->TestSetFdeInfo(1, info); + info.pc = 0x250; + info.offset = 0x10200; + this->eh_frame_->TestSetFdeInfo(2, info); + + uint64_t fde_offset; + ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset)); + EXPECT_EQ(0x10100U, fde_offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) { + // CIE 32 information. + this->memory_.SetData32(0xf000, 0x100); + this->memory_.SetData32(0xf004, 0); + this->memory_.SetData8(0xf008, 0x1); + this->memory_.SetData8(0xf009, '\0'); + this->memory_.SetData8(0xf00a, 4); + this->memory_.SetData8(0xf00b, 8); + this->memory_.SetData8(0xf00c, 0x20); + + // FDE 32 information. + this->memory_.SetData32(0x14000, 0x20); + this->memory_.SetData32(0x14004, 0x5004); + this->memory_.SetData32(0x14008, 0x9000); + this->memory_.SetData32(0x1400c, 0x100); + + const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x14000); + ASSERT_TRUE(fde != nullptr); + EXPECT_EQ(0x14010U, fde->cfa_instructions_offset); + EXPECT_EQ(0x14024U, fde->cfa_instructions_end); + EXPECT_EQ(0x1d00cU, fde->pc_start); + EXPECT_EQ(0x1d10cU, fde->pc_end); + EXPECT_EQ(0xf000U, fde->cie_offset); + EXPECT_EQ(0U, fde->lsda_address); + + ASSERT_TRUE(fde->cie != nullptr); + EXPECT_EQ(1U, fde->cie->version); + EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding); + EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding); + EXPECT_EQ(0U, fde->cie->segment_size); + EXPECT_EQ(1U, fde->cie->augmentation_string.size()); + EXPECT_EQ('\0', fde->cie->augmentation_string[0]); + EXPECT_EQ(0U, fde->cie->personality_handler); + EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset); + EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end); + EXPECT_EQ(4U, fde->cie->code_alignment_factor); + EXPECT_EQ(8, fde->cie->data_alignment_factor); + EXPECT_EQ(0x20U, fde->cie->return_address_register); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) { + // CIE 64 information. + this->memory_.SetData32(0x6000, 0xffffffff); + this->memory_.SetData64(0x6004, 0x100); + this->memory_.SetData64(0x600c, 0); + this->memory_.SetData8(0x6014, 0x1); + this->memory_.SetData8(0x6015, '\0'); + this->memory_.SetData8(0x6016, 4); + this->memory_.SetData8(0x6017, 8); + this->memory_.SetData8(0x6018, 0x20); + + // FDE 64 information. + this->memory_.SetData32(0x8000, 0xffffffff); + this->memory_.SetData64(0x8004, 0x200); + this->memory_.SetData64(0x800c, 0x200c); + this->memory_.SetData64(0x8014, 0x5000); + this->memory_.SetData64(0x801c, 0x300); + + const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x8000); + ASSERT_TRUE(fde != nullptr); + EXPECT_EQ(0x8024U, fde->cfa_instructions_offset); + EXPECT_EQ(0x820cU, fde->cfa_instructions_end); + EXPECT_EQ(0xd01cU, fde->pc_start); + EXPECT_EQ(0xd31cU, fde->pc_end); + EXPECT_EQ(0x6000U, fde->cie_offset); + EXPECT_EQ(0U, fde->lsda_address); + + ASSERT_TRUE(fde->cie != nullptr); + EXPECT_EQ(1U, fde->cie->version); + EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding); + EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding); + EXPECT_EQ(0U, fde->cie->segment_size); + EXPECT_EQ(1U, fde->cie->augmentation_string.size()); + EXPECT_EQ('\0', fde->cie->augmentation_string[0]); + EXPECT_EQ(0U, fde->cie->personality_handler); + EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset); + EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end); + EXPECT_EQ(4U, fde->cie->code_alignment_factor); + EXPECT_EQ(8, fde->cie->data_alignment_factor); + EXPECT_EQ(0x20U, fde->cie->return_address_register); +} + +REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init, GetFdeInfoFromIndex_expect_cache_fail, + GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel, + GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify, + GetFdeOffsetSequential, GetFdeOffsetSequential_last_element, + GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count, + GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search, + GetCieFde32, GetCieFde64); + +typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes; +INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes); + +} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp index 4877f36..08fe7cf 100644 --- a/libunwindstack/tests/DwarfMemoryTest.cpp +++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -21,10 +21,12 @@ #include <gtest/gtest.h> -#include "DwarfMemory.h" +#include <unwindstack/DwarfMemory.h> #include "MemoryFake.h" +namespace unwindstack { + class DwarfMemoryTest : public ::testing::Test { protected: void SetUp() override { @@ -237,9 +239,13 @@ ASSERT_EQ(0U, value); } -TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) { ReadEncodedValue_omit<uint32_t>(); } +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_omit_uint64_t) { + ReadEncodedValue_omit<uint64_t>(); +} TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint32_t) { uint64_t value = 100; @@ -302,9 +308,13 @@ ASSERT_EQ(0xffffffffffffe100ULL, value); } -TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) { ReadEncodedValue_leb128<uint32_t>(); } +TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) { + ReadEncodedValue_leb128<uint32_t>(); +} -TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) { ReadEncodedValue_leb128<uint64_t>(); } +TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) { + ReadEncodedValue_leb128<uint64_t>(); +} template <typename AddressType> void DwarfMemoryTest::ReadEncodedValue_data1() { @@ -319,9 +329,13 @@ ASSERT_EQ(0xffffffffffffffe0ULL, value); } -TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) { ReadEncodedValue_data1<uint32_t>(); } +TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) { + ReadEncodedValue_data1<uint32_t>(); +} -TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) { ReadEncodedValue_data1<uint64_t>(); } +TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) { + ReadEncodedValue_data1<uint64_t>(); +} template <typename AddressType> void DwarfMemoryTest::ReadEncodedValue_data2() { @@ -336,9 +350,13 @@ ASSERT_EQ(0xffffffffffffe000ULL, value); } -TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) { ReadEncodedValue_data2<uint32_t>(); } +TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) { + ReadEncodedValue_data2<uint32_t>(); +} -TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) { ReadEncodedValue_data2<uint64_t>(); } +TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) { + ReadEncodedValue_data2<uint64_t>(); +} template <typename AddressType> void DwarfMemoryTest::ReadEncodedValue_data4() { @@ -353,9 +371,13 @@ ASSERT_EQ(0xffffffffe0000000ULL, value); } -TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) { ReadEncodedValue_data4<uint32_t>(); } +TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) { + ReadEncodedValue_data4<uint32_t>(); +} -TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) { ReadEncodedValue_data4<uint64_t>(); } +TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) { + ReadEncodedValue_data4<uint64_t>(); +} template <typename AddressType> void DwarfMemoryTest::ReadEncodedValue_data8() { @@ -370,9 +392,13 @@ ASSERT_EQ(0xe000000000000000ULL, value); } -TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) { ReadEncodedValue_data8<uint32_t>(); } +TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) { + ReadEncodedValue_data8<uint32_t>(); +} -TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) { ReadEncodedValue_data8<uint64_t>(); } +TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) { + ReadEncodedValue_data8<uint64_t>(); +} template <typename AddressType> void DwarfMemoryTest::ReadEncodedValue_non_zero_adjust() { @@ -470,3 +496,5 @@ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value)); ASSERT_EQ(0x14234U, value); } + +} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp index d18aad0..234d1c9 100644 --- a/libunwindstack/tests/DwarfOpLogTest.cpp +++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -21,14 +21,17 @@ #include <gtest/gtest.h> +#include <unwindstack/DwarfMemory.h> +#include <unwindstack/Log.h> +#include <unwindstack/Regs.h> + #include "DwarfError.h" -#include "DwarfMemory.h" #include "DwarfOp.h" -#include "Log.h" -#include "Regs.h" #include "MemoryFake.h" +namespace unwindstack { + template <typename TypeParam> class DwarfOpLogTest : public ::testing::Test { protected: @@ -66,3 +69,5 @@ typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes; INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes); + +} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp index 20c488a..47a40cf 100644 --- a/libunwindstack/tests/DwarfOpTest.cpp +++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -21,14 +21,17 @@ #include <gtest/gtest.h> +#include <unwindstack/DwarfMemory.h> +#include <unwindstack/Log.h> + #include "DwarfError.h" -#include "DwarfMemory.h" #include "DwarfOp.h" -#include "Log.h" #include "MemoryFake.h" #include "RegsFake.h" +namespace unwindstack { + template <typename TypeParam> class DwarfOpTest : public ::testing::Test { protected: @@ -1579,3 +1582,5 @@ typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes; INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes); + +} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp index 71b114a..b871539 100644 --- a/libunwindstack/tests/DwarfSectionImplTest.cpp +++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -19,12 +19,17 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> -#include "DwarfSection.h" +#include <unwindstack/DwarfSection.h> + +#include "DwarfEncoding.h" +#include "DwarfError.h" #include "LogFake.h" #include "MemoryFake.h" #include "RegsFake.h" +namespace unwindstack { + template <typename TypeParam> class MockDwarfSectionImpl : public DwarfSectionImpl<TypeParam> { public: @@ -830,3 +835,5 @@ typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes; INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes); + +} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp index 20f0e2a..fc67063 100644 --- a/libunwindstack/tests/DwarfSectionTest.cpp +++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -19,10 +19,12 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> -#include "DwarfSection.h" +#include <unwindstack/DwarfSection.h> #include "MemoryFake.h" +namespace unwindstack { + class MockDwarfSection : public DwarfSection { public: MockDwarfSection(Memory* memory) : DwarfSection(memory) {} @@ -158,3 +160,5 @@ ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process)); } + +} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp index 67c9a6b..c7ef4a1 100644 --- a/libunwindstack/tests/ElfInterfaceArmTest.cpp +++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -20,12 +20,15 @@ #include <vector> +#include <unwindstack/Regs.h> + #include "ElfInterfaceArm.h" #include "Machine.h" -#include "Regs.h" #include "MemoryFake.h" +namespace unwindstack { + class ElfInterfaceArmTest : public ::testing::Test { protected: void SetUp() override { @@ -370,3 +373,5 @@ ASSERT_EQ(0x10U, regs.pc()); ASSERT_EQ(0x10U, regs[ARM_REG_PC]); } + +} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp index c31903d..acb7320 100644 --- a/libunwindstack/tests/ElfInterfaceTest.cpp +++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -20,7 +20,9 @@ #include <gtest/gtest.h> -#include "ElfInterface.h" +#include <unwindstack/ElfInterface.h> + +#include "DwarfEncoding.h" #include "ElfInterfaceArm.h" #include "MemoryFake.h" @@ -33,6 +35,8 @@ #define EM_AARCH64 183 #endif +namespace unwindstack { + class ElfInterfaceTest : public ::testing::Test { protected: void SetUp() override { @@ -67,9 +71,49 @@ template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType> void SonameSize(); + template <typename ElfType> + void InitHeadersEhFrameTest(); + + template <typename ElfType> + void InitHeadersDebugFrame(); + + template <typename ElfType> + void InitHeadersEhFrameFail(); + + template <typename ElfType> + void InitHeadersDebugFrameFail(); + + template <typename Ehdr, typename Shdr, typename ElfInterfaceType> + void InitSectionHeadersMalformed(); + + template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType> + void InitSectionHeaders(uint64_t entry_size); + + template <typename Ehdr, typename Shdr, typename ElfInterfaceType> + void InitSectionHeadersOffsets(); + + template <typename Sym> + void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset, + uint64_t sym_offset, const char* name); + MemoryFake memory_; }; +template <typename Sym> +void ElfInterfaceTest::InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset, + uint64_t sym_offset, const char* name) { + Sym sym; + memset(&sym, 0, sizeof(sym)); + sym.st_info = STT_FUNC; + sym.st_value = value; + sym.st_size = size; + sym.st_name = name_offset; + sym.st_shndx = SHN_COMMON; + + memory_.SetMemory(offset, &sym, sizeof(sym)); + memory_.SetMemory(sym_offset + name_offset, name, strlen(name) + 1); +} + template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType> void ElfInterfaceTest::SinglePtLoad() { std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_)); @@ -571,3 +615,315 @@ TEST_F(ElfInterfaceTest, elf64_soname_size) { SonameSize<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>(); } + +class MockElfInterface32 : public ElfInterface32 { + public: + MockElfInterface32(Memory* memory) : ElfInterface32(memory) {} + virtual ~MockElfInterface32() = default; + + void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; } + void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; } + + void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; } + void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; } +}; + +class MockElfInterface64 : public ElfInterface64 { + public: + MockElfInterface64(Memory* memory) : ElfInterface64(memory) {} + virtual ~MockElfInterface64() = default; + + void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; } + void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; } + + void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; } + void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; } +}; + +template <typename ElfType> +void ElfInterfaceTest::InitHeadersEhFrameTest() { + ElfType elf(&memory_); + + elf.TestSetEhFrameOffset(0x10000); + elf.TestSetEhFrameSize(0); + elf.TestSetDebugFrameOffset(0); + elf.TestSetDebugFrameSize(0); + + memory_.SetMemory(0x10000, + std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata2, DW_EH_PE_udata2}); + memory_.SetData32(0x10004, 0x500); + memory_.SetData32(0x10008, 250); + + elf.InitHeaders(); + + EXPECT_FALSE(elf.eh_frame() == nullptr); + EXPECT_TRUE(elf.debug_frame() == nullptr); +} + +TEST_F(ElfInterfaceTest, init_headers_eh_frame32) { + InitHeadersEhFrameTest<MockElfInterface32>(); +} + +TEST_F(ElfInterfaceTest, init_headers_eh_frame64) { + InitHeadersEhFrameTest<MockElfInterface64>(); +} + +template <typename ElfType> +void ElfInterfaceTest::InitHeadersDebugFrame() { + ElfType elf(&memory_); + + elf.TestSetEhFrameOffset(0); + elf.TestSetEhFrameSize(0); + elf.TestSetDebugFrameOffset(0x5000); + elf.TestSetDebugFrameSize(0x200); + + memory_.SetData32(0x5000, 0xfc); + memory_.SetData32(0x5004, 0xffffffff); + memory_.SetData8(0x5008, 1); + memory_.SetData8(0x5009, '\0'); + + memory_.SetData32(0x5100, 0xfc); + memory_.SetData32(0x5104, 0); + memory_.SetData32(0x5108, 0x1500); + memory_.SetData32(0x510c, 0x200); + + elf.InitHeaders(); + + EXPECT_TRUE(elf.eh_frame() == nullptr); + EXPECT_FALSE(elf.debug_frame() == nullptr); +} + +TEST_F(ElfInterfaceTest, init_headers_debug_frame32) { + InitHeadersDebugFrame<MockElfInterface32>(); +} + +TEST_F(ElfInterfaceTest, init_headers_debug_frame64) { + InitHeadersDebugFrame<MockElfInterface64>(); +} + +template <typename ElfType> +void ElfInterfaceTest::InitHeadersEhFrameFail() { + ElfType elf(&memory_); + + elf.TestSetEhFrameOffset(0x1000); + elf.TestSetEhFrameSize(0x100); + elf.TestSetDebugFrameOffset(0); + elf.TestSetDebugFrameSize(0); + + elf.InitHeaders(); + + EXPECT_TRUE(elf.eh_frame() == nullptr); + EXPECT_EQ(0U, elf.eh_frame_offset()); + EXPECT_EQ(static_cast<uint64_t>(-1), elf.eh_frame_size()); + EXPECT_TRUE(elf.debug_frame() == nullptr); +} + +TEST_F(ElfInterfaceTest, init_headers_eh_frame32_fail) { + InitHeadersEhFrameFail<MockElfInterface32>(); +} + +TEST_F(ElfInterfaceTest, init_headers_eh_frame64_fail) { + InitHeadersEhFrameFail<MockElfInterface64>(); +} + +template <typename ElfType> +void ElfInterfaceTest::InitHeadersDebugFrameFail() { + ElfType elf(&memory_); + + elf.TestSetEhFrameOffset(0); + elf.TestSetEhFrameSize(0); + elf.TestSetDebugFrameOffset(0x1000); + elf.TestSetDebugFrameSize(0x100); + + elf.InitHeaders(); + + EXPECT_TRUE(elf.eh_frame() == nullptr); + EXPECT_TRUE(elf.debug_frame() == nullptr); + EXPECT_EQ(0U, elf.debug_frame_offset()); + EXPECT_EQ(static_cast<uint64_t>(-1), elf.debug_frame_size()); +} + +TEST_F(ElfInterfaceTest, init_headers_debug_frame32_fail) { + InitHeadersDebugFrameFail<MockElfInterface32>(); +} + +TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) { + InitHeadersDebugFrameFail<MockElfInterface64>(); +} + +template <typename Ehdr, typename Shdr, typename ElfInterfaceType> +void ElfInterfaceTest::InitSectionHeadersMalformed() { + std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_)); + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_shoff = 0x1000; + ehdr.e_shnum = 10; + ehdr.e_shentsize = sizeof(Shdr); + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + ASSERT_TRUE(elf->Init()); +} + +TEST_F(ElfInterfaceTest, init_section_headers_malformed32) { + InitSectionHeadersMalformed<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(); +} + +TEST_F(ElfInterfaceTest, init_section_headers_malformed64) { + InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(); +} + +template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType> +void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) { + std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_)); + + uint64_t offset = 0x1000; + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_shoff = offset; + ehdr.e_shnum = 10; + ehdr.e_shentsize = entry_size; + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + offset += ehdr.e_shentsize; + + Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_SYMTAB; + shdr.sh_link = 4; + shdr.sh_addr = 0x5000; + shdr.sh_offset = 0x5000; + shdr.sh_entsize = sizeof(Sym); + shdr.sh_size = shdr.sh_entsize * 10; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_DYNSYM; + shdr.sh_link = 4; + shdr.sh_addr = 0x6000; + shdr.sh_offset = 0x6000; + shdr.sh_entsize = sizeof(Sym); + shdr.sh_size = shdr.sh_entsize * 10; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_name = 0xa000; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + // The string data for the entries. + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_STRTAB; + shdr.sh_name = 0x20000; + shdr.sh_offset = 0xf000; + shdr.sh_size = 0x1000; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + InitSym<Sym>(0x5000, 0x90000, 0x1000, 0x100, 0xf000, "function_one"); + InitSym<Sym>(0x6000, 0xd0000, 0x1000, 0x300, 0xf000, "function_two"); + + ASSERT_TRUE(elf->Init()); + EXPECT_EQ(0U, elf->debug_frame_offset()); + EXPECT_EQ(0U, elf->debug_frame_size()); + EXPECT_EQ(0U, elf->gnu_debugdata_offset()); + EXPECT_EQ(0U, elf->gnu_debugdata_size()); + + // Look in the first symbol table. + std::string name; + uint64_t name_offset; + ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset)); + EXPECT_EQ("function_one", name); + EXPECT_EQ(16U, name_offset); + ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset)); + EXPECT_EQ("function_two", name); + EXPECT_EQ(32U, name_offset); +} + +TEST_F(ElfInterfaceTest, init_section_headers32) { + InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(sizeof(Elf32_Shdr)); +} + +TEST_F(ElfInterfaceTest, init_section_headers64) { + InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(sizeof(Elf64_Shdr)); +} + +TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size32) { + InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(0x100); +} + +TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size64) { + InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(0x100); +} + +template <typename Ehdr, typename Shdr, typename ElfInterfaceType> +void ElfInterfaceTest::InitSectionHeadersOffsets() { + std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_)); + + uint64_t offset = 0x2000; + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_shoff = offset; + ehdr.e_shnum = 10; + ehdr.e_shentsize = sizeof(Shdr); + ehdr.e_shstrndx = 2; + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + offset += ehdr.e_shentsize; + + Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_link = 2; + shdr.sh_name = 0x200; + shdr.sh_addr = 0x5000; + shdr.sh_offset = 0x5000; + shdr.sh_entsize = 0x100; + shdr.sh_size = 0x800; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + // The string data for section header names. + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_STRTAB; + shdr.sh_name = 0x20000; + shdr.sh_offset = 0xf000; + shdr.sh_size = 0x1000; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_link = 2; + shdr.sh_name = 0x100; + shdr.sh_addr = 0x6000; + shdr.sh_offset = 0x6000; + shdr.sh_entsize = 0x100; + shdr.sh_size = 0x500; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame")); + memory_.SetMemory(0xf200, ".gnu_debugdata", sizeof(".gnu_debugdata")); + + ASSERT_TRUE(elf->Init()); + EXPECT_EQ(0x6000U, elf->debug_frame_offset()); + EXPECT_EQ(0x500U, elf->debug_frame_size()); + EXPECT_EQ(0x5000U, elf->gnu_debugdata_offset()); + EXPECT_EQ(0x800U, elf->gnu_debugdata_size()); +} + +TEST_F(ElfInterfaceTest, init_section_headers_offsets32) { + InitSectionHeadersOffsets<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(); +} + +TEST_F(ElfInterfaceTest, init_section_headers_offsets64) { + InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(); +} + +} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp index 25fec8e..ed1be3b 100644 --- a/libunwindstack/tests/ElfTest.cpp +++ b/libunwindstack/tests/ElfTest.cpp
@@ -15,20 +15,25 @@ */ #include <elf.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> #include <gtest/gtest.h> -#include "Elf.h" +#include <unwindstack/Elf.h> +#include <unwindstack/MapInfo.h> +#include "ElfTestUtils.h" +#include "LogFake.h" #include "MemoryFake.h" #if !defined(PT_ARM_EXIDX) #define PT_ARM_EXIDX 0x70000001 #endif -#if !defined(EM_AARCH64) -#define EM_AARCH64 183 -#endif +namespace unwindstack { class ElfTest : public ::testing::Test { protected: @@ -36,35 +41,16 @@ memory_ = new MemoryFake; } - template <typename Ehdr> - void InitEhdr(Ehdr* ehdr) { - memset(ehdr, 0, sizeof(Ehdr)); - memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG); - ehdr->e_ident[EI_DATA] = ELFDATA2LSB; - ehdr->e_ident[EI_VERSION] = EV_CURRENT; - ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV; - } - - void InitElf32(uint32_t type) { + void InitElf32(uint32_t machine_type) { Elf32_Ehdr ehdr; + TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, machine_type); - InitEhdr<Elf32_Ehdr>(&ehdr); - ehdr.e_ident[EI_CLASS] = ELFCLASS32; - - ehdr.e_type = ET_DYN; - ehdr.e_machine = type; - ehdr.e_version = EV_CURRENT; - ehdr.e_entry = 0; ehdr.e_phoff = 0x100; - ehdr.e_shoff = 0; - ehdr.e_flags = 0; ehdr.e_ehsize = sizeof(ehdr); ehdr.e_phentsize = sizeof(Elf32_Phdr); ehdr.e_phnum = 1; ehdr.e_shentsize = sizeof(Elf32_Shdr); - ehdr.e_shnum = 0; - ehdr.e_shstrndx = 0; - if (type == EM_ARM) { + if (machine_type == EM_ARM) { ehdr.e_flags = 0x5000200; ehdr.e_phnum = 2; } @@ -73,16 +59,13 @@ Elf32_Phdr phdr; memset(&phdr, 0, sizeof(phdr)); phdr.p_type = PT_LOAD; - phdr.p_offset = 0; - phdr.p_vaddr = 0; - phdr.p_paddr = 0; phdr.p_filesz = 0x10000; phdr.p_memsz = 0x10000; phdr.p_flags = PF_R | PF_X; phdr.p_align = 0x1000; memory_->SetMemory(0x100, &phdr, sizeof(phdr)); - if (type == EM_ARM) { + if (machine_type == EM_ARM) { memset(&phdr, 0, sizeof(phdr)); phdr.p_type = PT_ARM_EXIDX; phdr.p_offset = 0x30000; @@ -96,33 +79,21 @@ } } - void InitElf64(uint32_t type) { + void InitElf64(uint32_t machine_type) { Elf64_Ehdr ehdr; + TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, machine_type); - InitEhdr<Elf64_Ehdr>(&ehdr); - ehdr.e_ident[EI_CLASS] = ELFCLASS64; - - ehdr.e_type = ET_DYN; - ehdr.e_machine = type; - ehdr.e_version = EV_CURRENT; - ehdr.e_entry = 0; ehdr.e_phoff = 0x100; - ehdr.e_shoff = 0; ehdr.e_flags = 0x5000200; ehdr.e_ehsize = sizeof(ehdr); ehdr.e_phentsize = sizeof(Elf64_Phdr); ehdr.e_phnum = 1; ehdr.e_shentsize = sizeof(Elf64_Shdr); - ehdr.e_shnum = 0; - ehdr.e_shstrndx = 0; memory_->SetMemory(0, &ehdr, sizeof(ehdr)); Elf64_Phdr phdr; memset(&phdr, 0, sizeof(phdr)); phdr.p_type = PT_LOAD; - phdr.p_offset = 0; - phdr.p_vaddr = 0; - phdr.p_paddr = 0; phdr.p_filesz = 0x10000; phdr.p_memsz = 0x10000; phdr.p_flags = PF_R | PF_X; @@ -161,6 +132,32 @@ ASSERT_FALSE(elf.Step(0, nullptr, nullptr)); } +TEST_F(ElfTest, elf32_invalid_machine) { + Elf elf(memory_); + + InitElf32(EM_PPC); + + ResetLogs(); + ASSERT_FALSE(elf.Init()); + + ASSERT_EQ("", GetFakeLogBuf()); + ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86: e_machine = 20\n\n", + GetFakeLogPrint()); +} + +TEST_F(ElfTest, elf64_invalid_machine) { + Elf elf(memory_); + + InitElf64(EM_PPC64); + + ResetLogs(); + ASSERT_FALSE(elf.Init()); + + ASSERT_EQ("", GetFakeLogBuf()); + ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64: e_machine = 21\n\n", + GetFakeLogPrint()); +} + TEST_F(ElfTest, elf_arm) { Elf elf(memory_); @@ -208,3 +205,95 @@ ASSERT_EQ(ELFCLASS64, elf.class_type()); ASSERT_TRUE(elf.interface() != nullptr); } + +TEST_F(ElfTest, gnu_debugdata_init_fail32) { + TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false, + [&](uint64_t offset, const void* ptr, size_t size) { + memory_->SetMemory(offset, ptr, size); + }); + + Elf elf(memory_); + ASSERT_TRUE(elf.Init()); + ASSERT_TRUE(elf.interface() != nullptr); + ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr); + EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset()); + EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size()); +} + +TEST_F(ElfTest, gnu_debugdata_init_fail64) { + TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false, + [&](uint64_t offset, const void* ptr, size_t size) { + memory_->SetMemory(offset, ptr, size); + }); + + Elf elf(memory_); + ASSERT_TRUE(elf.Init()); + ASSERT_TRUE(elf.interface() != nullptr); + ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr); + EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset()); + EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size()); +} + +TEST_F(ElfTest, gnu_debugdata_init32) { + TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true, + [&](uint64_t offset, const void* ptr, size_t size) { + memory_->SetMemory(offset, ptr, size); + }); + + Elf elf(memory_); + ASSERT_TRUE(elf.Init()); + ASSERT_TRUE(elf.interface() != nullptr); + ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr); + EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset()); + EXPECT_EQ(0x8cU, elf.interface()->gnu_debugdata_size()); + + elf.InitGnuDebugdata(); + ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr); +} + +TEST_F(ElfTest, gnu_debugdata_init64) { + TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true, + [&](uint64_t offset, const void* ptr, size_t size) { + memory_->SetMemory(offset, ptr, size); + }); + + Elf elf(memory_); + ASSERT_TRUE(elf.Init()); + ASSERT_TRUE(elf.interface() != nullptr); + ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr); + EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset()); + EXPECT_EQ(0x90U, elf.interface()->gnu_debugdata_size()); + + elf.InitGnuDebugdata(); + ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr); +} + +class MockElf : public Elf { + public: + MockElf(Memory* memory) : Elf(memory) {} + virtual ~MockElf() = default; + + void set_valid(bool valid) { valid_ = valid; } + void set_elf_interface(ElfInterface* interface) { interface_.reset(interface); } +}; + +TEST_F(ElfTest, rel_pc) { + MockElf elf(memory_); + + ElfInterface* interface = new ElfInterface32(memory_); + elf.set_elf_interface(interface); + + elf.set_valid(true); + interface->set_load_bias(0); + MapInfo map_info{.start = 0x1000, .end = 0x2000}; + + ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info)); + + interface->set_load_bias(0x3000); + ASSERT_EQ(0x3101U, elf.GetRelPc(0x1101, &map_info)); + + elf.set_valid(false); + ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info)); +} + +} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.cpp b/libunwindstack/tests/ElfTestUtils.cpp new file mode 100644 index 0000000..069386b --- /dev/null +++ b/libunwindstack/tests/ElfTestUtils.cpp
@@ -0,0 +1,142 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <elf.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include <gtest/gtest.h> + +#include "ElfTestUtils.h" + +namespace unwindstack { + +template <typename Ehdr> +void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type) { + memset(ehdr, 0, sizeof(Ehdr)); + memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG); + ehdr->e_ident[EI_DATA] = ELFDATA2LSB; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV; + ehdr->e_ident[EI_CLASS] = elf_class; + ehdr->e_type = ET_DYN; + ehdr->e_machine = machine_type; + ehdr->e_version = EV_CURRENT; + ehdr->e_ehsize = sizeof(Ehdr); +} + +static std::string GetTestFileDirectory() { + std::string exec(testing::internal::GetArgvs()[0]); + auto const value = exec.find_last_of('/'); + if (value == std::string::npos) { + return "tests/files/"; + } + return exec.substr(0, value + 1) + "tests/files/"; +} + +template <typename Ehdr, typename Shdr> +void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine, bool init_gnu_debugdata, + TestCopyFuncType copy_func) { + Ehdr ehdr; + + TestInitEhdr(&ehdr, elf_class, machine); + + uint64_t offset = sizeof(Ehdr); + ehdr.e_shoff = offset; + ehdr.e_shnum = 3; + ehdr.e_shentsize = sizeof(Shdr); + ehdr.e_shstrndx = 2; + copy_func(0, &ehdr, sizeof(ehdr)); + + Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_NULL; + copy_func(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + // Skip this header, it will contain the gnu_debugdata information. + uint64_t gnu_offset = offset; + offset += ehdr.e_shentsize; + + uint64_t symtab_offset = sizeof(ehdr) + ehdr.e_shnum * ehdr.e_shentsize; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_name = 1; + shdr.sh_type = SHT_STRTAB; + shdr.sh_offset = symtab_offset; + shdr.sh_size = 0x100; + copy_func(offset, &shdr, sizeof(shdr)); + + char value = '\0'; + uint64_t symname_offset = symtab_offset; + copy_func(symname_offset, &value, 1); + symname_offset++; + std::string name(".shstrtab"); + copy_func(symname_offset, name.c_str(), name.size() + 1); + symname_offset += name.size() + 1; + name = ".gnu_debugdata"; + copy_func(symname_offset, name.c_str(), name.size() + 1); + + ssize_t bytes = 0x100; + offset = symtab_offset + 0x100; + if (init_gnu_debugdata) { + // Read in the compressed elf data and copy it in. + name = GetTestFileDirectory(); + if (elf_class == ELFCLASS32) { + name += "elf32.xz"; + } else { + name += "elf64.xz"; + } + int fd = TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY)); + ASSERT_NE(-1, fd) << "Cannot open " + name; + // Assumes the file is less than 1024 bytes. + std::vector<uint8_t> buf(1024); + bytes = TEMP_FAILURE_RETRY(read(fd, buf.data(), buf.size())); + ASSERT_GT(bytes, 0); + // Make sure the file isn't too big. + ASSERT_NE(static_cast<size_t>(bytes), buf.size()) + << "File " + name + " is too big, increase buffer size."; + close(fd); + buf.resize(bytes); + copy_func(offset, buf.data(), buf.size()); + } + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_name = symname_offset - symtab_offset; + shdr.sh_addr = offset; + shdr.sh_offset = offset; + shdr.sh_size = bytes; + copy_func(gnu_offset, &shdr, sizeof(shdr)); +} + +template void TestInitEhdr<Elf32_Ehdr>(Elf32_Ehdr*, uint32_t, uint32_t); +template void TestInitEhdr<Elf64_Ehdr>(Elf64_Ehdr*, uint32_t, uint32_t); + +template void TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(uint32_t, uint32_t, bool, + TestCopyFuncType); +template void TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(uint32_t, uint32_t, bool, + TestCopyFuncType); + +} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h new file mode 100644 index 0000000..6ef00e1 --- /dev/null +++ b/libunwindstack/tests/ElfTestUtils.h
@@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H +#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H + +#include <functional> + +namespace unwindstack { + +typedef std::function<void(uint64_t, const void*, size_t)> TestCopyFuncType; + +template <typename Ehdr> +void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type); + +template <typename Ehdr, typename Shdr> +void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata, + TestCopyFuncType copy_func); + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
diff --git a/libunwindstack/tests/GenGnuDebugdata.cpp b/libunwindstack/tests/GenGnuDebugdata.cpp new file mode 100644 index 0000000..2644582 --- /dev/null +++ b/libunwindstack/tests/GenGnuDebugdata.cpp
@@ -0,0 +1,112 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <elf.h> +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <string> + +#if !defined(EM_AARCH64) +#define EM_AARCH64 183 +#endif + +template <typename Ehdr> +void InitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine) { + memset(ehdr, 0, sizeof(Ehdr)); + memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG); + ehdr->e_ident[EI_DATA] = ELFDATA2LSB; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV; + ehdr->e_ident[EI_CLASS] = elf_class; + ehdr->e_type = ET_DYN; + ehdr->e_machine = machine; + ehdr->e_version = EV_CURRENT; + ehdr->e_ehsize = sizeof(Ehdr); +} + +template <typename Ehdr, typename Shdr> +void GenElf(Ehdr* ehdr, int fd) { + uint64_t offset = sizeof(Ehdr); + ehdr->e_shoff = offset; + ehdr->e_shnum = 3; + ehdr->e_shentsize = sizeof(Shdr); + ehdr->e_shstrndx = 2; + TEMP_FAILURE_RETRY(write(fd, ehdr, sizeof(Ehdr))); + + Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_name = 0; + shdr.sh_type = SHT_NULL; + TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr))); + offset += ehdr->e_shentsize; + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_name = 11; + shdr.sh_addr = 0x5000; + shdr.sh_offset = 0x5000; + shdr.sh_entsize = 0x100; + shdr.sh_size = 0x800; + TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr))); + offset += ehdr->e_shentsize; + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_STRTAB; + shdr.sh_name = 1; + shdr.sh_offset = 0x200; + shdr.sh_size = 24; + TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr))); + + // Write out the name entries information. + lseek(fd, 0x200, SEEK_SET); + std::string name; + TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1)); + name = ".shstrtab"; + TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1)); + name = ".debug_frame"; + TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1)); +} + +int main() { + int elf32_fd = TEMP_FAILURE_RETRY(open("elf32", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644)); + if (elf32_fd == -1) { + printf("Failed to create elf32: %s\n", strerror(errno)); + return 1; + } + + int elf64_fd = TEMP_FAILURE_RETRY(open("elf64", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644)); + if (elf64_fd == -1) { + printf("Failed to create elf64: %s\n", strerror(errno)); + return 1; + } + + Elf32_Ehdr ehdr32; + InitEhdr<Elf32_Ehdr>(&ehdr32, ELFCLASS32, EM_ARM); + GenElf<Elf32_Ehdr, Elf32_Shdr>(&ehdr32, elf32_fd); + close(elf32_fd); + + Elf64_Ehdr ehdr64; + InitEhdr<Elf64_Ehdr>(&ehdr64, ELFCLASS64, EM_AARCH64); + GenElf<Elf64_Ehdr, Elf64_Shdr>(&ehdr64, elf64_fd); + close(elf64_fd); +}
diff --git a/libunwindstack/tests/LogFake.cpp b/libunwindstack/tests/LogFake.cpp index 411594a..537ccaf 100644 --- a/libunwindstack/tests/LogFake.cpp +++ b/libunwindstack/tests/LogFake.cpp
@@ -25,7 +25,6 @@ #include "LogFake.h" // Forward declarations. -class Backtrace; struct EventTagMap; struct AndroidLogEntry; @@ -33,6 +32,8 @@ std::string g_fake_log_print; +namespace unwindstack { + void ResetLogs() { g_fake_log_buf = ""; g_fake_log_print = ""; @@ -46,6 +47,8 @@ return g_fake_log_print; } +} // namespace unwindstack + extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) { g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' '; g_fake_log_buf += tag;
diff --git a/libunwindstack/tests/LogFake.h b/libunwindstack/tests/LogFake.h index 006d393..e1dc50d 100644 --- a/libunwindstack/tests/LogFake.h +++ b/libunwindstack/tests/LogFake.h
@@ -19,8 +19,12 @@ #include <string> +namespace unwindstack { + void ResetLogs(); std::string GetFakeLogBuf(); std::string GetFakeLogPrint(); +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp similarity index 83% rename from libunwindstack/tests/MapInfoTest.cpp rename to libunwindstack/tests/MapInfoCreateMemoryTest.cpp index c846ad7..9e45e78 100644 --- a/libunwindstack/tests/MapInfoTest.cpp +++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -30,11 +30,13 @@ #include <android-base/test_utils.h> #include <gtest/gtest.h> -#include "Elf.h" -#include "MapInfo.h" -#include "Memory.h" +#include <unwindstack/Elf.h> +#include <unwindstack/MapInfo.h> +#include <unwindstack/Memory.h> -class MapInfoTest : public ::testing::Test { +namespace unwindstack { + +class MapInfoCreateMemoryTest : public ::testing::Test { protected: static void SetUpTestCase() { std::vector<uint8_t> buffer(1024); @@ -58,10 +60,10 @@ static TemporaryFile elf_at_100_; }; -TemporaryFile MapInfoTest::elf_; -TemporaryFile MapInfoTest::elf_at_100_; +TemporaryFile MapInfoCreateMemoryTest::elf_; +TemporaryFile MapInfoCreateMemoryTest::elf_at_100_; -TEST_F(MapInfoTest, end_le_start) { +TEST_F(MapInfoCreateMemoryTest, end_le_start) { MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path}; std::unique_ptr<Memory> memory; @@ -75,12 +77,12 @@ // Make sure this test is valid. info.end = 0x101; memory.reset(info.CreateMemory(getpid())); - ASSERT_FALSE(info.CreateMemory(getpid()) == nullptr); + ASSERT_TRUE(memory.get() != nullptr); } // Verify that if the offset is non-zero but there is no elf at the offset, // that the full file is used. -TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_full_file) { +TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) { MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path}; std::unique_ptr<Memory> memory(info.CreateMemory(getpid())); @@ -100,7 +102,7 @@ // 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) { +TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) { MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path}; std::unique_ptr<Memory> memory(info.CreateMemory(getpid())); @@ -119,7 +121,7 @@ } // Verify that device file names will never result in Memory object creation. -TEST_F(MapInfoTest, create_memory_check_device_maps) { +TEST_F(MapInfoCreateMemoryTest, check_device_maps) { // Set up some memory so that a valid local memory object would // be returned if the file mapping fails, but the device check is incorrect. std::vector<uint8_t> buffer(1024); @@ -135,7 +137,7 @@ ASSERT_TRUE(memory.get() == nullptr); } -TEST_F(MapInfoTest, create_memory_local_memory) { +TEST_F(MapInfoCreateMemoryTest, 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++) { @@ -160,7 +162,7 @@ ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1)); } -TEST_F(MapInfoTest, create_memory_remote_memory) { +TEST_F(MapInfoCreateMemoryTest, remote_memory) { std::vector<uint8_t> buffer(1024); memset(buffer.data(), 0xa, buffer.size()); @@ -201,20 +203,7 @@ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); kill(pid, SIGKILL); + ASSERT_EQ(pid, wait(nullptr)); } -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)); -} +} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp new file mode 100644 index 0000000..abfa172 --- /dev/null +++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <elf.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <unistd.h> + +#include <memory> +#include <vector> + +#include <android-base/file.h> +#include <android-base/test_utils.h> +#include <gtest/gtest.h> + +#include <unwindstack/Elf.h> +#include <unwindstack/MapInfo.h> +#include <unwindstack/Memory.h> + +#include "ElfTestUtils.h" + +namespace unwindstack { + +class MapInfoGetElfTest : public ::testing::Test { + protected: + void SetUp() override { + map_ = mmap(nullptr, kMapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(MAP_FAILED, map_); + + uint64_t start = reinterpret_cast<uint64_t>(map_); + info_.reset(new MapInfo{.start = start, .end = start + 1024, .offset = 0, .name = ""}); + } + + void TearDown() override { munmap(map_, kMapSize); } + + const size_t kMapSize = 4096; + + void* map_ = nullptr; + std::unique_ptr<MapInfo> info_; +}; + +TEST_F(MapInfoGetElfTest, invalid) { + // The map is empty, but this should still create an invalid elf object. + std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_FALSE(elf->valid()); +} + +TEST_F(MapInfoGetElfTest, valid32) { + Elf32_Ehdr ehdr; + TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM); + memcpy(map_, &ehdr, sizeof(ehdr)); + + std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_TRUE(elf->valid()); + EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type()); + EXPECT_EQ(ELFCLASS32, elf->class_type()); +} + +TEST_F(MapInfoGetElfTest, valid64) { + Elf64_Ehdr ehdr; + TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64); + memcpy(map_, &ehdr, sizeof(ehdr)); + + std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_TRUE(elf->valid()); + EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type()); + EXPECT_EQ(ELFCLASS64, elf->class_type()); +} + +TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) { + TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>( + ELFCLASS32, EM_ARM, false, [&](uint64_t offset, const void* ptr, size_t size) { + memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size); + }); + + std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_TRUE(elf->valid()); + EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type()); + EXPECT_EQ(ELFCLASS32, elf->class_type()); + EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr); +} + +TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) { + TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>( + ELFCLASS64, EM_AARCH64, false, [&](uint64_t offset, const void* ptr, size_t size) { + memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size); + }); + + std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_TRUE(elf->valid()); + EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type()); + EXPECT_EQ(ELFCLASS64, elf->class_type()); + EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr); +} + +TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) { + TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>( + ELFCLASS32, EM_ARM, true, [&](uint64_t offset, const void* ptr, size_t size) { + memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size); + }); + + std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_TRUE(elf->valid()); + EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type()); + EXPECT_EQ(ELFCLASS32, elf->class_type()); + EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr); +} + +TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) { + TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>( + ELFCLASS64, EM_AARCH64, true, [&](uint64_t offset, const void* ptr, size_t size) { + memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size); + }); + + std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_TRUE(elf->valid()); + EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type()); + EXPECT_EQ(ELFCLASS64, elf->class_type()); + EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr); +} + +} // namespace unwindstack
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp index 7eb9bae..9430cf3 100644 --- a/libunwindstack/tests/MapsTest.cpp +++ b/libunwindstack/tests/MapsTest.cpp
@@ -20,7 +20,9 @@ #include <android-base/test_utils.h> #include <gtest/gtest.h> -#include "Maps.h" +#include <unwindstack/Maps.h> + +namespace unwindstack { TEST(MapsTest, parse_permissions) { BufferMaps maps( @@ -235,3 +237,5 @@ ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags); ASSERT_EQ("/system/lib/fake5.so", info->name); } + +} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryBuffer.cpp b/libunwindstack/tests/MemoryBufferTest.cpp similarity index 96% rename from libunwindstack/tests/MemoryBuffer.cpp rename to libunwindstack/tests/MemoryBufferTest.cpp index af3d6b9..50a8a1b 100644 --- a/libunwindstack/tests/MemoryBuffer.cpp +++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -18,10 +18,12 @@ #include <gtest/gtest.h> -#include "Memory.h" +#include <unwindstack/Memory.h> #include "LogFake.h" +namespace unwindstack { + class MemoryBufferTest : public ::testing::Test { protected: void SetUp() override { @@ -75,3 +77,5 @@ ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200)); } + +} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp index afb1029..2026acc 100644 --- a/libunwindstack/tests/MemoryFake.cpp +++ b/libunwindstack/tests/MemoryFake.cpp
@@ -21,6 +21,8 @@ #include "MemoryFake.h" +namespace unwindstack { + void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) { const uint8_t* src = reinterpret_cast<const uint8_t*>(memory); for (size_t i = 0; i < length; i++, addr++) { @@ -44,3 +46,5 @@ } return true; } + +} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h index 70ef30a..d374261 100644 --- a/libunwindstack/tests/MemoryFake.h +++ b/libunwindstack/tests/MemoryFake.h
@@ -23,7 +23,9 @@ #include <vector> #include <unordered_map> -#include "Memory.h" +#include <unwindstack/Memory.h> + +namespace unwindstack { class MemoryFake : public Memory { public: @@ -87,4 +89,6 @@ } }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp index aa7a23a..a204bae 100644 --- a/libunwindstack/tests/MemoryFileTest.cpp +++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -21,7 +21,9 @@ #include <android-base/file.h> #include <gtest/gtest.h> -#include "Memory.h" +#include <unwindstack/Memory.h> + +namespace unwindstack { class MemoryFileTest : public ::testing::Test { protected: @@ -269,3 +271,5 @@ ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i; } } + +} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp index ab999da..73eebdd 100644 --- a/libunwindstack/tests/MemoryLocalTest.cpp +++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -21,7 +21,9 @@ #include <gtest/gtest.h> -#include "Memory.h" +#include <unwindstack/Memory.h> + +namespace unwindstack { TEST(MemoryLocalTest, read) { std::vector<uint8_t> src(1024); @@ -64,3 +66,5 @@ uint64_t value; ASSERT_FALSE(local.Read(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX)); } + +} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp index ee5ba01..6d1366c 100644 --- a/libunwindstack/tests/MemoryRangeTest.cpp +++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -22,25 +22,19 @@ #include <gtest/gtest.h> -#include "Memory.h" +#include <unwindstack/Memory.h> #include "MemoryFake.h" -class MemoryRangeTest : public ::testing::Test { - protected: - void SetUp() override { - memory_ = new MemoryFake; - } +namespace unwindstack { - MemoryFake* memory_; -}; - -TEST_F(MemoryRangeTest, read) { +TEST(MemoryRangeTest, read) { std::vector<uint8_t> src(1024); memset(src.data(), 0x4c, 1024); - memory_->SetMemory(9001, src); + MemoryFake* memory = new MemoryFake; + memory->SetMemory(9001, src); - MemoryRange range(memory_, 9001, 9001 + src.size()); + MemoryRange range(memory, 9001, 9001 + src.size()); std::vector<uint8_t> dst(1024); ASSERT_TRUE(range.Read(0, dst.data(), src.size())); @@ -49,12 +43,13 @@ } } -TEST_F(MemoryRangeTest, read_near_limit) { +TEST(MemoryRangeTest, read_near_limit) { std::vector<uint8_t> src(4096); memset(src.data(), 0x4c, 4096); - memory_->SetMemory(1000, src); + MemoryFake* memory = new MemoryFake; + memory->SetMemory(1000, src); - MemoryRange range(memory_, 1000, 2024); + MemoryRange range(memory, 1000, 2024); std::vector<uint8_t> dst(1024); ASSERT_TRUE(range.Read(1020, dst.data(), 4)); @@ -71,9 +66,11 @@ ASSERT_TRUE(range.Read(1020, dst.data(), 4)); } -TEST_F(MemoryRangeTest, read_overflow) { +TEST(MemoryRangeTest, read_overflow) { std::vector<uint8_t> buffer(100); std::unique_ptr<MemoryRange> overflow(new MemoryRange(new MemoryFakeAlwaysReadZero, 100, 200)); ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100)); } + +} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp index e48edf7..f8965b2 100644 --- a/libunwindstack/tests/MemoryRemoteTest.cpp +++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -31,10 +31,12 @@ #include <android-base/file.h> #include <gtest/gtest.h> -#include "Memory.h" +#include <unwindstack/Memory.h> #include "MemoryFake.h" +namespace unwindstack { + class MemoryRemoteTest : public ::testing::Test { protected: static uint64_t NanoTime() { @@ -91,6 +93,7 @@ ASSERT_TRUE(Detach(pid)); kill(pid, SIGKILL); + ASSERT_EQ(pid, wait(nullptr)); } TEST_F(MemoryRemoteTest, read_fail) { @@ -131,6 +134,7 @@ ASSERT_TRUE(Detach(pid)); kill(pid, SIGKILL); + ASSERT_EQ(pid, wait(nullptr)); } TEST_F(MemoryRemoteTest, read_overflow) { @@ -160,4 +164,7 @@ ASSERT_TRUE(Detach(pid)); kill(pid, SIGKILL); + ASSERT_EQ(pid, wait(nullptr)); } + +} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp index 51b5d7d..4a9ed9f 100644 --- a/libunwindstack/tests/MemoryTest.cpp +++ b/libunwindstack/tests/MemoryTest.cpp
@@ -22,10 +22,12 @@ #include <gtest/gtest.h> -#include "Memory.h" +#include <unwindstack/Memory.h> #include "MemoryFake.h" +namespace unwindstack { + TEST(MemoryTest, read32) { MemoryFakeAlwaysReadZero memory; @@ -124,3 +126,5 @@ ASSERT_TRUE(memory.ReadString(0, &dst_name)); ASSERT_EQ("short", dst_name); } + +} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h index ff030c8..6669d7d 100644 --- a/libunwindstack/tests/RegsFake.h +++ b/libunwindstack/tests/RegsFake.h
@@ -19,7 +19,10 @@ #include <stdint.h> -#include "Regs.h" +#include <unwindstack/Memory.h> +#include <unwindstack/Regs.h> + +namespace unwindstack { template <typename TypeParam> class RegsFake : public RegsImpl<TypeParam> { @@ -28,9 +31,12 @@ : RegsImpl<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; } + void SetFromRaw() override {} + bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; } bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; } }; +} // namespace unwindstack + #endif // _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp new file mode 100644 index 0000000..85192d5 --- /dev/null +++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -0,0 +1,201 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> + +#include <gtest/gtest.h> + +#include <unwindstack/Elf.h> +#include <unwindstack/Regs.h> + +#include "Machine.h" + +#include "MemoryFake.h" + +namespace unwindstack { + +class RegsStepIfSignalHandlerTest : public ::testing::Test { + protected: + void SetUp() override { + elf_memory_ = new MemoryFake; + elf_.reset(new Elf(elf_memory_)); + } + + void ArmStepIfSignalHandlerNonRt(uint32_t pc_data); + void ArmStepIfSignalHandlerRt(uint32_t pc_data); + + MemoryFake* elf_memory_; + MemoryFake process_memory_; + std::unique_ptr<Elf> elf_; +}; + +void RegsStepIfSignalHandlerTest::ArmStepIfSignalHandlerNonRt(uint32_t pc_data) { + uint64_t addr = 0x1000; + RegsArm regs; + regs[ARM_REG_PC] = 0x5000; + regs[ARM_REG_SP] = addr; + regs.SetFromRaw(); + + elf_memory_->SetData32(0x5000, pc_data); + + for (uint64_t index = 0; index <= 30; index++) { + process_memory_.SetData32(addr + index * 4, index * 0x10); + } + + ASSERT_TRUE(regs.StepIfSignalHandler(0x5000, elf_.get(), &process_memory_)); + EXPECT_EQ(0x100U, regs[ARM_REG_SP]); + EXPECT_EQ(0x120U, regs[ARM_REG_PC]); + EXPECT_EQ(0x100U, regs.sp()); + EXPECT_EQ(0x120U, regs.pc()); +} + +TEST_F(RegsStepIfSignalHandlerTest, arm_step_if_signal_handler_non_rt) { + // Form 1 + ArmStepIfSignalHandlerNonRt(0xe3a07077); + + // Form 2 + ArmStepIfSignalHandlerNonRt(0xef900077); + + // Form 3 + ArmStepIfSignalHandlerNonRt(0xdf002777); +} + +void RegsStepIfSignalHandlerTest::ArmStepIfSignalHandlerRt(uint32_t pc_data) { + uint64_t addr = 0x1000; + RegsArm regs; + regs[ARM_REG_PC] = 0x5000; + regs[ARM_REG_SP] = addr; + regs.SetFromRaw(); + + elf_memory_->SetData32(0x5000, pc_data); + + for (uint64_t index = 0; index <= 100; index++) { + process_memory_.SetData32(addr + index * 4, index * 0x10); + } + + ASSERT_TRUE(regs.StepIfSignalHandler(0x5000, elf_.get(), &process_memory_)); + EXPECT_EQ(0x350U, regs[ARM_REG_SP]); + EXPECT_EQ(0x370U, regs[ARM_REG_PC]); + EXPECT_EQ(0x350U, regs.sp()); + EXPECT_EQ(0x370U, regs.pc()); +} + +TEST_F(RegsStepIfSignalHandlerTest, arm_step_if_signal_handler_rt) { + // Form 1 + ArmStepIfSignalHandlerRt(0xe3a070ad); + + // Form 2 + ArmStepIfSignalHandlerRt(0xef9000ad); + + // Form 3 + ArmStepIfSignalHandlerRt(0xdf0027ad); +} + +TEST_F(RegsStepIfSignalHandlerTest, arm64_step_if_signal_handler) { + uint64_t addr = 0x1000; + RegsArm64 regs; + regs[ARM64_REG_PC] = 0x8000; + regs[ARM64_REG_SP] = addr; + regs.SetFromRaw(); + + elf_memory_->SetData64(0x8000, 0xd4000001d2801168ULL); + + for (uint64_t index = 0; index <= 100; index++) { + process_memory_.SetData64(addr + index * 8, index * 0x10); + } + + ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_)); + EXPECT_EQ(0x460U, regs[ARM64_REG_SP]); + EXPECT_EQ(0x470U, regs[ARM64_REG_PC]); + EXPECT_EQ(0x460U, regs.sp()); + EXPECT_EQ(0x470U, regs.pc()); +} + +TEST_F(RegsStepIfSignalHandlerTest, x86_step_if_signal_handler_no_siginfo) { + uint64_t addr = 0xa00; + RegsX86 regs; + regs[X86_REG_EIP] = 0x4100; + regs[X86_REG_ESP] = addr; + regs.SetFromRaw(); + + elf_memory_->SetData64(0x4100, 0x80cd00000077b858ULL); + for (uint64_t index = 0; index <= 25; index++) { + process_memory_.SetData32(addr + index * 4, index * 0x10); + } + + ASSERT_TRUE(regs.StepIfSignalHandler(0x4100, elf_.get(), &process_memory_)); + EXPECT_EQ(0x70U, regs[X86_REG_EBP]); + EXPECT_EQ(0x80U, regs[X86_REG_ESP]); + EXPECT_EQ(0x90U, regs[X86_REG_EBX]); + EXPECT_EQ(0xa0U, regs[X86_REG_EDX]); + EXPECT_EQ(0xb0U, regs[X86_REG_ECX]); + EXPECT_EQ(0xc0U, regs[X86_REG_EAX]); + EXPECT_EQ(0xf0U, regs[X86_REG_EIP]); + EXPECT_EQ(0x80U, regs.sp()); + EXPECT_EQ(0xf0U, regs.pc()); +} + +TEST_F(RegsStepIfSignalHandlerTest, x86_step_if_signal_handler_siginfo) { + uint64_t addr = 0xa00; + RegsX86 regs; + regs[X86_REG_EIP] = 0x4100; + regs[X86_REG_ESP] = addr; + regs.SetFromRaw(); + + elf_memory_->SetData64(0x4100, 0x0080cd000000adb8ULL); + addr += 8; + // Pointer to ucontext data. + process_memory_.SetData32(addr, 0x8100); + + addr = 0x8100; + for (uint64_t index = 0; index <= 30; index++) { + process_memory_.SetData32(addr + index * 4, index * 0x10); + } + + ASSERT_TRUE(regs.StepIfSignalHandler(0x4100, elf_.get(), &process_memory_)); + EXPECT_EQ(0xb0U, regs[X86_REG_EBP]); + EXPECT_EQ(0xc0U, regs[X86_REG_ESP]); + EXPECT_EQ(0xd0U, regs[X86_REG_EBX]); + EXPECT_EQ(0xe0U, regs[X86_REG_EDX]); + EXPECT_EQ(0xf0U, regs[X86_REG_ECX]); + EXPECT_EQ(0x100U, regs[X86_REG_EAX]); + EXPECT_EQ(0x130U, regs[X86_REG_EIP]); + EXPECT_EQ(0xc0U, regs.sp()); + EXPECT_EQ(0x130U, regs.pc()); +} + +TEST_F(RegsStepIfSignalHandlerTest, x86_64_step_if_signal_handler) { + uint64_t addr = 0x500; + RegsX86_64 regs; + regs[X86_64_REG_RIP] = 0x7000; + regs[X86_64_REG_RSP] = addr; + regs.SetFromRaw(); + + elf_memory_->SetData64(0x7000, 0x0f0000000fc0c748); + elf_memory_->SetData16(0x7008, 0x0f05); + + for (uint64_t index = 0; index <= 30; index++) { + process_memory_.SetData64(addr + index * 8, index * 0x10); + } + + ASSERT_TRUE(regs.StepIfSignalHandler(0x7000, elf_.get(), &process_memory_)); + EXPECT_EQ(0x140U, regs[X86_64_REG_RSP]); + EXPECT_EQ(0x150U, regs[X86_64_REG_RIP]); + EXPECT_EQ(0x140U, regs.sp()); + EXPECT_EQ(0x150U, regs.pc()); +} + +} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp index 3056373..e6de56a 100644 --- a/libunwindstack/tests/RegsTest.cpp +++ b/libunwindstack/tests/RegsTest.cpp
@@ -18,13 +18,15 @@ #include <gtest/gtest.h> -#include "Elf.h" -#include "ElfInterface.h" -#include "MapInfo.h" -#include "Regs.h" +#include <unwindstack/Elf.h> +#include <unwindstack/ElfInterface.h> +#include <unwindstack/MapInfo.h> +#include <unwindstack/Regs.h> #include "MemoryFake.h" +namespace unwindstack { + class ElfFake : public Elf { public: ElfFake(Memory* memory) : Elf(memory) { valid_ = true; } @@ -56,7 +58,9 @@ : RegsImpl<TypeParam>(total_regs, regs_sp, return_loc) {} virtual ~RegsTestImpl() = default; - uint64_t GetAdjustedPc(uint64_t, Elf*) { return 0; } + uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; } + void SetFromRaw() override {} + bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; } }; class RegsTest : public ::testing::Test { @@ -69,10 +73,7 @@ } template <typename AddressType> - void regs_rel_pc(); - - template <typename AddressType> - void regs_return_address_register(); + void RegsReturnAddressRegister(); ElfInterfaceFake* elf_interface_; MemoryFake* memory_; @@ -126,27 +127,7 @@ } template <typename AddressType> -void RegsTest::regs_rel_pc() { - RegsTestImpl<AddressType> regs(30, 12); - - elf_interface_->set_load_bias(0); - MapInfo map_info{.start = 0x1000, .end = 0x2000}; - regs.set_pc(0x1101); - ASSERT_EQ(0x101U, regs.GetRelPc(elf_.get(), &map_info)); - elf_interface_->set_load_bias(0x3000); - ASSERT_EQ(0x3101U, regs.GetRelPc(elf_.get(), &map_info)); -} - -TEST_F(RegsTest, regs32_rel_pc) { - regs_rel_pc<uint32_t>(); -} - -TEST_F(RegsTest, regs64_rel_pc) { - regs_rel_pc<uint64_t>(); -} - -template <typename AddressType> -void RegsTest::regs_return_address_register() { +void RegsTest::RegsReturnAddressRegister() { RegsTestImpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5)); regs[5] = 0x12345; @@ -156,11 +137,11 @@ } TEST_F(RegsTest, regs32_return_address_register) { - regs_return_address_register<uint32_t>(); + RegsReturnAddressRegister<uint32_t>(); } TEST_F(RegsTest, regs64_return_address_register) { - regs_return_address_register<uint64_t>(); + RegsReturnAddressRegister<uint64_t>(); } TEST_F(RegsTest, regs32_return_address_sp_offset) { @@ -249,18 +230,60 @@ MapInfo map_info{.start = 0x1000, .end = 0x2000}; regs_arm.set_pc(0x1500); - ASSERT_EQ(0x500U, regs_arm.GetRelPc(&invalid_elf, &map_info)); + ASSERT_EQ(0x500U, invalid_elf.GetRelPc(regs_arm.pc(), &map_info)); ASSERT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, &invalid_elf)); regs_arm64.set_pc(0x1600); - ASSERT_EQ(0x600U, regs_arm64.GetRelPc(&invalid_elf, &map_info)); + ASSERT_EQ(0x600U, invalid_elf.GetRelPc(regs_arm64.pc(), &map_info)); ASSERT_EQ(0x600U, regs_arm64.GetAdjustedPc(0x600U, &invalid_elf)); regs_x86.set_pc(0x1700); - ASSERT_EQ(0x700U, regs_x86.GetRelPc(&invalid_elf, &map_info)); + ASSERT_EQ(0x700U, invalid_elf.GetRelPc(regs_x86.pc(), &map_info)); ASSERT_EQ(0x700U, regs_x86.GetAdjustedPc(0x700U, &invalid_elf)); regs_x86_64.set_pc(0x1800); - ASSERT_EQ(0x800U, regs_x86_64.GetRelPc(&invalid_elf, &map_info)); + ASSERT_EQ(0x800U, invalid_elf.GetRelPc(regs_x86_64.pc(), &map_info)); ASSERT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, &invalid_elf)); } + +TEST_F(RegsTest, arm_set_from_raw) { + RegsArm arm; + uint32_t* regs = reinterpret_cast<uint32_t*>(arm.RawData()); + regs[13] = 0x100; + regs[15] = 0x200; + arm.SetFromRaw(); + EXPECT_EQ(0x100U, arm.sp()); + EXPECT_EQ(0x200U, arm.pc()); +} + +TEST_F(RegsTest, arm64_set_from_raw) { + RegsArm64 arm64; + uint64_t* regs = reinterpret_cast<uint64_t*>(arm64.RawData()); + regs[31] = 0xb100000000ULL; + regs[32] = 0xc200000000ULL; + arm64.SetFromRaw(); + EXPECT_EQ(0xb100000000U, arm64.sp()); + EXPECT_EQ(0xc200000000U, arm64.pc()); +} + +TEST_F(RegsTest, x86_set_from_raw) { + RegsX86 x86; + uint32_t* regs = reinterpret_cast<uint32_t*>(x86.RawData()); + regs[4] = 0x23450000; + regs[8] = 0xabcd0000; + x86.SetFromRaw(); + EXPECT_EQ(0x23450000U, x86.sp()); + EXPECT_EQ(0xabcd0000U, x86.pc()); +} + +TEST_F(RegsTest, x86_64_set_from_raw) { + RegsX86_64 x86_64; + uint64_t* regs = reinterpret_cast<uint64_t*>(x86_64.RawData()); + regs[7] = 0x1200000000ULL; + regs[16] = 0x4900000000ULL; + x86_64.SetFromRaw(); + EXPECT_EQ(0x1200000000U, x86_64.sp()); + EXPECT_EQ(0x4900000000U, x86_64.pc()); +} + +} // namespace unwindstack
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp index a0a21e6..da258a6 100644 --- a/libunwindstack/tests/SymbolsTest.cpp +++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -32,10 +32,13 @@ #include <android-base/test_utils.h> #include <gtest/gtest.h> -#include "Memory.h" +#include <unwindstack/Memory.h> + #include "MemoryFake.h" #include "Symbols.h" +namespace unwindstack { + template <typename TypeParam> class SymbolsTest : public ::testing::Test { protected: @@ -333,3 +336,5 @@ typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes; INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes); + +} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp new file mode 100644 index 0000000..3c69e2a --- /dev/null +++ b/libunwindstack/tests/UnwindTest.cpp
@@ -0,0 +1,316 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <string.h> + +#include <signal.h> +#include <stdint.h> +#include <sys/ptrace.h> +#include <sys/syscall.h> +#include <unistd.h> + +#include <gtest/gtest.h> + +#include <atomic> +#include <memory> +#include <sstream> +#include <string> +#include <thread> +#include <vector> + +#include <unwindstack/Elf.h> +#include <unwindstack/MapInfo.h> +#include <unwindstack/Maps.h> +#include <unwindstack/Memory.h> +#include <unwindstack/Regs.h> +#include <unwindstack/RegsGetLocal.h> + +namespace unwindstack { + +static std::atomic_bool g_ready(false); +static volatile bool g_ready_for_remote = false; +static volatile bool g_signal_ready_for_remote = false; +static std::atomic_bool g_finish(false); +static std::atomic_uintptr_t g_ucontext; + +static std::vector<const char*> kFunctionOrder{"InnerFunction", "MiddleFunction", "OuterFunction"}; + +static std::vector<const char*> kFunctionSignalOrder{"SignalInnerFunction", "SignalMiddleFunction", + "SignalOuterFunction", "InnerFunction", + "MiddleFunction", "OuterFunction"}; + +static void SignalHandler(int, siginfo_t*, void* sigcontext) { + g_ucontext = reinterpret_cast<uintptr_t>(sigcontext); + while (!g_finish.load()) { + } +} + +extern "C" void SignalInnerFunction() { + g_signal_ready_for_remote = true; + while (!g_finish.load()) { + } +} + +extern "C" void SignalMiddleFunction() { + SignalInnerFunction(); +} + +extern "C" void SignalOuterFunction() { + SignalMiddleFunction(); +} + +static void SignalCallerHandler(int, siginfo_t*, void*) { + SignalOuterFunction(); +} + +static std::string ErrorMsg(const std::vector<const char*>& function_names, size_t index, + std::stringstream& unwind_stream) { + return std::string( + "Unwind completed without finding all frames\n" + " Looking for function: ") + + function_names[index] + "\n" + "Unwind data:\n" + unwind_stream.str(); +} + +static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs, + std::vector<const char*>& function_names) { + size_t function_name_index = 0; + + std::stringstream unwind_stream; + unwind_stream << std::hex; + for (size_t frame_num = 0; frame_num < 64; frame_num++) { + ASSERT_NE(0U, regs->pc()) << ErrorMsg(function_names, function_name_index, unwind_stream); + MapInfo* map_info = maps->Find(regs->pc()); + ASSERT_TRUE(map_info != nullptr) << ErrorMsg(function_names, function_name_index, unwind_stream); + + Elf* elf = map_info->GetElf(pid, true); + uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info); + uint64_t adjusted_rel_pc = rel_pc; + if (frame_num != 0) { + adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf); + } + unwind_stream << " PC: 0x" << regs->pc() << " Rel: 0x" << adjusted_rel_pc; + unwind_stream << " Map: "; + if (!map_info->name.empty()) { + unwind_stream << map_info->name; + } else { + unwind_stream << " anonymous"; + } + unwind_stream << "<" << map_info->start << "-" << map_info->end << ">"; + + std::string name; + uint64_t func_offset; + if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) { + if (name == function_names[function_name_index]) { + if (++function_name_index == function_names.size()) { + return; + } + } + unwind_stream << " " << name; + } + unwind_stream << "\n"; + ASSERT_TRUE(elf->Step(rel_pc + map_info->elf_offset, regs, memory)) + << ErrorMsg(function_names, function_name_index, unwind_stream); + } + ASSERT_TRUE(false) << ErrorMsg(function_names, function_name_index, unwind_stream); +} + +// This test assumes that this code is compiled with optimizations turned +// off. If this doesn't happen, then all of the calls will be optimized +// away. +extern "C" void InnerFunction(bool local) { + if (local) { + LocalMaps maps; + ASSERT_TRUE(maps.Parse()); + std::unique_ptr<Regs> regs(Regs::CreateFromLocal()); + RegsGetLocal(regs.get()); + MemoryLocal memory; + + VerifyUnwind(getpid(), &memory, &maps, regs.get(), kFunctionOrder); + } else { + g_ready_for_remote = true; + g_ready = true; + while (!g_finish.load()) { + } + } +} + +extern "C" void MiddleFunction(bool local) { + InnerFunction(local); +} + +extern "C" void OuterFunction(bool local) { + MiddleFunction(local); +} + +TEST(UnwindTest, local) { + OuterFunction(true); +} + +void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) { + *completed = false; + // Need to sleep before attempting first ptrace. Without this, on the + // host it becomes impossible to attach and ptrace set errno to EPERM. + usleep(1000); + for (size_t i = 0; i < 100; i++) { + ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0)); + for (size_t j = 0; j < 100; j++) { + siginfo_t si; + if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) { + MemoryRemote memory(pid); + // Read the remote value to see if we are ready. + bool value; + if (memory.Read(addr, &value, sizeof(value)) && value) { + *completed = true; + break; + } + } + usleep(1000); + } + if (leave_attached && *completed) { + break; + } + ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0)); + if (*completed) { + break; + } + usleep(1000); + } +} + +TEST(UnwindTest, remote) { + pid_t pid; + if ((pid = fork()) == 0) { + OuterFunction(false); + exit(0); + } + ASSERT_NE(-1, pid); + + bool completed; + WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed); + ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready."; + + RemoteMaps maps(pid); + ASSERT_TRUE(maps.Parse()); + MemoryRemote memory(pid); + uint32_t machine_type; + std::unique_ptr<Regs> regs(Regs::RemoteGet(pid, &machine_type)); + ASSERT_TRUE(regs.get() != nullptr); + + VerifyUnwind(pid, &memory, &maps, regs.get(), kFunctionOrder); + + ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0)); + + kill(pid, SIGKILL); + ASSERT_EQ(pid, wait(nullptr)); +} + +TEST(UnwindTest, from_context) { + std::atomic_int tid(0); + std::thread thread([&]() { + tid = syscall(__NR_gettid); + OuterFunction(false); + }); + + struct sigaction act, oldact; + memset(&act, 0, sizeof(act)); + act.sa_sigaction = SignalHandler; + act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; + ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact)); + // Wait for the tid to get set. + for (size_t i = 0; i < 100; i++) { + if (tid.load() != 0) { + break; + } + usleep(1000); + } + ASSERT_NE(0, tid.load()); + // Portable tgkill method. + ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno); + + // Wait for context data. + void* ucontext; + for (size_t i = 0; i < 2000; i++) { + ucontext = reinterpret_cast<void*>(g_ucontext.load()); + if (ucontext != nullptr) { + break; + } + usleep(1000); + } + ASSERT_TRUE(ucontext != nullptr) << "Timed out waiting for thread to respond to signal."; + + LocalMaps maps; + ASSERT_TRUE(maps.Parse()); + std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::GetMachineType(), ucontext)); + MemoryLocal memory; + + VerifyUnwind(tid.load(), &memory, &maps, regs.get(), kFunctionOrder); + + ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr)); + + g_finish = true; + thread.join(); +} + +static void RemoteThroughSignal(unsigned int sa_flags) { + g_ready = false; + g_signal_ready_for_remote = false; + g_finish = false; + + pid_t pid; + if ((pid = fork()) == 0) { + struct sigaction act, oldact; + memset(&act, 0, sizeof(act)); + act.sa_sigaction = SignalCallerHandler; + act.sa_flags = SA_RESTART | SA_ONSTACK | sa_flags; + ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact)); + + OuterFunction(false); + exit(0); + } + ASSERT_NE(-1, pid); + + bool completed; + WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed); + ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready."; + ASSERT_EQ(0, kill(pid, SIGUSR1)); + WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote), true, &completed); + ASSERT_TRUE(completed) << "Timed out waiting for remote process to be in signal handler."; + + RemoteMaps maps(pid); + ASSERT_TRUE(maps.Parse()); + MemoryRemote memory(pid); + uint32_t machine_type; + std::unique_ptr<Regs> regs(Regs::RemoteGet(pid, &machine_type)); + ASSERT_TRUE(regs.get() != nullptr); + + VerifyUnwind(pid, &memory, &maps, regs.get(), kFunctionSignalOrder); + + ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0)); + + kill(pid, SIGKILL); + ASSERT_EQ(pid, wait(nullptr)); +} + +TEST(UnwindTest, remote_through_signal) { + RemoteThroughSignal(0); +} + +TEST(UnwindTest, remote_through_signal_sa_siginfo) { + RemoteThroughSignal(SA_SIGINFO); +} + +} // namespace unwindstack
diff --git a/libunwindstack/tests/files/elf32.xz b/libunwindstack/tests/files/elf32.xz new file mode 100644 index 0000000..f25d433 --- /dev/null +++ b/libunwindstack/tests/files/elf32.xz Binary files differ
diff --git a/libunwindstack/tests/files/elf64.xz b/libunwindstack/tests/files/elf64.xz new file mode 100644 index 0000000..eb1618e --- /dev/null +++ b/libunwindstack/tests/files/elf64.xz Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp new file mode 100644 index 0000000..642105a --- /dev/null +++ b/libunwindstack/tools/unwind.cpp
@@ -0,0 +1,162 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <elf.h> +#include <errno.h> +#include <inttypes.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <unistd.h> + +#include <string> + +#include <unwindstack/Elf.h> +#include <unwindstack/MapInfo.h> +#include <unwindstack/Maps.h> +#include <unwindstack/Memory.h> +#include <unwindstack/Regs.h> + +static bool Attach(pid_t pid) { + if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) { + return false; + } + + // Allow at least 1 second to attach properly. + for (size_t i = 0; i < 1000; i++) { + siginfo_t si; + if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) { + return true; + } + usleep(1000); + } + printf("%d: Failed to stop.\n", pid); + return false; +} + +static bool Detach(pid_t pid) { + return ptrace(PTRACE_DETACH, pid, 0, 0) == 0; +} + +void DoUnwind(pid_t pid) { + unwindstack::RemoteMaps remote_maps(pid); + if (!remote_maps.Parse()) { + printf("Failed to parse map data.\n"); + return; + } + + uint32_t machine_type; + unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid, &machine_type); + if (regs == nullptr) { + printf("Unable to get remote reg data\n"); + return; + } + + bool bits32 = true; + printf("ABI: "); + switch (machine_type) { + case EM_ARM: + printf("arm"); + break; + case EM_386: + printf("x86"); + break; + case EM_AARCH64: + printf("arm64"); + bits32 = false; + break; + case EM_X86_64: + printf("x86_64"); + bits32 = false; + break; + default: + printf("unknown\n"); + return; + } + printf("\n"); + + unwindstack::MemoryRemote remote_memory(pid); + for (size_t frame_num = 0; frame_num < 64; frame_num++) { + if (regs->pc() == 0) { + break; + } + unwindstack::MapInfo* map_info = remote_maps.Find(regs->pc()); + if (map_info == nullptr) { + printf("Failed to find map data for the pc\n"); + break; + } + + unwindstack::Elf* elf = map_info->GetElf(pid, true); + + uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info); + uint64_t adjusted_rel_pc = rel_pc; + // Don't need to adjust the first frame pc. + if (frame_num != 0) { + adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf); + } + + std::string name; + if (bits32) { + printf(" #%02zu pc %08" PRIx64, frame_num, adjusted_rel_pc); + } else { + printf(" #%02zu pc %016" PRIx64, frame_num, adjusted_rel_pc); + } + if (!map_info->name.empty()) { + printf(" %s", map_info->name.c_str()); + if (map_info->elf_offset != 0) { + printf(" (offset 0x%" PRIx64 ")", map_info->elf_offset); + } + } else { + printf(" <anonymous:%" PRIx64 ">", map_info->offset); + } + uint64_t func_offset; + if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) { + printf(" (%s", name.c_str()); + if (func_offset != 0) { + printf("+%" PRId64, func_offset); + } + printf(")"); + } + printf("\n"); + + if (!elf->Step(rel_pc + map_info->elf_offset, regs, &remote_memory)) { + break; + } + } +} + +int main(int argc, char** argv) { + if (argc != 2) { + printf("Usage: unwind <PID>\n"); + return 1; + } + + pid_t pid = atoi(argv[1]); + if (!Attach(pid)) { + printf("Failed to attach to pid %d: %s\n", pid, strerror(errno)); + return 1; + } + + DoUnwind(pid); + + Detach(pid); + + return 0; +}
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp new file mode 100644 index 0000000..66a9439 --- /dev/null +++ b/libunwindstack/tools/unwind_info.cpp
@@ -0,0 +1,184 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <elf.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <unwindstack/DwarfSection.h> +#include <unwindstack/DwarfStructs.h> +#include <unwindstack/Elf.h> +#include <unwindstack/ElfInterface.h> +#include <unwindstack/Log.h> + +#include "ArmExidx.h" +#include "ElfInterfaceArm.h" + +namespace unwindstack { + +void DumpArm(ElfInterfaceArm* interface) { + if (interface == nullptr) { + printf("No ARM Unwind Information.\n\n"); + return; + } + + printf("ARM Unwind Information:\n"); + for (const auto& entry : interface->pt_loads()) { + uint64_t load_bias = entry.second.table_offset; + printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias, + entry.second.table_size + load_bias); + for (auto addr : *interface) { + std::string name; + printf(" PC 0x%" PRIx64, addr + load_bias); + uint64_t func_offset; + uint64_t pc = addr + load_bias; + // This might be a thumb function, so set the low bit. + if (interface->GetFunctionName(pc | 1, &name, &func_offset) && !name.empty()) { + printf(" <%s>", name.c_str()); + } + printf("\n"); + uint64_t entry; + if (!interface->FindEntry(pc, &entry)) { + printf(" Cannot find entry for address.\n"); + continue; + } + ArmExidx arm(nullptr, interface->memory(), nullptr); + arm.set_log(true); + arm.set_log_skip_execution(true); + arm.set_log_indent(2); + if (!arm.ExtractEntryData(entry)) { + if (arm.status() != ARM_STATUS_NO_UNWIND) { + printf(" Error trying to extract data.\n"); + } + continue; + } + if (arm.data()->size() > 0) { + if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) { + printf(" Error trying to evaluate dwarf data.\n"); + } + } + } + } + printf("\n"); +} + +void DumpDwarfSection(ElfInterface* interface, DwarfSection* section, uint64_t load_bias) { + for (const DwarfFde* fde : *section) { + // Sometimes there are entries that have empty length, skip those since + // they don't contain any interesting information. + if (fde->pc_start == fde->pc_end) { + continue; + } + printf("\n PC 0x%" PRIx64, fde->pc_start + load_bias); + std::string name; + uint64_t func_offset; + if (interface->GetFunctionName(fde->pc_start + load_bias, &name, &func_offset) && + !name.empty()) { + printf(" <%s>", name.c_str()); + } + printf("\n"); + if (!section->Log(2, UINT64_MAX, load_bias, fde)) { + printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start); + } + } +} + +int GetElfInfo(const char* file) { + // Send all log messages to stdout. + log_to_stdout(true); + + MemoryFileAtOffset* memory = new MemoryFileAtOffset; + if (!memory->Init(file, 0)) { + // Initializatation failed. + printf("Failed to init\n"); + return 1; + } + + Elf elf(memory); + if (!elf.Init() || !elf.valid()) { + printf("%s is not a valid elf file.\n", file); + return 1; + } + + ElfInterface* interface = elf.interface(); + if (elf.machine_type() == EM_ARM) { + DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface)); + printf("\n"); + } + + if (interface->eh_frame() != nullptr) { + printf("eh_frame information:\n"); + DumpDwarfSection(interface, interface->eh_frame(), interface->load_bias()); + printf("\n"); + } else { + printf("\nno eh_frame information\n"); + } + + if (interface->debug_frame() != nullptr) { + printf("\ndebug_frame information:\n"); + DumpDwarfSection(interface, interface->debug_frame(), interface->load_bias()); + printf("\n"); + } else { + printf("\nno debug_frame information\n"); + } + + // If there is a gnu_debugdata interface, dump the information for that. + ElfInterface* gnu_debugdata_interface = elf.gnu_debugdata_interface(); + if (gnu_debugdata_interface != nullptr) { + if (gnu_debugdata_interface->eh_frame() != nullptr) { + printf("\ngnu_debugdata (eh_frame):\n"); + DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->eh_frame(), 0); + printf("\n"); + } + if (gnu_debugdata_interface->debug_frame() != nullptr) { + printf("\ngnu_debugdata (debug_frame):\n"); + DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->debug_frame(), 0); + printf("\n"); + } + } else { + printf("\nno valid gnu_debugdata information\n"); + } + + return 0; +} + +} // namespace unwindstack + +int main(int argc, char** argv) { + if (argc != 2) { + printf("Need to pass the name of an elf file to the program.\n"); + return 1; + } + + struct stat st; + if (stat(argv[1], &st) == -1) { + printf("Cannot stat %s: %s\n", argv[1], strerror(errno)); + return 1; + } + if (!S_ISREG(st.st_mode)) { + printf("%s is not a regular file.\n", argv[1]); + return 1; + } + + return unwindstack::GetElfInfo(argv[1]); +}
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp new file mode 100644 index 0000000..b757c1e --- /dev/null +++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <elf.h> +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <unwindstack/Elf.h> +#include <unwindstack/Log.h> +#include <unwindstack/Memory.h> + +int main(int argc, char** argv) { + if (argc != 2) { + printf("Need to pass the name of an elf file to the program.\n"); + return 1; + } + + struct stat st; + if (stat(argv[1], &st) == -1) { + printf("Cannot stat %s: %s\n", argv[1], strerror(errno)); + return 1; + } + if (!S_ISREG(st.st_mode)) { + printf("%s is not a regular file.\n", argv[1]); + return 1; + } + + // Send all log messages to stdout. + unwindstack::log_to_stdout(true); + + unwindstack::MemoryFileAtOffset* memory = new unwindstack::MemoryFileAtOffset; + if (!memory->Init(argv[1], 0)) { + printf("Failed to init\n"); + return 1; + } + + unwindstack::Elf elf(memory); + if (!elf.Init() || !elf.valid()) { + printf("%s is not a valid elf file.\n", argv[1]); + return 1; + } + + switch (elf.machine_type()) { + case EM_ARM: + printf("ABI: arm\n"); + break; + case EM_AARCH64: + printf("ABI: arm64\n"); + break; + case EM_386: + printf("ABI: x86\n"); + break; + case EM_X86_64: + printf("ABI: x86_64\n"); + break; + default: + printf("ABI: unknown\n"); + return 1; + } + + // This is a crude way to get the symbols in order. + std::string name; + uint64_t load_bias = elf.interface()->load_bias(); + for (const auto& entry : elf.interface()->pt_loads()) { + uint64_t start = entry.second.offset + load_bias; + uint64_t end = entry.second.table_size + load_bias; + for (uint64_t addr = start; addr < end; addr += 4) { + std::string cur_name; + uint64_t func_offset; + if (elf.GetFunctionName(addr, &cur_name, &func_offset)) { + if (cur_name != name) { + printf("<0x%" PRIx64 "> Function: %s\n", addr - func_offset, cur_name.c_str()); + } + name = cur_name; + } + } + } + + return 0; +}
diff --git a/libunwindstack/unwind_info.cpp b/libunwindstack/unwind_info.cpp deleted file mode 100644 index 6f158b0..0000000 --- a/libunwindstack/unwind_info.cpp +++ /dev/null
@@ -1,121 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <elf.h> -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <stdio.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "ArmExidx.h" -#include "Elf.h" -#include "ElfInterface.h" -#include "ElfInterfaceArm.h" -#include "Log.h" - -void DumpArm(ElfInterfaceArm* interface) { - if (interface == nullptr) { - printf("No ARM Unwind Information.\n\n"); - return; - } - - printf("ARM Unwind Information:\n"); - for (const auto& entry : interface->pt_loads()) { - uint64_t load_bias = entry.second.table_offset; - printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias, - entry.second.table_size + load_bias); - for (auto addr : *interface) { - std::string name; - printf(" PC 0x%" PRIx64, addr + load_bias); - uint64_t func_offset; - if (interface->GetFunctionName(addr + load_bias + 1, &name, &func_offset) && !name.empty()) { - printf(" <%s>", name.c_str()); - } - printf("\n"); - uint64_t entry; - if (!interface->FindEntry(addr + load_bias, &entry)) { - printf(" Cannot find entry for address.\n"); - continue; - } - ArmExidx arm(nullptr, interface->memory(), nullptr); - arm.set_log(true); - arm.set_log_skip_execution(true); - arm.set_log_indent(2); - if (!arm.ExtractEntryData(entry)) { - if (arm.status() != ARM_STATUS_NO_UNWIND) { - printf(" Error trying to extract data.\n"); - } - continue; - } - if (arm.data()->size() > 0) { - if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) { - printf(" Error trying to evaluate dwarf data.\n"); - } - } - } - } - printf("\n"); -} - -int main(int argc, char** argv) { - if (argc != 2) { - printf("Need to pass the name of an elf file to the program.\n"); - return 1; - } - - struct stat st; - if (stat(argv[1], &st) == -1) { - printf("Cannot stat %s: %s\n", argv[1], strerror(errno)); - return 1; - } - if (!S_ISREG(st.st_mode)) { - printf("%s is not a regular file.\n", argv[1]); - return 1; - } - if (S_ISDIR(st.st_mode)) { - printf("%s is a directory.\n", argv[1]); - return 1; - } - - // Send all log messages to stdout. - log_to_stdout(true); - - MemoryFileAtOffset* memory = new MemoryFileAtOffset; - if (!memory->Init(argv[1], 0)) { - // Initializatation failed. - printf("Failed to init\n"); - return 1; - } - - Elf elf(memory); - if (!elf.Init() || !elf.valid()) { - printf("%s is not a valid elf file.\n", argv[1]); - return 1; - } - - ElfInterface* interface = elf.interface(); - if (elf.machine_type() == EM_ARM) { - DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface)); - printf("\n"); - } - - return 0; -}
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp index a0d6b9b..fc6f305 100644 --- a/libusbhost/Android.bp +++ b/libusbhost/Android.bp
@@ -16,6 +16,10 @@ cc_library { name: "libusbhost", + vendor_available: true, + vndk: { + enabled: true, + }, host_supported: true, srcs: ["usbhost.c"], cflags: ["-Werror"],
diff --git a/libutils/Android.bp b/libutils/Android.bp index 508f553..a779a8c 100644 --- a/libutils/Android.bp +++ b/libutils/Android.bp
@@ -18,10 +18,12 @@ host_supported: true, header_libs: [ + "liblog_headers", "libsystem_headers", "libcutils_headers" ], export_header_lib_headers: [ + "liblog_headers", "libsystem_headers", "libcutils_headers" ], @@ -44,13 +46,16 @@ cc_library { name: "libutils", vendor_available: true, + vndk: { + enabled: true, + support_system_process: true, + }, host_supported: true, srcs: [ "CallStack.cpp", "FileMap.cpp", "JenkinsHash.cpp", - "Log.cpp", "NativeHandle.cpp", "Printer.cpp", "PropertyMap.cpp", @@ -79,6 +84,10 @@ "libutils_headers", ], + shared_libs: [ + "liblog", + ], + arch: { mips: { cflags: ["-DALIGN_DOUBLE"], @@ -99,7 +108,6 @@ "libbacktrace", "libcutils", "libdl", - "liblog", "libvndksupport", ],
diff --git a/libutils/Log.cpp b/libutils/Log.cpp deleted file mode 100644 index 2c1fb86..0000000 --- a/libutils/Log.cpp +++ /dev/null
@@ -1,37 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Log" - -#include <utils/Log.h> -#include <utils/Timers.h> - -namespace android { - -LogIfSlow::LogIfSlow( - const char* tag, android_LogPriority priority, int timeoutMillis, const char* message) - : mTag(tag), mPriority(priority), mTimeoutMillis(timeoutMillis), mMessage(message), - mStart(systemTime(SYSTEM_TIME_BOOTTIME)) { -} - -LogIfSlow::~LogIfSlow() { - int durationMillis = (int)nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_BOOTTIME) - mStart); - if (durationMillis > mTimeoutMillis) { - LOG_PRI(mPriority, mTag, "%s: %dms", mMessage, durationMillis); - } -} - -} // namespace android
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp index 12f77bb..cbf042e 100644 --- a/libutils/Printer.cpp +++ b/libutils/Printer.cpp
@@ -45,9 +45,11 @@ #ifndef _WIN32 if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error ALOGE("%s: Failed to format string", __FUNCTION__); + va_end(arglist); return; } #else + va_end(arglist); return; #endif
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp index 28fc351..73ec1be 100644 --- a/libutils/SystemClock.cpp +++ b/libutils/SystemClock.cpp
@@ -23,9 +23,9 @@ #include <utils/SystemClock.h> -#include <sys/time.h> #include <string.h> #include <errno.h> +#include <time.h> #include <cutils/compiler.h>
diff --git a/libutils/include/utils/Log.h b/libutils/include/utils/Log.h index 5276a49..42e03e7 100644 --- a/libutils/include/utils/Log.h +++ b/libutils/include/utils/Log.h
@@ -1,72 +1,7 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// DO NOT INCLUDE ANYTHING NEW IN THIS FILE. -// -// C/C++ logging functions. See the logging documentation for API details. -// -// We'd like these to be available from C code (in case we import some from -// somewhere), so this has a C interface. -// -// The output will be correct when the log file is shared between multiple -// threads and/or multiple processes so long as the operating system -// supports O_APPEND. These calls have mutex-protected data structures -// and so are NOT reentrant. Do not use LOG in a signal handler. -// -#ifndef _LIBS_UTILS_LOG_H -#define _LIBS_UTILS_LOG_H - -#include <sys/types.h> +// <log/log.h> has replaced this file and all changes should go there instead. +// This path remains strictly to include that header as there are thousands of +// references to <utils/Log.h> in the tree. #include <log/log.h> - -#ifdef __cplusplus - -namespace android { - -/* - * A very simple utility that yells in the log when an operation takes too long. - */ -class LogIfSlow { -public: - LogIfSlow(const char* tag, android_LogPriority priority, - int timeoutMillis, const char* message); - ~LogIfSlow(); - -private: - const char* const mTag; - const android_LogPriority mPriority; - const int mTimeoutMillis; - const char* const mMessage; - const int64_t mStart; -}; - -/* - * Writes the specified debug log message if this block takes longer than the - * specified number of milliseconds to run. Includes the time actually taken. - * - * { - * ALOGD_IF_SLOW(50, "Excessive delay doing something."); - * doSomething(); - * } - */ -#define ALOGD_IF_SLOW(timeoutMillis, message) \ - android::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message); - -} // namespace android - -#endif // __cplusplus - -#endif // _LIBS_UTILS_LOG_H
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c index 696e978..d06cafc 100644 --- a/libvndksupport/linker.c +++ b/libvndksupport/linker.c
@@ -23,24 +23,38 @@ extern struct android_namespace_t* android_get_exported_namespace(const char*); +static const char* namespace_name = NULL; + +static struct android_namespace_t* get_vendor_namespace() { + const char* namespace_names[] = {"sphal", "default", NULL}; + static struct android_namespace_t* vendor_namespace = NULL; + if (vendor_namespace == NULL) { + int name_idx = 0; + while (namespace_names[name_idx] != NULL) { + vendor_namespace = android_get_exported_namespace(namespace_names[name_idx]); + if (vendor_namespace != NULL) { + namespace_name = namespace_names[name_idx]; + break; + } + name_idx++; + } + } + return vendor_namespace; +} + void* android_load_sphal_library(const char* name, int flag) { - struct android_namespace_t* sphal_namespace = android_get_exported_namespace("sphal"); - if (sphal_namespace != NULL) { + struct android_namespace_t* vendor_namespace = get_vendor_namespace(); + if (vendor_namespace != NULL) { const android_dlextinfo dlextinfo = { - .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = sphal_namespace, + .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = vendor_namespace, }; void* handle = android_dlopen_ext(name, flag, &dlextinfo); if (!handle) { - ALOGE( - "Could not load %s from sphal namespace: %s. ", - name, dlerror()); + ALOGE("Could not load %s from %s namespace: %s.", name, namespace_name, dlerror()); } return handle; } else { - ALOGI( - "sphal namespace is not configured for this process. " - "Loading %s from the current namespace instead.", - name); + ALOGD("Loading %s from current namespace instead of sphal namespace.", name); return dlopen(name, flag); } }
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp index 287a99c..f395c74 100644 --- a/libziparchive/Android.bp +++ b/libziparchive/Android.bp
@@ -50,19 +50,32 @@ "libbase", "liblog", ], -} + export_include_dirs: ["include"], +} cc_library { name: "libziparchive", host_supported: true, - vendor_available:true, + vendor_available: true, + vndk: { + enabled: true, + }, - defaults: ["libziparchive_defaults", "libziparchive_flags"], - shared_libs: ["liblog", "libbase"], + defaults: [ + "libziparchive_defaults", + "libziparchive_flags", + ], + shared_libs: [ + "liblog", + "libbase", + ], target: { android: { - shared_libs: ["libz", "libutils"], + shared_libs: [ + "libz", + "libutils", + ], }, host: { static_libs: ["libutils"], @@ -88,7 +101,10 @@ name: "libziparchive-host", host_supported: true, device_supported: false, - defaults: ["libziparchive_defaults", "libziparchive_flags"], + defaults: [ + "libziparchive_defaults", + "libziparchive_flags", + ], shared_libs: ["libz-host"], static_libs: ["libutils"], } @@ -150,3 +166,13 @@ }, }, } + +cc_binary { + name: "unzip", + defaults: ["libziparchive_flags"], + srcs: ["unzip.cpp"], + shared_libs: [ + "libbase", + "libziparchive", + ], +}
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h index ddbc286..5fc2fb4 100644 --- a/libziparchive/entry_name_utils-inl.h +++ b/libziparchive/entry_name_utils-inl.h
@@ -55,5 +55,4 @@ return true; } - #endif // LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
diff --git a/libziparchive/entry_name_utils_test.cc b/libziparchive/entry_name_utils_test.cc index 20715bb..d83d854 100644 --- a/libziparchive/entry_name_utils_test.cc +++ b/libziparchive/entry_name_utils_test.cc
@@ -20,44 +20,43 @@ TEST(entry_name_utils, NullChars) { // 'A', 'R', '\0', 'S', 'E' - const uint8_t zeroes[] = { 0x41, 0x52, 0x00, 0x53, 0x45 }; + const uint8_t zeroes[] = {0x41, 0x52, 0x00, 0x53, 0x45}; ASSERT_FALSE(IsValidEntryName(zeroes, sizeof(zeroes))); - const uint8_t zeroes_continuation_chars[] = { 0xc2, 0xa1, 0xc2, 0x00 }; - ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars, - sizeof(zeroes_continuation_chars))); + const uint8_t zeroes_continuation_chars[] = {0xc2, 0xa1, 0xc2, 0x00}; + ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars, sizeof(zeroes_continuation_chars))); } TEST(entry_name_utils, InvalidSequence) { // 0xfe is an invalid start byte - const uint8_t invalid[] = { 0x41, 0xfe }; + const uint8_t invalid[] = {0x41, 0xfe}; ASSERT_FALSE(IsValidEntryName(invalid, sizeof(invalid))); // 0x91 is an invalid start byte (it's a valid continuation byte). - const uint8_t invalid2[] = { 0x41, 0x91 }; + const uint8_t invalid2[] = {0x41, 0x91}; ASSERT_FALSE(IsValidEntryName(invalid2, sizeof(invalid2))); } TEST(entry_name_utils, TruncatedContinuation) { // Malayalam script with truncated bytes. There should be 2 bytes // after 0xe0 - const uint8_t truncated[] = { 0xe0, 0xb4, 0x85, 0xe0, 0xb4 }; + const uint8_t truncated[] = {0xe0, 0xb4, 0x85, 0xe0, 0xb4}; ASSERT_FALSE(IsValidEntryName(truncated, sizeof(truncated))); // 0xc2 is the start of a 2 byte sequence that we've subsequently // dropped. - const uint8_t truncated2[] = { 0xc2, 0xc2, 0xa1 }; + const uint8_t truncated2[] = {0xc2, 0xc2, 0xa1}; ASSERT_FALSE(IsValidEntryName(truncated2, sizeof(truncated2))); } TEST(entry_name_utils, BadContinuation) { // 0x41 is an invalid continuation char, since it's MSBs // aren't "10..." (are 01). - const uint8_t bad[] = { 0xc2, 0xa1, 0xc2, 0x41 }; + const uint8_t bad[] = {0xc2, 0xa1, 0xc2, 0x41}; ASSERT_FALSE(IsValidEntryName(bad, sizeof(bad))); // 0x41 is an invalid continuation char, since it's MSBs // aren't "10..." (are 11). - const uint8_t bad2[] = { 0xc2, 0xa1, 0xc2, 0xfe }; + const uint8_t bad2[] = {0xc2, 0xa1, 0xc2, 0xfe}; ASSERT_FALSE(IsValidEntryName(bad2, sizeof(bad2))); }
diff --git a/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h similarity index 86% rename from include/ziparchive/zip_archive.h rename to libziparchive/include/ziparchive/zip_archive.h index 31fc2df..73ae68d 100644 --- a/include/ziparchive/zip_archive.h +++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -28,8 +28,8 @@ /* Zip compression methods we support */ enum { - kCompressStored = 0, // no compression - kCompressDeflated = 8, // standard deflate + kCompressStored = 0, // no compression + kCompressDeflated = 8, // standard deflate }; struct ZipString { @@ -44,19 +44,17 @@ explicit ZipString(const char* entry_name); bool operator==(const ZipString& rhs) const { - return name && (name_length == rhs.name_length) && - (memcmp(name, rhs.name, name_length) == 0); + return name && (name_length == rhs.name_length) && (memcmp(name, rhs.name, name_length) == 0); } bool StartsWith(const ZipString& prefix) const { return name && (name_length >= prefix.name_length) && - (memcmp(name, prefix.name, prefix.name_length) == 0); + (memcmp(name, prefix.name, prefix.name_length) == 0); } bool EndsWith(const ZipString& suffix) const { return name && (name_length >= suffix.name_length) && - (memcmp(name + name_length - suffix.name_length, suffix.name, - suffix.name_length) == 0); + (memcmp(name + name_length - suffix.name_length, suffix.name, suffix.name_length) == 0); } }; @@ -71,8 +69,17 @@ // Modification time. The zipfile format specifies // that the first two little endian bytes contain the time // and the last two little endian bytes contain the date. + // See `GetModificationTime`. + // TODO: should be overridden by extra time field, if present. uint32_t mod_time; + // Returns `mod_time` as a broken-down struct tm. + struct tm GetModificationTime() const; + + // Suggested Unix mode for this entry, from the zip archive if created on + // Unix, or a default otherwise. + mode_t unix_mode; + // 1 if this entry contains a data descriptor segment, 0 // otherwise. uint8_t has_data_descriptor; @@ -125,11 +132,11 @@ * * Returns 0 on success, and negative values on failure. */ -int32_t OpenArchiveFd(const int fd, const char* debugFileName, - ZipArchiveHandle *handle, bool assume_ownership = true); +int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle, + bool assume_ownership = true); int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debugFileName, - ZipArchiveHandle *handle); + ZipArchiveHandle* handle); /* * Close archive, releasing resources associated with it. This will * unmap the central directory of the zipfile and free all internal @@ -155,8 +162,7 @@ * On non-Windows platforms this method does not modify internal state and * can be called concurrently. */ -int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, - ZipEntry* data); +int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data); /* * Start iterating over all entries of a zip file. The order of iteration @@ -171,8 +177,7 @@ * * Returns 0 on success and negative values on failure. */ -int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, - const ZipString* optional_prefix, +int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix, const ZipString* optional_suffix); /* @@ -208,8 +213,7 @@ * * Returns 0 on success and negative values on failure. */ -int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, - uint8_t* begin, uint32_t size); +int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size); int GetFileDescriptor(const ZipArchiveHandle handle); @@ -221,9 +225,9 @@ /* * Stream the uncompressed data through the supplied function, * passing cookie to it each time it gets called. -*/ + */ int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry, - ProcessZipEntryFunction func, void* cookie); + ProcessZipEntryFunction func, void* cookie); #endif #endif // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h similarity index 100% rename from include/ziparchive/zip_archive_stream_entry.h rename to libziparchive/include/ziparchive/zip_archive_stream_entry.h
diff --git a/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h similarity index 96% rename from include/ziparchive/zip_writer.h rename to libziparchive/include/ziparchive/zip_writer.h index 08ead48..c350a27 100644 --- a/include/ziparchive/zip_writer.h +++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -19,7 +19,6 @@ #include <cstdio> #include <ctime> -#include <zlib.h> #include <memory> #include <string> @@ -28,6 +27,9 @@ #include "android-base/macros.h" #include "utils/Compat.h" +struct z_stream_s; +typedef struct z_stream_s z_stream; + /** * Writes a Zip file via a stateful interface. * @@ -50,7 +52,7 @@ * fclose(file); */ class ZipWriter { -public: + public: enum { /** * Flag to compress the zip entry using deflate. @@ -120,8 +122,7 @@ /** * Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry. */ - int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time, - uint32_t alignment); + int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time, uint32_t alignment); /** * Writes bytes to the zip file for the previously started zip entry. @@ -156,7 +157,7 @@ */ int32_t Finish(); -private: + private: DISALLOW_COPY_AND_ASSIGN(ZipWriter); int32_t HandleError(int32_t error_code); @@ -179,7 +180,7 @@ std::vector<FileEntry> files_; FileEntry current_file_entry_; - std::unique_ptr<z_stream, void(*)(z_stream*)> z_stream_; + std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_; std::vector<uint8_t> buffer_; };
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp new file mode 100644 index 0000000..6756007 --- /dev/null +++ b/libziparchive/unzip.cpp
@@ -0,0 +1,345 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <getopt.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +#include <set> +#include <string> + +#include <android-base/file.h> +#include <android-base/strings.h> +#include <ziparchive/zip_archive.h> + +enum OverwriteMode { + kAlways, + kNever, + kPrompt, +}; + +static OverwriteMode overwrite_mode = kPrompt; +static const char* flag_d = nullptr; +static bool flag_l = false; +static bool flag_p = false; +static bool flag_q = false; +static bool flag_v = false; +static const char* archive_name = nullptr; +static std::set<std::string> includes; +static std::set<std::string> excludes; +static uint64_t total_uncompressed_length = 0; +static uint64_t total_compressed_length = 0; +static size_t file_count = 0; + +static bool Filter(const std::string& name) { + if (!excludes.empty() && excludes.find(name) != excludes.end()) return true; + if (!includes.empty() && includes.find(name) == includes.end()) return true; + return false; +} + +static bool MakeDirectoryHierarchy(const std::string& path) { + // stat rather than lstat because a symbolic link to a directory is fine too. + struct stat sb; + if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return true; + + // Ensure the parent directories exist first. + if (!MakeDirectoryHierarchy(android::base::Dirname(path))) return false; + + // Then try to create this directory. + return (mkdir(path.c_str(), 0777) != -1); +} + +static int CompressionRatio(int64_t uncompressed, int64_t compressed) { + if (uncompressed == 0) return 0; + return (100LL * (uncompressed - compressed)) / uncompressed; +} + +static void MaybeShowHeader() { + if (!flag_q) printf("Archive: %s\n", archive_name); + if (flag_v) { + printf( + " Length Method Size Cmpr Date Time CRC-32 Name\n" + "-------- ------ ------- ---- ---------- ----- -------- ----\n"); + } else if (flag_l) { + printf( + " Length Date Time Name\n" + "--------- ---------- ----- ----\n"); + } +} + +static void MaybeShowFooter() { + if (flag_v) { + printf( + "-------- ------- --- -------\n" + "%8" PRId64 " %8" PRId64 " %3d%% %zu file%s\n", + total_uncompressed_length, total_compressed_length, + CompressionRatio(total_uncompressed_length, total_compressed_length), file_count, + (file_count == 1) ? "" : "s"); + } else if (flag_l) { + printf( + "--------- -------\n" + "%9" PRId64 " %zu file%s\n", + total_uncompressed_length, file_count, (file_count == 1) ? "" : "s"); + } +} + +static bool PromptOverwrite(const std::string& dst) { + // TODO: [r]ename not implemented because it doesn't seem useful. + printf("replace %s? [y]es, [n]o, [A]ll, [N]one: ", dst.c_str()); + fflush(stdout); + while (true) { + char* line = nullptr; + size_t n; + if (getline(&line, &n, stdin) == -1) { + error(1, 0, "(EOF/read error; assuming [N]one...)"); + overwrite_mode = kNever; + return false; + } + if (n == 0) continue; + char cmd = line[0]; + free(line); + switch (cmd) { + case 'y': + return true; + case 'n': + return false; + case 'A': + overwrite_mode = kAlways; + return true; + case 'N': + overwrite_mode = kNever; + return false; + } + } +} + +static void ExtractToPipe(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) { + // We need to extract to memory because ExtractEntryToFile insists on + // being able to seek and truncate, and you can't do that with stdout. + uint8_t* buffer = new uint8_t[entry.uncompressed_length]; + int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length); + if (err < 0) { + error(1, 0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err)); + } + if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) { + error(1, errno, "failed to write %s to stdout", name.c_str()); + } + delete[] buffer; +} + +static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) { + // Bad filename? + if (android::base::StartsWith(name, "/") || android::base::StartsWith(name, "../") || + name.find("/../") != std::string::npos) { + error(1, 0, "bad filename %s", name.c_str()); + } + + // Where are we actually extracting to (for human-readable output)? + std::string dst; + if (flag_d) { + dst = flag_d; + if (!android::base::EndsWith(dst, "/")) dst += '/'; + } + dst += name; + + // Ensure the directory hierarchy exists. + if (!MakeDirectoryHierarchy(android::base::Dirname(name))) { + error(1, errno, "couldn't create directory hierarchy for %s", dst.c_str()); + } + + // An entry in a zip file can just be a directory itself. + if (android::base::EndsWith(name, "/")) { + if (mkdir(name.c_str(), entry.unix_mode) == -1) { + // If the directory already exists, that's fine. + if (errno == EEXIST) { + struct stat sb; + if (stat(name.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return; + } + error(1, errno, "couldn't extract directory %s", dst.c_str()); + } + return; + } + + // Create the file. + int fd = open(name.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC | O_EXCL, entry.unix_mode); + if (fd == -1 && errno == EEXIST) { + if (overwrite_mode == kNever) return; + if (overwrite_mode == kPrompt && !PromptOverwrite(dst)) return; + // Either overwrite_mode is kAlways or the user consented to this specific case. + fd = open(name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, entry.unix_mode); + } + if (fd == -1) error(1, errno, "couldn't create file %s", dst.c_str()); + + // Actually extract into the file. + if (!flag_q) printf(" inflating: %s\n", dst.c_str()); + int err = ExtractEntryToFile(zah, &entry, fd); + if (err < 0) error(1, 0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err)); + close(fd); +} + +static void ListOne(const ZipEntry& entry, const std::string& name) { + tm t = entry.GetModificationTime(); + char time[32]; + snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1, + t.tm_mday, t.tm_hour, t.tm_min); + if (flag_v) { + printf("%8d %s %7d %3d%% %s %08x %s\n", entry.uncompressed_length, + (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length, + CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32, + name.c_str()); + } else { + printf("%9d %s %s\n", entry.uncompressed_length, time, name.c_str()); + } +} + +static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) { + if (flag_l || flag_v) { + // -l or -lv or -lq or -v. + ListOne(entry, name); + } else { + // Actually extract. + if (flag_p) { + ExtractToPipe(zah, entry, name); + } else { + ExtractOne(zah, entry, name); + } + } + total_uncompressed_length += entry.uncompressed_length; + total_compressed_length += entry.compressed_length; + ++file_count; +} + +static void ProcessAll(ZipArchiveHandle zah) { + MaybeShowHeader(); + + // libziparchive iteration order doesn't match the central directory. + // We could sort, but that would cost extra and wouldn't match either. + void* cookie; + int err = StartIteration(zah, &cookie, nullptr, nullptr); + if (err != 0) { + error(1, 0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err)); + } + + ZipEntry entry; + ZipString string; + while ((err = Next(cookie, &entry, &string)) >= 0) { + std::string name(string.name, string.name + string.name_length); + if (!Filter(name)) ProcessOne(zah, entry, name); + } + + if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err)); + EndIteration(cookie); + + MaybeShowFooter(); +} + +static void ShowHelp(bool full) { + fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n"); + if (!full) exit(EXIT_FAILURE); + + printf( + "\n" + "Extract FILEs from ZIP archive. Default is all files.\n" + "\n" + "-d DIR Extract into DIR\n" + "-l List contents (-lq excludes archive name, -lv is verbose)\n" + "-n Never overwrite files (default: prompt)\n" + "-o Always overwrite files\n" + "-p Pipe to stdout\n" + "-q Quiet\n" + "-v List contents verbosely\n" + "-x FILE Exclude files\n"); + exit(EXIT_SUCCESS); +} + +int main(int argc, char* argv[]) { + static struct option opts[] = { + {"help", no_argument, 0, 'h'}, + }; + bool saw_x = false; + int opt; + while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) { + switch (opt) { + case 'd': + flag_d = optarg; + break; + case 'h': + ShowHelp(true); + break; + case 'l': + flag_l = true; + break; + case 'n': + overwrite_mode = kNever; + break; + case 'o': + overwrite_mode = kAlways; + break; + case 'p': + flag_p = flag_q = true; + break; + case 'q': + flag_q = true; + break; + case 'v': + flag_v = true; + break; + case 'x': + saw_x = true; + break; + case 1: + // -x swallows all following arguments, so we use '-' in the getopt + // string and collect files here. + if (!archive_name) { + archive_name = optarg; + } else if (saw_x) { + excludes.insert(optarg); + } else { + includes.insert(optarg); + } + break; + default: + ShowHelp(false); + } + } + + if (!archive_name) error(1, 0, "missing archive filename"); + + // We can't support "-" to unzip from stdin because libziparchive relies on mmap. + ZipArchiveHandle zah; + int32_t err; + if ((err = OpenArchive(archive_name, &zah)) != 0) { + error(1, 0, "couldn't open %s: %s", archive_name, ErrorCodeString(err)); + } + + // Implement -d by changing into that directory. + // We'll create implicit directories based on paths in the zip file, but we + // require that the -d directory already exists. + if (flag_d && chdir(flag_d) == -1) error(1, errno, "couldn't chdir to %s", flag_d); + + ProcessAll(zah); + + CloseArchive(zah); + return 0; +}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc index d0bbd72..4559b32 100644 --- a/libziparchive/zip_archive.cc +++ b/libziparchive/zip_archive.cc
@@ -27,6 +27,7 @@ #include <limits.h> #include <stdlib.h> #include <string.h> +#include <time.h> #include <unistd.h> #include <memory> @@ -48,6 +49,10 @@ using android::base::get_unaligned; +// Used to turn on crc checks - verify that the content CRC matches the values +// specified in the local file header and the central directory. +static const bool kCrcChecksEnabled = false; + // This is for windows. If we don't open a file in binary mode, weird // things will happen. #ifndef O_BINARY @@ -57,68 +62,6 @@ // The maximum number of bytes to scan backwards for the EOCD start. static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord); -static const char* kErrorMessages[] = { - "Unknown return code.", - "Iteration ended", - "Zlib error", - "Invalid file", - "Invalid handle", - "Duplicate entries in archive", - "Empty archive", - "Entry not found", - "Invalid offset", - "Inconsistent information", - "Invalid entry name", - "I/O Error", - "File mapping failed" -}; - -static const int32_t kErrorMessageUpperBound = 0; - -static const int32_t kIterationEnd = -1; - -// We encountered a Zlib error when inflating a stream from this file. -// Usually indicates file corruption. -static const int32_t kZlibError = -2; - -// The input file cannot be processed as a zip archive. Usually because -// it's too small, too large or does not have a valid signature. -static const int32_t kInvalidFile = -3; - -// An invalid iteration / ziparchive handle was passed in as an input -// argument. -static const int32_t kInvalidHandle = -4; - -// The zip archive contained two (or possibly more) entries with the same -// name. -static const int32_t kDuplicateEntry = -5; - -// The zip archive contains no entries. -static const int32_t kEmptyArchive = -6; - -// The specified entry was not found in the archive. -static const int32_t kEntryNotFound = -7; - -// The zip archive contained an invalid local file header pointer. -static const int32_t kInvalidOffset = -8; - -// The zip archive contained inconsistent entry information. This could -// be because the central directory & local file header did not agree, or -// if the actual uncompressed length or crc32 do not match their declared -// values. -static const int32_t kInconsistentInformation = -9; - -// An invalid entry name was encountered. -static const int32_t kInvalidEntryName = -10; - -// An I/O related system call (read, lseek, ftruncate, map) failed. -static const int32_t kIoError = -11; - -// We were not able to mmap the central directory or entry contents. -static const int32_t kMmapFailed = -12; - -static const int32_t kErrorMessageLowerBound = -13; - /* * A Read-only Zip archive. * @@ -172,8 +115,7 @@ * Convert a ZipEntry to a hash table index, verifying that it's in a * valid range. */ -static int64_t EntryToIndex(const ZipString* hash_table, - const uint32_t hash_table_size, +static int64_t EntryToIndex(const ZipString* hash_table, const uint32_t hash_table_size, const ZipString& name) { const uint32_t hash = ComputeHash(name); @@ -194,7 +136,7 @@ /* * Add a new entry to the hash table. */ -static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size, +static int32_t AddToHash(ZipString* hash_table, const uint64_t hash_table_size, const ZipString& name) { const uint64_t hash = ComputeHash(name); uint32_t ent = hash & (hash_table_size - 1); @@ -218,13 +160,12 @@ } static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive, - off64_t file_length, off64_t read_amount, - uint8_t* scan_buffer) { + off64_t file_length, off64_t read_amount, uint8_t* scan_buffer) { const off64_t search_start = file_length - read_amount; - if(!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) { - ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed", - static_cast<int64_t>(read_amount), static_cast<int64_t>(search_start)); + if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) { + ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed", static_cast<int64_t>(read_amount), + static_cast<int64_t>(search_start)); return kIoError; } @@ -255,8 +196,7 @@ * Verify that there's no trailing space at the end of the central directory * and its comment. */ - const off64_t calculated_length = eocd_offset + sizeof(EocdRecord) - + eocd->comment_length; + const off64_t calculated_length = eocd_offset + sizeof(EocdRecord) + eocd->comment_length; if (calculated_length != file_length) { ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory", static_cast<int64_t>(file_length - calculated_length)); @@ -269,7 +209,7 @@ */ if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) { ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")", - eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset)); + eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset)); #if defined(__ANDROID__) if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) { android_errorWriteLog(0x534e4554, "31251826"); @@ -282,8 +222,8 @@ return kEmptyArchive; } - ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32, - eocd->num_records, eocd->cd_size, eocd->cd_start_offset); + ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32, eocd->num_records, + eocd->cd_size, eocd->cd_start_offset); /* * It all looks good. Create a mapping for the CD, and set the fields @@ -312,7 +252,6 @@ * num_entries */ static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) { - // Test file length. We use lseek64 to make sure the file // is small enough to be a zip file (Its size must be less than // 0xffffffff bytes). @@ -349,8 +288,8 @@ } std::vector<uint8_t> scan_buffer(read_amount); - int32_t result = MapCentralDirectory0(debug_file_name, archive, file_length, read_amount, - scan_buffer.data()); + int32_t result = + MapCentralDirectory0(debug_file_name, archive, file_length, read_amount, scan_buffer.data()); return result; } @@ -371,8 +310,8 @@ * least one unused entry to avoid an infinite loop during creation. */ archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3); - archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size, - sizeof(ZipString))); + archive->hash_table = + reinterpret_cast<ZipString*>(calloc(archive->hash_table_size, sizeof(ZipString))); if (archive->hash_table == nullptr) { ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu", archive->hash_table_size, sizeof(ZipString)); @@ -394,8 +333,7 @@ return -1; } - const CentralDirectoryRecord* cdr = - reinterpret_cast<const CentralDirectoryRecord*>(ptr); + const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr); if (cdr->record_signature != CentralDirectoryRecord::kSignature) { ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i); return -1; @@ -404,7 +342,7 @@ const off64_t local_header_offset = cdr->local_file_header_offset; if (local_header_offset >= archive->directory_offset) { ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16, - static_cast<int64_t>(local_header_offset), i); + static_cast<int64_t>(local_header_offset), i); return -1; } @@ -414,8 +352,10 @@ const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord); if (file_name + file_name_length > cd_end) { - ALOGW("Zip: file name boundary exceeds the central directory range, file_name_length: " - "%" PRIx16 ", cd_length: %zu", file_name_length, cd_length); + ALOGW( + "Zip: file name boundary exceeds the central directory range, file_name_length: " + "%" PRIx16 ", cd_length: %zu", + file_name_length, cd_length); return -1; } /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */ @@ -427,8 +367,7 @@ ZipString entry_name; entry_name.name = file_name; entry_name.name_length = file_name_length; - const int add_result = AddToHash(archive->hash_table, - archive->hash_table_size, entry_name); + const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name); if (add_result != 0) { ALOGW("Zip: Error adding entry to hash table %d", add_result); return add_result; @@ -436,8 +375,7 @@ ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length; if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) { - ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, - ptr - cd_ptr, cd_length, i); + ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, ptr - cd_ptr, cd_length, i); return -1; } } @@ -462,8 +400,7 @@ return 0; } -static int32_t OpenArchiveInternal(ZipArchive* archive, - const char* debug_file_name) { +static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) { int32_t result = -1; if ((result = MapCentralDirectory(debug_file_name, archive)) != 0) { return result; @@ -476,8 +413,8 @@ return 0; } -int32_t OpenArchiveFd(int fd, const char* debug_file_name, - ZipArchiveHandle* handle, bool assume_ownership) { +int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle, + bool assume_ownership) { ZipArchive* archive = new ZipArchive(fd, assume_ownership); *handle = archive; return OpenArchiveInternal(archive, debug_file_name); @@ -497,7 +434,7 @@ } int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name, - ZipArchiveHandle *handle) { + ZipArchiveHandle* handle) { ZipArchive* archive = new ZipArchive(address, length); *handle = archive; return OpenArchiveInternal(archive, debug_file_name); @@ -512,8 +449,7 @@ delete archive; } -static int32_t UpdateEntryFromDataDescriptor(MappedZipFile& mapped_zip, - ZipEntry *entry) { +static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry) { uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)]; if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) { return kIoError; @@ -523,15 +459,22 @@ const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0; const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset); - entry->crc32 = descriptor->crc32; - entry->compressed_length = descriptor->compressed_size; - entry->uncompressed_length = descriptor->uncompressed_size; + // Validate that the values in the data descriptor match those in the central + // directory. + if (entry->compressed_length != descriptor->compressed_size || + entry->uncompressed_length != descriptor->uncompressed_size || + entry->crc32 != descriptor->crc32) { + ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32 + "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}", + entry->compressed_length, entry->uncompressed_length, entry->crc32, + descriptor->compressed_size, descriptor->uncompressed_size, descriptor->crc32); + return kInconsistentInformation; + } return 0; } -static int32_t FindEntry(const ZipArchive* archive, const int ent, - ZipEntry* data) { +static int32_t FindEntry(const ZipArchive* archive, const int ent, ZipEntry* data) { const uint16_t nameLen = archive->hash_table[ent].name_length; // Recover the start of the central directory entry from the filename @@ -549,8 +492,7 @@ return kInvalidOffset; } - const CentralDirectoryRecord *cdr = - reinterpret_cast<const CentralDirectoryRecord*>(ptr); + const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr); // The offset of the start of the central directory in the zipfile. // We keep this lying around so that we can sanity check all our lengths @@ -578,46 +520,62 @@ uint8_t lfh_buf[sizeof(LocalFileHeader)]; if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) { ALOGW("Zip: failed reading lfh name from offset %" PRId64, - static_cast<int64_t>(local_header_offset)); + static_cast<int64_t>(local_header_offset)); return kIoError; } - const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf); + const LocalFileHeader* lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf); if (lfh->lfh_signature != LocalFileHeader::kSignature) { ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64, - static_cast<int64_t>(local_header_offset)); + static_cast<int64_t>(local_header_offset)); return kInvalidOffset; } // Paranoia: Match the values specified in the local file header // to those specified in the central directory. - // Verify that the central directory and local file header have the same general purpose bit - // flags set. - if (lfh->gpb_flags != cdr->gpb_flags) { - ALOGW("Zip: gpb flag mismatch. expected {%04" PRIx16 "}, was {%04" PRIx16 "}", + // Warn if central directory and local file header don't agree on the use + // of a trailing Data Descriptor. The reference implementation is inconsistent + // and appears to use the LFH value during extraction (unzip) but the CD value + // while displayng information about archives (zipinfo). The spec remains + // silent on this inconsistency as well. + // + // For now, always use the version from the LFH but make sure that the values + // specified in the central directory match those in the data descriptor. + // + // NOTE: It's also worth noting that unzip *does* warn about inconsistencies in + // bit 11 (EFS: The language encoding flag, marking that filename and comment are + // encoded using UTF-8). This implementation does not check for the presence of + // that flag and always enforces that entry names are valid UTF-8. + if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) { + ALOGW("Zip: gpb flag mismatch at bit 3. expected {%04" PRIx16 "}, was {%04" PRIx16 "}", cdr->gpb_flags, lfh->gpb_flags); - return kInconsistentInformation; } // If there is no trailing data descriptor, verify that the central directory and local file // header agree on the crc, compressed, and uncompressed sizes of the entry. if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) { data->has_data_descriptor = 0; - if (data->compressed_length != lfh->compressed_size - || data->uncompressed_length != lfh->uncompressed_size - || data->crc32 != lfh->crc32) { - ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 - ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}", - data->compressed_length, data->uncompressed_length, data->crc32, - lfh->compressed_size, lfh->uncompressed_size, lfh->crc32); + if (data->compressed_length != lfh->compressed_size || + data->uncompressed_length != lfh->uncompressed_size || data->crc32 != lfh->crc32) { + ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32 + "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}", + data->compressed_length, data->uncompressed_length, data->crc32, lfh->compressed_size, + lfh->uncompressed_size, lfh->crc32); return kInconsistentInformation; } } else { data->has_data_descriptor = 1; } + // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3. + if ((cdr->version_made_by >> 8) == 3) { + data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff; + } else { + data->unix_mode = 0777; + } + // Check that the local file header name matches the declared // name in the central directory. if (lfh->file_name_length == nameLen) { @@ -642,8 +600,8 @@ return kInconsistentInformation; } - const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader) - + lfh->file_name_length + lfh->extra_field_length; + const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader) + + lfh->file_name_length + lfh->extra_field_length; if (data_offset > cd_offset) { ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset)); return kInvalidOffset; @@ -651,16 +609,17 @@ if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) { ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")", - static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset)); + static_cast<int64_t>(data_offset), data->compressed_length, + static_cast<int64_t>(cd_offset)); return kInvalidOffset; } if (data->method == kCompressStored && - static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) { - ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")", - static_cast<int64_t>(data_offset), data->uncompressed_length, - static_cast<int64_t>(cd_offset)); - return kInvalidOffset; + static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) { + ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")", + static_cast<int64_t>(data_offset), data->uncompressed_length, + static_cast<int64_t>(cd_offset)); + return kInvalidOffset; } data->offset = data_offset; @@ -675,8 +634,7 @@ ZipString suffix; ZipArchive* archive; - IterationHandle(const ZipString* in_prefix, - const ZipString* in_suffix) { + IterationHandle(const ZipString* in_prefix, const ZipString* in_suffix) { if (in_prefix) { uint8_t* name_copy = new uint8_t[in_prefix->name_length]; memcpy(name_copy, in_prefix->name, in_prefix->name_length); @@ -703,8 +661,7 @@ } }; -int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, - const ZipString* optional_prefix, +int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix, const ZipString* optional_suffix) { ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle); @@ -717,7 +674,7 @@ cookie->position = 0; cookie->archive = archive; - *cookie_ptr = cookie ; + *cookie_ptr = cookie; return 0; } @@ -725,16 +682,14 @@ delete reinterpret_cast<IterationHandle*>(cookie); } -int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, - ZipEntry* data) { +int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data) { const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle); if (entryName.name_length == 0) { ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name); return kInvalidEntryName; } - const int64_t ent = EntryToIndex(archive->hash_table, - archive->hash_table_size, entryName); + const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName); if (ent < 0) { ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name); @@ -762,10 +717,8 @@ for (uint32_t i = currentOffset; i < hash_table_length; ++i) { if (hash_table[i].name != NULL && - (handle->prefix.name_length == 0 || - hash_table[i].StartsWith(handle->prefix)) && - (handle->suffix.name_length == 0 || - hash_table[i].EndsWith(handle->suffix))) { + (handle->prefix.name_length == 0 || hash_table[i].StartsWith(handle->prefix)) && + (handle->suffix.name_length == 0 || hash_table[i].EndsWith(handle->suffix))) { handle->position = (i + 1); const int error = FindEntry(archive, i, data); if (!error) { @@ -785,8 +738,10 @@ public: virtual bool Append(uint8_t* buf, size_t buf_size) = 0; virtual ~Writer() {} + protected: Writer() = default; + private: DISALLOW_COPY_AND_ASSIGN(Writer); }; @@ -796,14 +751,12 @@ // the data appended to it. class MemoryWriter : public Writer { public: - MemoryWriter(uint8_t* buf, size_t size) : Writer(), - buf_(buf), size_(size), bytes_written_(0) { - } + MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {} virtual bool Append(uint8_t* buf, size_t buf_size) override { if (bytes_written_ + buf_size > size_) { - ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", - size_, bytes_written_ + buf_size); + ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", size_, + bytes_written_ + buf_size); return false; } @@ -822,7 +775,6 @@ // The file will be truncated to the end of the written data. class FileWriter : public Writer { public: - // Creates a FileWriter for |fd| and prepare to write |entry| to it, // guaranteeing that the file descriptor is valid and that there's enough // space on the volume to write out the entry completely and that the file @@ -881,8 +833,8 @@ virtual bool Append(uint8_t* buf, size_t buf_size) override { if (total_bytes_written_ + buf_size > declared_length_) { - ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", - declared_length_, total_bytes_written_ + buf_size); + ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", declared_length_, + total_bytes_written_ + buf_size); return false; } @@ -895,13 +847,10 @@ return result; } + private: - FileWriter(const int fd, const size_t declared_length) : - Writer(), - fd_(fd), - declared_length_(declared_length), - total_bytes_written_(0) { - } + FileWriter(const int fd, const size_t declared_length) + : Writer(), fd_(fd), declared_length_(declared_length), total_bytes_written_(0) {} const int fd_; const size_t declared_length_; @@ -944,8 +893,7 @@ zerr = zlib_inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)", - ZLIB_VERSION); + ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION); } else { ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr); } @@ -954,13 +902,14 @@ } auto zstream_deleter = [](z_stream* stream) { - inflateEnd(stream); /* free up any allocated structures */ + inflateEnd(stream); /* free up any allocated structures */ }; std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter); const uint32_t uncompressed_length = entry->uncompressed_length; + uint64_t crc = 0; uint32_t compressed_length = entry->compressed_length; do { /* read as much as we can */ @@ -980,19 +929,19 @@ /* uncompress the data */ zerr = inflate(&zstream, Z_NO_FLUSH); if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", - zerr, zstream.next_in, zstream.avail_in, - zstream.next_out, zstream.avail_out); + ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, zstream.next_in, + zstream.avail_in, zstream.next_out, zstream.avail_out); return kZlibError; } /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) { + if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) { const size_t write_size = zstream.next_out - &write_buf[0]; if (!writer->Append(&write_buf[0], write_size)) { // The file might have declared a bogus length. return kInconsistentInformation; + } else { + crc = crc32(crc, &write_buf[0], write_size); } zstream.next_out = &write_buf[0]; @@ -1000,14 +949,19 @@ } } while (zerr == Z_OK); - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - // stream.adler holds the crc32 value for such streams. - *crc_out = zstream.adler; + // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS + // "feature" of zlib to tell it there won't be a zlib file header. zlib + // doesn't bother calculating the checksum in that scenario. We just do + // it ourselves above because there are no additional gains to be made by + // having zlib calculate it for us, since they do it by calling crc32 in + // the same manner that we have above. + *crc_out = crc; if (zstream.total_out != uncompressed_length || compressed_length != 0) { - ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", - zstream.total_out, uncompressed_length); + ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out, + uncompressed_length); return kInconsistentInformation; } @@ -1015,7 +969,7 @@ } static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, Writer* writer, - uint64_t *crc_out) { + uint64_t* crc_out) { static const uint32_t kBufSize = 32768; std::vector<uint8_t> buf(kBufSize); @@ -1045,8 +999,7 @@ return 0; } -int32_t ExtractToWriter(ZipArchiveHandle handle, - ZipEntry* entry, Writer* writer) { +int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, Writer* writer) { ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle); const uint16_t method = entry->method; off64_t data_offset = entry->offset; @@ -1066,15 +1019,14 @@ } if (!return_value && entry->has_data_descriptor) { - return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry); + return_value = ValidateDataDescriptor(archive->mapped_zip, entry); if (return_value) { return return_value; } } - // TODO: Fix this check by passing the right flags to inflate2 so that - // it calculates the CRC for us. - if (entry->crc32 != crc && false) { + // Validate that the CRC matches the calculated value. + if (kCrcChecksEnabled && (entry->crc32 != static_cast<uint32_t>(crc))) { ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc); return kInconsistentInformation; } @@ -1082,14 +1034,12 @@ return return_value; } -int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, - uint8_t* begin, uint32_t size) { +int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size) { std::unique_ptr<Writer> writer(new MemoryWriter(begin, size)); return ExtractToWriter(handle, entry, writer.get()); } -int32_t ExtractEntryToFile(ZipArchiveHandle handle, - ZipEntry* entry, int fd) { +int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd) { std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry)); if (writer.get() == nullptr) { return kIoError; @@ -1099,19 +1049,24 @@ } const char* ErrorCodeString(int32_t error_code) { - if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) { - return kErrorMessages[error_code * -1]; + // Make sure that the number of entries in kErrorMessages and ErrorCodes + // match. + static_assert((-kLastErrorCode + 1) == arraysize(kErrorMessages), + "(-kLastErrorCode + 1) != arraysize(kErrorMessages)"); + + const uint32_t idx = -error_code; + if (idx < arraysize(kErrorMessages)) { + return kErrorMessages[idx]; } - return kErrorMessages[0]; + return "Unknown return code"; } int GetFileDescriptor(const ZipArchiveHandle handle) { return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor(); } -ZipString::ZipString(const char* entry_name) - : name(reinterpret_cast<const uint8_t*>(entry_name)) { +ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) { size_t len = strlen(entry_name); CHECK_LE(len, static_cast<size_t>(UINT16_MAX)); name_length = static_cast<uint16_t>(len); @@ -1120,10 +1075,8 @@ #if !defined(_WIN32) class ProcessWriter : public Writer { public: - ProcessWriter(ProcessZipEntryFunction func, void* cookie) : Writer(), - proc_function_(func), - cookie_(cookie) { - } + ProcessWriter(ProcessZipEntryFunction func, void* cookie) + : Writer(), proc_function_(func), cookie_(cookie) {} virtual bool Append(uint8_t* buf, size_t buf_size) override { return proc_function_(buf, buf_size, cookie_); @@ -1140,7 +1093,7 @@ return ExtractToWriter(handle, entry, &writer); } -#endif //!defined(_WIN32) +#endif //! defined(_WIN32) int MappedZipFile::GetFileDescriptor() const { if (!has_fd_) { @@ -1183,8 +1136,7 @@ return true; } else { if (offset < 0 || offset > static_cast<off64_t>(data_length_)) { - ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n" , offset, - data_length_); + ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n", offset, data_length_); return false; } @@ -1195,7 +1147,7 @@ bool MappedZipFile::ReadData(uint8_t* buffer, size_t read_amount) { if (has_fd_) { - if(!android::base::ReadFully(fd_, buffer, read_amount)) { + if (!android::base::ReadFully(fd_, buffer, read_amount)) { ALOGE("Zip: read from %d failed\n", fd_); return false; } @@ -1221,7 +1173,6 @@ return false; } return ReadData(buf, len); - } void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) { @@ -1232,13 +1183,13 @@ bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset, size_t cd_size) { if (mapped_zip.HasFd()) { - if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(), - cd_start_offset, cd_size, true /* read only */)) { + if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(), cd_start_offset, + cd_size, true /* read only */)) { return false; } CHECK_EQ(directory_map->getDataLength(), cd_size); - central_directory.Initialize(directory_map->getDataPtr(), 0/*offset*/, cd_size); + central_directory.Initialize(directory_map->getDataPtr(), 0 /*offset*/, cd_size); } else { if (mapped_zip.GetBasePtr() == nullptr) { ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n"); @@ -1246,9 +1197,10 @@ } if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) > mapped_zip.GetFileLength()) { - ALOGE("Zip: Failed to map central directory, offset exceeds mapped memory region (" - "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")", - static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength()); + ALOGE( + "Zip: Failed to map central directory, offset exceeds mapped memory region (" + "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")", + static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength()); return false; } @@ -1256,3 +1208,17 @@ } return true; } + +tm ZipEntry::GetModificationTime() const { + tm t = {}; + + t.tm_hour = (mod_time >> 11) & 0x1f; + t.tm_min = (mod_time >> 5) & 0x3f; + t.tm_sec = (mod_time & 0x1f) << 1; + + t.tm_year = ((mod_time >> 25) & 0x7f) + 80; + t.tm_mon = ((mod_time >> 21) & 0xf) - 1; + t.tm_mday = (mod_time >> 16) & 0x1f; + + return t; +}
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h index ca42509..8b99bde 100644 --- a/libziparchive/zip_archive_common.h +++ b/libziparchive/zip_archive_common.h
@@ -57,6 +57,7 @@ uint32_t cd_start_offset; // Length of the central directory comment. uint16_t comment_length; + private: EocdRecord() = default; DISALLOW_COPY_AND_ASSIGN(EocdRecord); @@ -73,7 +74,7 @@ // The start of record signature. Must be |kSignature|. uint32_t record_signature; - // Tool version. Ignored by this implementation. + // Source tool version. Top byte gives source OS. uint16_t version_made_by; // Tool version. Ignored by this implementation. uint16_t version_needed; @@ -106,11 +107,12 @@ uint16_t file_start_disk; // File attributes. Ignored by this implementation. uint16_t internal_file_attributes; - // File attributes. Ignored by this implementation. + // File attributes. For archives created on Unix, the top bits are the mode. uint32_t external_file_attributes; // The offset to the local file header for this entry, from the // beginning of this archive. uint32_t local_file_header_offset; + private: CentralDirectoryRecord() = default; DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord); @@ -149,6 +151,7 @@ // The length of the extra field info (in bytes). This data // will appear immediately after the entry file name. uint16_t extra_field_length; + private: LocalFileHeader() = default; DISALLOW_COPY_AND_ASSIGN(LocalFileHeader); @@ -164,6 +167,7 @@ uint32_t compressed_size; // Uncompressed size of the entry. uint32_t uncompressed_size; + private: DataDescriptor() = default; DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h index 971db4f..840f1af 100644 --- a/libziparchive/zip_archive_private.h +++ b/libziparchive/zip_archive_private.h
@@ -26,24 +26,83 @@ #include <utils/FileMap.h> #include <ziparchive/zip_archive.h> +#include "android-base/macros.h" + +static const char* kErrorMessages[] = { + "Success", + "Iteration ended", + "Zlib error", + "Invalid file", + "Invalid handle", + "Duplicate entries in archive", + "Empty archive", + "Entry not found", + "Invalid offset", + "Inconsistent information", + "Invalid entry name", + "I/O error", + "File mapping failed", +}; + +enum ErrorCodes : int32_t { + kIterationEnd = -1, + + // We encountered a Zlib error when inflating a stream from this file. + // Usually indicates file corruption. + kZlibError = -2, + + // The input file cannot be processed as a zip archive. Usually because + // it's too small, too large or does not have a valid signature. + kInvalidFile = -3, + + // An invalid iteration / ziparchive handle was passed in as an input + // argument. + kInvalidHandle = -4, + + // The zip archive contained two (or possibly more) entries with the same + // name. + kDuplicateEntry = -5, + + // The zip archive contains no entries. + kEmptyArchive = -6, + + // The specified entry was not found in the archive. + kEntryNotFound = -7, + + // The zip archive contained an invalid local file header pointer. + kInvalidOffset = -8, + + // The zip archive contained inconsistent entry information. This could + // be because the central directory & local file header did not agree, or + // if the actual uncompressed length or crc32 do not match their declared + // values. + kInconsistentInformation = -9, + + // An invalid entry name was encountered. + kInvalidEntryName = -10, + + // An I/O related system call (read, lseek, ftruncate, map) failed. + kIoError = -11, + + // We were not able to mmap the central directory or entry contents. + kMmapFailed = -12, + + kLastErrorCode = kMmapFailed, +}; class MappedZipFile { public: - explicit MappedZipFile(const int fd) : - has_fd_(true), - fd_(fd), - base_ptr_(nullptr), - data_length_(0), - read_pos_(0) {} + explicit MappedZipFile(const int fd) + : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0), read_pos_(0) {} - explicit MappedZipFile(void* address, size_t length) : - has_fd_(false), - fd_(-1), - base_ptr_(address), - data_length_(static_cast<off64_t>(length)), - read_pos_(0) {} + explicit MappedZipFile(void* address, size_t length) + : has_fd_(false), + fd_(-1), + base_ptr_(address), + data_length_(static_cast<off64_t>(length)), + read_pos_(0) {} - bool HasFd() const {return has_fd_;} + bool HasFd() const { return has_fd_; } int GetFileDescriptor() const; @@ -74,13 +133,11 @@ class CentralDirectory { public: - CentralDirectory(void) : - base_ptr_(nullptr), - length_(0) {} + CentralDirectory(void) : base_ptr_(nullptr), length_(0) {} - const uint8_t* GetBasePtr() const {return base_ptr_;} + const uint8_t* GetBasePtr() const { return base_ptr_; } - size_t GetMapLength() const {return length_;} + size_t GetMapLength() const { return length_; } void Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size); @@ -109,25 +166,25 @@ uint32_t hash_table_size; ZipString* hash_table; - ZipArchive(const int fd, bool assume_ownership) : - mapped_zip(fd), - close_file(assume_ownership), - directory_offset(0), - central_directory(), - directory_map(new android::FileMap()), - num_entries(0), - hash_table_size(0), - hash_table(nullptr) {} + ZipArchive(const int fd, bool assume_ownership) + : mapped_zip(fd), + close_file(assume_ownership), + directory_offset(0), + central_directory(), + directory_map(new android::FileMap()), + num_entries(0), + hash_table_size(0), + hash_table(nullptr) {} - ZipArchive(void* address, size_t length) : - mapped_zip(address, length), - close_file(false), - directory_offset(0), - central_directory(), - directory_map(new android::FileMap()), - num_entries(0), - hash_table_size(0), - hash_table(nullptr) {} + ZipArchive(void* address, size_t length) + : mapped_zip(address, length), + close_file(false), + directory_offset(0), + central_directory(), + directory_map(new android::FileMap()), + num_entries(0), + hash_table_size(0), + hash_table(nullptr) {} ~ZipArchive() { if (close_file && mapped_zip.GetFileDescriptor() >= 0) { @@ -139,7 +196,6 @@ bool InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset, size_t cd_size); - }; #endif // LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc index 3f336a6..50352ef 100644 --- a/libziparchive/zip_archive_stream_entry.cc +++ b/libziparchive/zip_archive_stream_entry.cc
@@ -162,8 +162,7 @@ int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)", - ZLIB_VERSION); + ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION); } else { ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr); } @@ -193,13 +192,14 @@ bool ZipArchiveStreamEntryCompressed::Verify() { return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 && - crc32_ == computed_crc32_; + crc32_ == computed_crc32_; } const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() { if (z_stream_.avail_out == 0) { z_stream_.next_out = out_.data(); - z_stream_.avail_out = out_.size();; + z_stream_.avail_out = out_.size(); + ; } while (true) { @@ -226,9 +226,8 @@ int zerr = inflate(&z_stream_, Z_NO_FLUSH); if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", - zerr, z_stream_.next_in, z_stream_.avail_in, - z_stream_.next_out, z_stream_.avail_out); + ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, z_stream_.next_in, + z_stream_.avail_in, z_stream_.next_out, z_stream_.avail_out); return nullptr; } @@ -276,8 +275,8 @@ return length_ == 0; } -ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create( - ZipArchiveHandle handle, const ZipEntry& entry) { +ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle, + const ZipEntry& entry) { ZipArchiveStreamEntry* stream = nullptr; if (entry.method != kCompressStored) { stream = new ZipArchiveStreamEntryCompressed(handle); @@ -292,8 +291,8 @@ return stream; } -ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw( - ZipArchiveHandle handle, const ZipEntry& entry) { +ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle, + const ZipEntry& entry) { ZipArchiveStreamEntry* stream = nullptr; if (entry.method == kCompressStored) { // Not compressed, don't need to do anything special.
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc index 3035a5e..753bd44 100644 --- a/libziparchive/zip_archive_test.cc +++ b/libziparchive/zip_archive_test.cc
@@ -14,6 +14,8 @@ * limitations under the License. */ +#include "zip_archive_private.h" + #include <errno.h> #include <fcntl.h> #include <getopt.h> @@ -42,21 +44,13 @@ static const std::string kBadFilenameZip = "bad_filename.zip"; static const std::string kUpdateZip = "dummy-update.zip"; -static const std::vector<uint8_t> kATxtContents { - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - '\n' -}; +static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', + 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'}; -static const std::vector<uint8_t> kATxtContentsCompressed { - 'K', 'L', 'J', 'N', 'I', 'M', 'K', 207, 'H', - 132, 210, '\\', '\0' -}; +static const std::vector<uint8_t> kATxtContentsCompressed{'K', 'L', 'J', 'N', 'I', 'M', 'K', + 207, 'H', 132, 210, '\\', '\0'}; -static const std::vector<uint8_t> kBTxtContents { - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - '\n' -}; +static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'}; static const std::string kATxtName("a.txt"); static const std::string kBTxtName("b.txt"); @@ -65,14 +59,12 @@ static const std::string kLargeCompressTxtName("compress.txt"); static const std::string kLargeUncompressTxtName("uncompress.txt"); -static int32_t OpenArchiveWrapper(const std::string& name, - ZipArchiveHandle* handle) { +static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) { const std::string abs_path = test_data_dir + "/" + name; return OpenArchive(abs_path.c_str(), handle); } -static void AssertNameEquals(const std::string& name_str, - const ZipString& name) { +static void AssertNameEquals(const std::string& name_str, const ZipString& name) { ASSERT_EQ(name_str.size(), name.name_length); ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length)); } @@ -343,47 +335,36 @@ } static const uint32_t kEmptyEntriesZip[] = { - 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, - 0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, - 0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b, - 0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000, - 0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974, - 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400, - 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 }; + 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000, + 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875, + 0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863, + 0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081, + 0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, + 0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000}; // This is a zip file containing a single entry (ab.txt) that contains // 90072 repetitions of the string "ab\n" and has an uncompressed length // of 270216 bytes. static const uint16_t kAbZip[] = { - 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, - 0x2cda, 0x011b, 0x0000, 0x1f88, 0x0004, 0x0006, 0x001c, 0x6261, - 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09, - 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, - 0xc2ed, 0x0d31, 0x0000, 0x030c, 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, - 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, - 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, - 0x1403, 0x0000, 0x0800, 0xd200, 0x9851, 0xb046, 0xdac4, 0x1b2c, - 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100, - 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, - 0x0554, 0x0300, 0x097c, 0x553a, 0x7875, 0x000b, 0x0401, 0x4289, - 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100, - 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000 -}; + 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88, + 0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09, + 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c, + 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, + 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200, + 0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100, + 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a, + 0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100, + 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000}; static const std::string kAbTxtName("ab.txt"); static const size_t kAbUncompressedSize = 270216; @@ -404,7 +385,6 @@ uint8_t buffer[1]; ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1)); - TemporaryFile tmp_output_file; ASSERT_NE(-1, tmp_output_file.fd); ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd)); @@ -418,7 +398,7 @@ TemporaryFile tmp_file; ASSERT_NE(-1, tmp_file.fd); ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip), - sizeof(kAbZip) - 1)); + sizeof(kAbZip) - 1)); ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle)); @@ -446,8 +426,7 @@ // the same as the memory buffer we extracted directly to. std::vector<uint8_t> file_contents(kAbUncompressedSize); ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET)); - ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], - file_contents.size())); + ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size())); ASSERT_EQ(file_contents, buffer); for (int i = 0; i < 90072; ++i) { @@ -463,7 +442,7 @@ ASSERT_NE(-1, tmp_file.fd); // Create a file with 8 bytes of random garbage. - static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' }; + static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'}; ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip))); ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer))); @@ -474,7 +453,7 @@ TEST(ziparchive, ExtractToFile) { TemporaryFile tmp_file; ASSERT_NE(-1, tmp_file.fd); - const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' }; + const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'}; const size_t data_size = sizeof(data); ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size)); @@ -488,7 +467,6 @@ ASSERT_EQ(0, FindEntry(handle, name, &entry)); ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd)); - // Assert that the first 8 bytes of the file haven't been clobbered. uint8_t read_buffer[data_size]; ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET)); @@ -497,10 +475,9 @@ // Assert that the remainder of the file contains the incompressed data. std::vector<uint8_t> uncompressed_data(entry.uncompressed_length); - ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), - entry.uncompressed_length)); - ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), - kATxtContents.size())); + ASSERT_TRUE( + android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), entry.uncompressed_length)); + ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size())); // Assert that the total length of the file is sane ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()), @@ -517,7 +494,7 @@ // Memory map the file first and open the archive from the memory region. android::FileMap file_map; - file_map.create(zip_path.c_str(), fd, 0/*offset*/, sb.st_size, true); + file_map.create(zip_path.c_str(), fd, 0 /*offset*/, sb.st_size, true); ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveFromMemory(file_map.getDataPtr(), file_map.getDataLength(), zip_path.c_str(), &handle)); @@ -533,9 +510,8 @@ } #endif -static void ZipArchiveStreamTest( - ZipArchiveHandle& handle, const std::string& entry_name, bool raw, - bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) { +static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw, + bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) { ZipString name; SetZipString(&name, entry_name); ASSERT_EQ(0, FindEntry(handle, name, entry)); @@ -564,9 +540,9 @@ ASSERT_EQ(total_size, read_data->size()); } -static void ZipArchiveStreamTestUsingContents( - const std::string& zip_file, const std::string& entry_name, - const std::vector<uint8_t>& contents, bool raw) { +static void ZipArchiveStreamTestUsingContents(const std::string& zip_file, + const std::string& entry_name, + const std::vector<uint8_t>& contents, bool raw) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle)); @@ -580,7 +556,8 @@ CloseArchive(handle); } -static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file, const std::string& entry_name) { +static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file, + const std::string& entry_name) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle)); @@ -642,6 +619,104 @@ CloseArchive(handle); } +// Generated using the following Java program: +// public static void main(String[] foo) throws Exception { +// FileOutputStream fos = new +// FileOutputStream("/tmp/data_descriptor.zip"); +// ZipOutputStream zos = new ZipOutputStream(fos); +// ZipEntry ze = new ZipEntry("name"); +// ze.setMethod(ZipEntry.DEFLATED); +// zos.putNextEntry(ze); +// zos.write("abdcdefghijk".getBytes()); +// zos.closeEntry(); +// zos.close(); +// } +// +// cat /tmp/data_descriptor.zip | xxd -i +// +static const std::vector<uint8_t> kDataDescriptorZipFile{ + 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61, + 0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00, + //[sig---------------], [crc32---------------], [csize---------------], [size----------------] + 0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, + 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61, + 0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00}; + +// The offsets of the data descriptor in this file, so we can mess with +// them later in the test. +static constexpr uint32_t kDataDescriptorOffset = 48; +static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8; +static constexpr uint32_t kSizeOffset = kCSizeOffset + 4; + +static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data, + std::vector<uint8_t>* entry_out, int32_t* error_code_out) { + TemporaryFile tmp_file; + ASSERT_NE(-1, tmp_file.fd); + ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size())); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle)); + + // This function expects a variant of kDataDescriptorZipFile, for look for + // an entry whose name is "name" and whose size is 12 (contents = + // "abdcdefghijk"). + ZipEntry entry; + ZipString empty_name; + SetZipString(&empty_name, "name"); + + ASSERT_EQ(0, FindEntry(handle, empty_name, &entry)); + ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length); + + entry_out->resize(12); + (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12); + + CloseArchive(handle); +} + +TEST(ziparchive, ValidDataDescriptors) { + std::vector<uint8_t> entry; + int32_t error_code = 0; + ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code); + + ASSERT_EQ(0, error_code); + ASSERT_EQ(12u, entry.size()); + ASSERT_EQ('a', entry[0]); + ASSERT_EQ('k', entry[11]); +} + +TEST(ziparchive, InvalidDataDescriptors) { + std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile; + invalid_csize[kCSizeOffset] = 0xfe; + + std::vector<uint8_t> entry; + int32_t error_code = 0; + ExtractEntryToMemory(invalid_csize, &entry, &error_code); + + ASSERT_EQ(kInconsistentInformation, error_code); + + std::vector<uint8_t> invalid_size = kDataDescriptorZipFile; + invalid_csize[kSizeOffset] = 0xfe; + + error_code = 0; + entry.clear(); + ExtractEntryToMemory(invalid_csize, &entry, &error_code); + + ASSERT_EQ(kInconsistentInformation, error_code); +} + +TEST(ziparchive, ErrorCodeString) { + ASSERT_STREQ("Success", ErrorCodeString(0)); + + // Out of bounds. + ASSERT_STREQ("Unknown return code", ErrorCodeString(1)); + ASSERT_STREQ("Unknown return code", ErrorCodeString(-13)); + + ASSERT_STREQ("I/O error", ErrorCodeString(kIoError)); +} + // A zip file whose local file header at offset zero is corrupted. // // --------------- @@ -694,10 +769,8 @@ int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); - static struct option options[] = { - { "test_data_dir", required_argument, nullptr, 't' }, - { nullptr, 0, nullptr, 0 } - }; + static struct option options[] = {{"test_data_dir", required_argument, nullptr, 't'}, + {nullptr, 0, nullptr, 0}}; while (true) { int option_index;
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc index 6d28bdb..6ad3366 100644 --- a/libziparchive/zip_writer.cc +++ b/libziparchive/zip_writer.cc
@@ -16,11 +16,11 @@ #include "ziparchive/zip_writer.h" -#include <cstdio> #include <sys/param.h> #include <sys/stat.h> #include <zlib.h> -#define DEF_MEM_LEVEL 8 // normally in zutil.h? +#include <cstdio> +#define DEF_MEM_LEVEL 8 // normally in zutil.h? #include <memory> #include <vector> @@ -33,13 +33,13 @@ #include "zip_archive_common.h" #if !defined(powerof2) -#define powerof2(x) ((((x)-1)&(x))==0) +#define powerof2(x) ((((x)-1) & (x)) == 0) #endif /* Zip compression methods we support */ enum { - kCompressStored = 0, // no compression - kCompressDeflated = 8, // standard deflate + kCompressStored = 0, // no compression + kCompressDeflated = 8, // standard deflate }; // Size of the output buffer used for compression. @@ -67,10 +67,7 @@ static const int32_t kInvalidAlignment = -6; static const char* sErrorCodes[] = { - "Invalid state", - "IO error", - "Invalid entry name", - "Zlib error", + "Invalid state", "IO error", "Invalid entry name", "Zlib error", }; const char* ZipWriter::ErrorCodeString(int32_t error_code) { @@ -85,9 +82,13 @@ delete stream; } -ZipWriter::ZipWriter(FILE* f) : file_(f), seekable_(false), current_offset_(0), - state_(State::kWritingZip), z_stream_(nullptr, DeleteZStream), - buffer_(kBufSize) { +ZipWriter::ZipWriter(FILE* f) + : file_(f), + seekable_(false), + current_offset_(0), + state_(State::kWritingZip), + z_stream_(nullptr, DeleteZStream), + buffer_(kBufSize) { // Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls // will fail as well. struct stat file_stats; @@ -96,13 +97,14 @@ } } -ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_), - seekable_(writer.seekable_), - current_offset_(writer.current_offset_), - state_(writer.state_), - files_(std::move(writer.files_)), - z_stream_(std::move(writer.z_stream_)), - buffer_(std::move(writer.buffer_)){ +ZipWriter::ZipWriter(ZipWriter&& writer) + : file_(writer.file_), + seekable_(writer.seekable_), + current_offset_(writer.current_offset_), + state_(writer.state_), + files_(std::move(writer.files_)), + z_stream_(std::move(writer.z_stream_)), + buffer_(std::move(writer.buffer_)) { writer.file_ = nullptr; writer.state_ = State::kError; } @@ -154,10 +156,10 @@ struct tm* ptm; #if !defined(_WIN32) - struct tm tm_result; - ptm = localtime_r(&when, &tm_result); + struct tm tm_result; + ptm = localtime_r(&when, &tm_result); #else - ptm = localtime(&when); + ptm = localtime(&when); #endif int year = ptm->tm_year; @@ -193,8 +195,8 @@ dst->extra_field_length = src.padding_length; } -int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags, - time_t time, uint32_t alignment) { +int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags, time_t time, + uint32_t alignment) { if (state_ != State::kWritingZip) { return kInvalidState; } @@ -252,9 +254,8 @@ return HandleError(kIoError); } - if (file_entry.padding_length != 0 && - fwrite(zero_padding.data(), 1, file_entry.padding_length, file_) - != file_entry.padding_length) { + if (file_entry.padding_length != 0 && fwrite(zero_padding.data(), 1, file_entry.padding_length, + file_) != file_entry.padding_length) { return HandleError(kIoError); } @@ -292,7 +293,7 @@ CHECK(state_ == State::kWritingZip); // Initialize the z_stream for compression. - z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream); + z_stream_ = std::unique_ptr<z_stream, void (*)(z_stream*)>(new z_stream(), DeleteZStream); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" @@ -331,8 +332,8 @@ return result; } - current_file_entry_.crc32 = crc32(current_file_entry_.crc32, - reinterpret_cast<const Bytef*>(data), len); + current_file_entry_.crc32 = + crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len); current_file_entry_.uncompressed_size += len; return kNoError; }
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc index 5b526a4..c284273 100644 --- a/libziparchive/zip_writer_test.cc +++ b/libziparchive/zip_writer_test.cc
@@ -14,8 +14,8 @@ * limitations under the License. */ -#include "ziparchive/zip_archive.h" #include "ziparchive/zip_writer.h" +#include "ziparchive/zip_archive.h" #include <android-base/test_utils.h> #include <gtest/gtest.h> @@ -135,17 +135,6 @@ CloseArchive(handle); } -static void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) { - memset(tm, 0, sizeof(struct tm)); - tm->tm_hour = (zip_time >> 11) & 0x1f; - tm->tm_min = (zip_time >> 5) & 0x3f; - tm->tm_sec = (zip_time & 0x1f) << 1; - - tm->tm_year = ((zip_time >> 25) & 0x7f) + 80; - tm->tm_mon = ((zip_time >> 21) & 0xf) - 1; - tm->tm_mday = (zip_time >> 16) & 0x1f; -} - static struct tm MakeTm() { struct tm tm; memset(&tm, 0, sizeof(struct tm)); @@ -177,8 +166,7 @@ ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data)); EXPECT_EQ(0, data.offset & 0x03); - struct tm mod; - ConvertZipTimeToTm(data.mod_time, &mod); + struct tm mod = data.GetModificationTime(); EXPECT_EQ(tm.tm_sec, mod.tm_sec); EXPECT_EQ(tm.tm_min, mod.tm_min); EXPECT_EQ(tm.tm_hour, mod.tm_hour); @@ -228,8 +216,7 @@ ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data)); EXPECT_EQ(0, data.offset & 0xfff); - struct tm mod; - ConvertZipTimeToTm(data.mod_time, &mod); + struct tm mod = data.GetModificationTime(); EXPECT_EQ(tm.tm_sec, mod.tm_sec); EXPECT_EQ(tm.tm_min, mod.tm_min); EXPECT_EQ(tm.tm_hour, mod.tm_hour); @@ -374,7 +361,7 @@ ASSERT_EQ(0, writer.StartEntry("large.txt", 0)); std::vector<uint8_t> data; - data.resize(1024*1024, 0xef); + data.resize(1024 * 1024, 0xef); ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size())); ASSERT_EQ(0, writer.FinishEntry()); @@ -395,8 +382,9 @@ ZipArchiveHandle handle, ZipEntry* zip_entry) { if (expected.size() != zip_entry->uncompressed_length) { - return ::testing::AssertionFailure() << "uncompressed entry size " - << zip_entry->uncompressed_length << " does not match expected size " << expected.size(); + return ::testing::AssertionFailure() + << "uncompressed entry size " << zip_entry->uncompressed_length + << " does not match expected size " << expected.size(); } std::string actual; @@ -409,7 +397,7 @@ if (expected != actual) { return ::testing::AssertionFailure() << "actual zip_entry data '" << actual - << "' does not match expected '" << expected << "'"; + << "' does not match expected '" << expected << "'"; } return ::testing::AssertionSuccess(); }
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c index 8a6168c..5cfa2c8 100644 --- a/lmkd/lmkd.c +++ b/lmkd/lmkd.c
@@ -18,19 +18,21 @@ #include <arpa/inet.h> #include <errno.h> +#include <inttypes.h> #include <sched.h> #include <signal.h> #include <stdlib.h> #include <string.h> -#include <time.h> #include <sys/cdefs.h> #include <sys/epoll.h> #include <sys/eventfd.h> #include <sys/mman.h> #include <sys/socket.h> #include <sys/types.h> +#include <time.h> #include <unistd.h> +#include <cutils/properties.h> #include <cutils/sockets.h> #include <log/log.h> #include <processgroup/processgroup.h> @@ -40,7 +42,10 @@ #endif #define MEMCG_SYSFS_PATH "/dev/memcg/" -#define MEMPRESSURE_WATCH_LEVEL "low" +#define MEMCG_MEMORY_USAGE "/dev/memcg/memory.usage_in_bytes" +#define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes" +#define MEMPRESSURE_WATCH_MEDIUM_LEVEL "medium" +#define MEMPRESSURE_WATCH_CRITICAL_LEVEL "critical" #define ZONEINFO_PATH "/proc/zoneinfo" #define LINE_MAX 128 @@ -48,6 +53,7 @@ #define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj" #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) +#define EIGHT_MEGA (1 << 23) enum lmk_cmd { LMK_TARGET, @@ -64,17 +70,28 @@ /* default to old in-kernel interface if no memory pressure events */ static int use_inkernel_interface = 1; +static bool has_inkernel_module; /* memory pressure level medium event */ -static int mpevfd; +static int mpevfd[2]; +#define CRITICAL_INDEX 1 +#define MEDIUM_INDEX 0 + +static int medium_oomadj; +static int critical_oomadj; +static bool debug_process_killing; +static bool enable_pressure_upgrade; +static int64_t upgrade_pressure; +static int64_t downgrade_pressure; +static bool is_go_device; /* control socket listen and data */ static int ctrl_lfd; static int ctrl_dfd = -1; static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */ -/* 1 memory pressure level, 1 ctrl listen socket, 1 ctrl data socket */ -#define MAX_EPOLL_EVENTS 3 +/* 2 memory pressure levels, 1 ctrl listen socket, 1 ctrl data socket */ +#define MAX_EPOLL_EVENTS 4 static int epollfd; static int maxevents; @@ -113,14 +130,6 @@ #define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN) static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1]; -/* - * Wait 1-2 seconds for the death report of a killed process prior to - * considering killing more processes. - */ -#define KILL_TIMEOUT 2 -/* Time of last process kill we initiated, stop me before I kill again */ -static time_t kill_lasttime; - /* PAGE_SIZE / 1024 */ static long page_k; @@ -241,6 +250,7 @@ struct proc *procp; char path[80]; char val[20]; + int soft_limit_mult; if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) { ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj); @@ -254,6 +264,38 @@ if (use_inkernel_interface) return; + if (oomadj >= 900) { + soft_limit_mult = 0; + } else if (oomadj >= 800) { + soft_limit_mult = 0; + } else if (oomadj >= 700) { + soft_limit_mult = 0; + } else if (oomadj >= 600) { + // Launcher should be perceptible, don't kill it. + oomadj = 200; + soft_limit_mult = 1; + } else if (oomadj >= 500) { + soft_limit_mult = 0; + } else if (oomadj >= 400) { + soft_limit_mult = 0; + } else if (oomadj >= 300) { + soft_limit_mult = 1; + } else if (oomadj >= 200) { + soft_limit_mult = 2; + } else if (oomadj >= 100) { + soft_limit_mult = 10; + } else if (oomadj >= 0) { + soft_limit_mult = 20; + } else { + // Persistent processes will have a large + // soft limit 512MB. + soft_limit_mult = 64; + } + + snprintf(path, sizeof(path), "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes", uid, pid); + snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA); + writefilestring(path, val); + procp = pid_lookup(pid); if (!procp) { procp = malloc(sizeof(struct proc)); @@ -278,7 +320,6 @@ return; pid_remove(pid); - kill_lasttime = 0; } static void cmd_target(int ntargets, int *params) { @@ -294,7 +335,7 @@ lowmem_targets_size = ntargets; - if (use_inkernel_interface) { + if (has_inkernel_module) { char minfreestr[128]; char killpriostr[128]; @@ -309,9 +350,9 @@ strlcat(killpriostr, ",", sizeof(killpriostr)); } - snprintf(val, sizeof(val), "%d", lowmem_minfree[i]); + snprintf(val, sizeof(val), "%d", use_inkernel_interface ? lowmem_minfree[i] : 0); strlcat(minfreestr, val, sizeof(minfreestr)); - snprintf(val, sizeof(val), "%d", lowmem_adj[i]); + snprintf(val, sizeof(val), "%d", use_inkernel_interface ? lowmem_adj[i] : 0); strlcat(killpriostr, val, sizeof(killpriostr)); } @@ -546,9 +587,7 @@ } /* Kill one process specified by procp. Returns the size of the process killed */ -static int kill_one_process(struct proc *procp, int other_free, int other_file, - int minfree, int min_score_adj, bool first) -{ +static int kill_one_process(struct proc* procp, int min_score_adj, bool is_critical) { int pid = procp->pid; uid_t uid = procp->uid; char *taskname; @@ -567,14 +606,12 @@ return -1; } - ALOGI("Killing '%s' (%d), uid %d, adj %d\n" - " to free %ldkB because cache %s%ldkB is below limit %ldkB for oom_adj %d\n" - " Free memory is %s%ldkB %s reserved", - taskname, pid, uid, procp->oomadj, tasksize * page_k, - first ? "" : "~", other_file * page_k, minfree * page_k, min_score_adj, - first ? "" : "~", other_free * page_k, other_free >= 0 ? "above" : "below"); + ALOGI( + "Killing '%s' (%d), uid %d, adj %d\n" + " to free %ldkB because system is under %s memory pressure oom_adj %d\n", + taskname, pid, uid, procp->oomadj, tasksize * page_k, is_critical ? "critical" : "medium", + min_score_adj); r = kill(pid, SIGKILL); - killProcessGroup(uid, pid, SIGKILL); pid_remove(pid); if (r) { @@ -589,23 +626,10 @@ * Find a process to kill based on the current (possibly estimated) free memory * and cached memory sizes. Returns the size of the killed processes. */ -static int find_and_kill_process(int other_free, int other_file, bool first) -{ +static int find_and_kill_process(bool is_critical) { int i; - int min_score_adj = OOM_SCORE_ADJ_MAX + 1; - int minfree = 0; int killed_size = 0; - - for (i = 0; i < lowmem_targets_size; i++) { - minfree = lowmem_minfree[i]; - if (other_free < minfree && other_file < minfree) { - min_score_adj = lowmem_adj[i]; - break; - } - } - - if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) - return 0; + int min_score_adj = is_critical ? critical_oomadj : medium_oomadj; for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) { struct proc *procp; @@ -614,7 +638,7 @@ procp = proc_adj_lru(i); if (procp) { - killed_size = kill_one_process(procp, other_free, other_file, minfree, min_score_adj, first); + killed_size = kill_one_process(procp, min_score_adj, is_critical); if (killed_size < 0) { goto retry; } else { @@ -626,42 +650,91 @@ return 0; } -static void mp_event(uint32_t events __unused) { +static int64_t get_memory_usage(const char* path) { + int ret; + int64_t mem_usage; + char buf[32]; + int fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd == -1) { + ALOGE("%s open: errno=%d", path, errno); + return -1; + } + + ret = read_all(fd, buf, sizeof(buf) - 1); + close(fd); + if (ret < 0) { + ALOGE("%s error: errno=%d", path, errno); + return -1; + } + sscanf(buf, "%" SCNd64, &mem_usage); + if (mem_usage == 0) { + ALOGE("No memory!"); + return -1; + } + return mem_usage; +} + +static void mp_event_common(bool is_critical) { int ret; unsigned long long evcount; - struct sysmeminfo mi; - int other_free; - int other_file; - int killed_size; - bool first = true; + int index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX; + int64_t mem_usage, memsw_usage; + int64_t mem_pressure; - ret = read(mpevfd, &evcount, sizeof(evcount)); + ret = read(mpevfd[index], &evcount, sizeof(evcount)); if (ret < 0) ALOGE("Error reading memory pressure event fd; errno=%d", errno); - if (time(NULL) - kill_lasttime < KILL_TIMEOUT) + mem_usage = get_memory_usage(MEMCG_MEMORY_USAGE); + memsw_usage = get_memory_usage(MEMCG_MEMORYSW_USAGE); + if (memsw_usage < 0 || mem_usage < 0) { + find_and_kill_process(is_critical); return; - - while (zoneinfo_parse(&mi) < 0) { - // Failed to read /proc/zoneinfo, assume ENOMEM and kill something - find_and_kill_process(0, 0, true); } - other_free = mi.nr_free_pages - mi.totalreserve_pages; - other_file = mi.nr_file_pages - mi.nr_shmem; + // Calculate percent for swappinness. + mem_pressure = (mem_usage * 100) / memsw_usage; - do { - killed_size = find_and_kill_process(other_free, other_file, first); - if (killed_size > 0) { - first = false; - other_free += killed_size; - other_file += killed_size; + if (enable_pressure_upgrade && !is_critical) { + // We are swapping too much. + if (mem_pressure < upgrade_pressure) { + ALOGI("Event upgraded to critical."); + is_critical = true; } - } while (killed_size > 0); + } + + // If the pressure is larger than downgrade_pressure lmk will not + // kill any process, since enough memory is available. + if (mem_pressure > downgrade_pressure) { + if (debug_process_killing) { + ALOGI("Ignore %s memory pressure", is_critical ? "critical" : "medium"); + } + return; + } else if (is_critical && mem_pressure > upgrade_pressure) { + if (debug_process_killing) { + ALOGI("Downgrade critical memory pressure"); + } + // Downgrade event to medium, since enough memory available. + is_critical = false; + } + + if (find_and_kill_process(is_critical) == 0) { + if (debug_process_killing) { + ALOGI("Nothing to kill"); + } + } } -static int init_mp(char *levelstr, void *event_handler) +static void mp_event(uint32_t events __unused) { + mp_event_common(false); +} + +static void mp_event_critical(uint32_t events __unused) { + mp_event_common(true); +} + +static int init_mp_common(char *levelstr, void *event_handler, bool is_critical) { int mpfd; int evfd; @@ -669,6 +742,7 @@ char buf[256]; struct epoll_event epev; int ret; + int mpevfd_index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX; mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC); if (mpfd < 0) { @@ -709,7 +783,7 @@ goto err; } maxevents++; - mpevfd = evfd; + mpevfd[mpevfd_index] = evfd; return 0; err: @@ -722,6 +796,16 @@ return -1; } +static int init_mp_medium() +{ + return init_mp_common(MEMPRESSURE_WATCH_MEDIUM_LEVEL, (void *)&mp_event, false); +} + +static int init_mp_critical() +{ + return init_mp_common(MEMPRESSURE_WATCH_CRITICAL_LEVEL, (void *)&mp_event_critical, true); +} + static int init(void) { struct epoll_event epev; int i; @@ -758,12 +842,14 @@ } maxevents++; - use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK); + has_inkernel_module = !access(INKERNEL_MINFREE_PATH, W_OK); + use_inkernel_interface = has_inkernel_module && !is_go_device; if (use_inkernel_interface) { ALOGI("Using in-kernel low memory killer interface"); } else { - ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event); + ret = init_mp_medium(); + ret |= init_mp_critical(); if (ret) ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer"); } @@ -806,6 +892,14 @@ .sched_priority = 1, }; + medium_oomadj = property_get_int32("ro.lmk.medium", 800); + critical_oomadj = property_get_int32("ro.lmk.critical", 0); + debug_process_killing = property_get_bool("ro.lmk.debug", false); + enable_pressure_upgrade = property_get_bool("ro.lmk.critical_upgrade", false); + upgrade_pressure = (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 50); + downgrade_pressure = (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 60); + is_go_device = property_get_bool("ro.config.low_ram", false); + mlockall(MCL_FUTURE); sched_setscheduler(0, SCHED_FIFO, ¶m); if (!init())
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index e9ef9cc..f64196f 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp
@@ -1638,7 +1638,7 @@ logcat_panic(context, HELP_FALSE, "read: unexpected length.\n"); break; } - logcat_panic(context, HELP_FALSE, "logcat read failure"); + logcat_panic(context, HELP_FALSE, "logcat read failure\n"); break; }
diff --git a/logd/Android.bp b/logd/Android.bp new file mode 100644 index 0000000..68b79d3 --- /dev/null +++ b/logd/Android.bp
@@ -0,0 +1,78 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This is what we want to do: +// event_logtags = $(shell +// sed -n +// "s/^\([0-9]*\)[ \t]*$1[ \t].*/-D`echo $1 | tr a-z A-Z`_LOG_TAG=\1/p" +// $(LOCAL_PATH)/$2/event.logtags) +// event_flag := $(call event_logtags,auditd) +// event_flag += $(call event_logtags,logd) +// event_flag += $(call event_logtags,tag_def) +// so make sure we do not regret hard-coding it as follows: +event_flag = [ + "-DAUDITD_LOG_TAG=1003", + "-DCHATTY_LOG_TAG=1004", + "-DTAG_DEF_LOG_TAG=1005", + "-DLIBLOG_LOG_TAG=1006" +] + +cc_library_static { + name: "liblogd", + + srcs: [ + "LogCommand.cpp", + "CommandListener.cpp", + "LogListener.cpp", + "LogReader.cpp", + "FlushCommand.cpp", + "LogBuffer.cpp", + "LogBufferElement.cpp", + "LogBufferInterface.cpp", + "LogTimes.cpp", + "LogStatistics.cpp", + "LogWhiteBlackList.cpp", + "libaudit.c", + "LogAudit.cpp", + "LogKlog.cpp", + "LogTags.cpp", + ], + logtags: ["event.logtags"], + + shared_libs: ["libbase"], + + export_include_dirs: ["."], + + cflags: ["-Werror"] + event_flag, +} + +cc_binary { + name: "logd", + init_rc: ["logd.rc"], + + srcs: ["main.cpp"], + + static_libs: ["liblogd"], + + shared_libs: [ + "libsysutils", + "liblog", + "libcutils", + "libbase", + "libpackagelistparser", + "libcap", + ], + + cflags: ["-Werror"], +}
diff --git a/logd/Android.mk b/logd/Android.mk index fb51992..1bca891 100644 --- a/logd/Android.mk +++ b/logd/Android.mk
@@ -2,73 +2,6 @@ include $(CLEAR_VARS) -LOCAL_MODULE:= liblogd - -LOCAL_SRC_FILES := \ - LogCommand.cpp \ - CommandListener.cpp \ - LogListener.cpp \ - LogReader.cpp \ - FlushCommand.cpp \ - LogBuffer.cpp \ - LogBufferElement.cpp \ - LogBufferInterface.cpp \ - LogTimes.cpp \ - LogStatistics.cpp \ - LogWhiteBlackList.cpp \ - libaudit.c \ - LogAudit.cpp \ - LogKlog.cpp \ - LogTags.cpp \ - event.logtags - -LOCAL_SHARED_LIBRARIES := \ - libbase - -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) - -# This is what we want to do: -# event_logtags = $(shell \ -# sed -n \ -# "s/^\([0-9]*\)[ \t]*$1[ \t].*/-D`echo $1 | tr a-z A-Z`_LOG_TAG=\1/p" \ -# $(LOCAL_PATH)/$2/event.logtags) -# event_flag := $(call event_logtags,auditd) -# event_flag += $(call event_logtags,logd) -# event_flag += $(call event_logtags,tag_def) -# so make sure we do not regret hard-coding it as follows: -event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004 -DTAG_DEF_LOG_TAG=1005 -event_flag += -DLIBLOG_LOG_TAG=1006 - -LOCAL_CFLAGS := -Werror $(event_flag) - -include $(BUILD_STATIC_LIBRARY) - -include $(CLEAR_VARS) - -LOCAL_MODULE:= logd - -LOCAL_INIT_RC := logd.rc - -LOCAL_SRC_FILES := \ - main.cpp - -LOCAL_STATIC_LIBRARIES := \ - liblogd - -LOCAL_SHARED_LIBRARIES := \ - libsysutils \ - liblog \ - libcutils \ - libbase \ - libpackagelistparser \ - libcap - -LOCAL_CFLAGS := -Werror - -include $(BUILD_EXECUTABLE) - -include $(CLEAR_VARS) - LOCAL_MODULE := logtagd.rc LOCAL_SRC_FILES := $(LOCAL_MODULE) LOCAL_MODULE_CLASS := ETC
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp index 381c974..f20ac45 100644 --- a/logd/LogBufferElement.cpp +++ b/logd/LogBufferElement.cpp
@@ -41,22 +41,20 @@ mTid(tid), mRealTime(realtime), mMsgLen(len), - mLogId(log_id) { + mLogId(log_id), + mDropped(false) { mMsg = new char[len]; memcpy(mMsg, msg, len); - mTag = (isBinary() && (mMsgLen >= sizeof(uint32_t))) - ? le32toh(reinterpret_cast<android_event_header_t*>(mMsg)->tag) - : 0; } LogBufferElement::LogBufferElement(const LogBufferElement& elem) - : mTag(elem.mTag), - mUid(elem.mUid), + : mUid(elem.mUid), mPid(elem.mPid), mTid(elem.mTid), mRealTime(elem.mRealTime), mMsgLen(elem.mMsgLen), - mLogId(elem.mLogId) { + mLogId(elem.mLogId), + mDropped(elem.mDropped) { mMsg = new char[mMsgLen]; memcpy(mMsg, elem.mMsg, mMsgLen); } @@ -65,6 +63,32 @@ delete[] mMsg; } +uint32_t LogBufferElement::getTag() const { + return (isBinary() && + ((mDropped && mMsg != nullptr) || + (!mDropped && mMsgLen >= sizeof(android_event_header_t)))) + ? reinterpret_cast<const android_event_header_t*>(mMsg)->tag + : 0; +} + +unsigned short LogBufferElement::setDropped(unsigned short value) { + // The tag information is saved in mMsg data, if the tag is non-zero + // save only the information needed to get the tag. + if (getTag() != 0) { + if (mMsgLen > sizeof(android_event_header_t)) { + char* truncated_msg = new char[sizeof(android_event_header_t)]; + memcpy(truncated_msg, mMsg, sizeof(android_event_header_t)); + delete[] mMsg; + mMsg = truncated_msg; + } // mMsgLen == sizeof(android_event_header_t), already at minimum. + } else { + delete[] mMsg; + mMsg = nullptr; + } + mDropped = true; + return mDroppedCount = value; +} + // caller must own and free character string char* android::tidToName(pid_t tid) { char* retval = NULL; @@ -164,8 +188,8 @@ // identical to below to calculate the buffer size required const char* type = lastSame ? "identical" : "expire"; size_t len = snprintf(NULL, 0, format_uid, mUid, name ? name : "", - commName ? commName : "", type, mDropped, - (mDropped > 1) ? "s" : ""); + commName ? commName : "", type, getDropped(), + (getDropped() > 1) ? "s" : ""); size_t hdrLen; if (isBinary()) { @@ -196,8 +220,8 @@ } snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "", - commName ? commName : "", type, mDropped, - (mDropped > 1) ? "s" : ""); + commName ? commName : "", type, getDropped(), + (getDropped() > 1) ? "s" : ""); free(const_cast<char*>(name)); free(const_cast<char*>(commName)); @@ -225,7 +249,7 @@ char* buffer = NULL; - if (!mMsg) { + if (mDropped) { entry.len = populateDroppedMessage(buffer, parent, lastSame); if (!entry.len) return mRealTime; iovec[1].iov_base = buffer;
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h index 814ec87..b168645 100644 --- a/logd/LogBufferElement.h +++ b/logd/LogBufferElement.h
@@ -32,25 +32,25 @@ // chatty for the temporal expire messages #define EXPIRE_RATELIMIT 10 // maximum rate in seconds to report expiration -class LogBufferElement { +class __attribute__((packed)) LogBufferElement { friend LogBuffer; // sized to match reality of incoming log packets - uint32_t mTag; // only valid for isBinary() const uint32_t mUid; const uint32_t mPid; const uint32_t mTid; log_time mRealTime; char* mMsg; union { - const uint16_t mMsgLen; // mMSg != NULL - uint16_t mDropped; // mMsg == NULL + const uint16_t mMsgLen; // mDropped == false + uint16_t mDroppedCount; // mDropped == true }; const uint8_t mLogId; + bool mDropped; static atomic_int_fast64_t sequence; - // assumption: mMsg == NULL + // assumption: mDropped == true size_t populateDroppedMessage(char*& buffer, LogBuffer* parent, bool lastSame); @@ -58,7 +58,7 @@ LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg, unsigned short len); LogBufferElement(const LogBufferElement& elem); - virtual ~LogBufferElement(); + ~LogBufferElement(); bool isBinary(void) const { return (mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY); @@ -76,24 +76,16 @@ pid_t getTid(void) const { return mTid; } - uint32_t getTag() const { - return mTag; - } + uint32_t getTag() const; unsigned short getDropped(void) const { - return mMsg ? 0 : mDropped; + return mDropped ? mDroppedCount : 0; } - unsigned short setDropped(unsigned short value) { - if (mMsg) { - delete[] mMsg; - mMsg = NULL; - } - return mDropped = value; - } + unsigned short setDropped(unsigned short value); unsigned short getMsgLen() const { - return mMsg ? mMsgLen : 0; + return mDropped ? 0 : mMsgLen; } const char* getMsg() const { - return mMsg; + return mDropped ? nullptr : mMsg; } log_time getRealTime(void) const { return mRealTime;
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk index 1915677..a0875ea 100644 --- a/logd/tests/Android.mk +++ b/logd/tests/Android.mk
@@ -63,7 +63,7 @@ LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64 LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux LOCAL_STATIC_LIBRARIES := libgtest libgtest_main -LOCAL_COMPATIBILITY_SUITE := cts +LOCAL_COMPATIBILITY_SUITE := cts vts LOCAL_CTS_TEST_PACKAGE := android.core.logd include $(BUILD_CTS_EXECUTABLE)
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp index cd80212..d242f9b 100644 --- a/logd/tests/logd_test.cpp +++ b/logd/tests/logd_test.cpp
@@ -933,8 +933,12 @@ } #ifdef __ANDROID__ -static inline int32_t get4LE(const char* src) { - return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); +static inline uint32_t get4LE(const uint8_t* src) { + return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); +} + +static inline uint32_t get4LE(const char* src) { + return get4LE(reinterpret_cast<const uint8_t*>(src)); } #endif @@ -1087,7 +1091,7 @@ // and dac_read_search on every try to get past the message // de-duper. We will also rotate the file name in the directory // as another measure. - static const char file[] = "/data/backup/cannot_access_directory_%u"; + static const char file[] = "/data/drm/cannot_access_directory_%u"; static const unsigned avc_requests_per_access = 2; rate /= avc_requests_per_access;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk index 2e5575f..3377716 100644 --- a/rootdir/Android.mk +++ b/rootdir/Android.mk
@@ -193,8 +193,62 @@ # ld.config.txt include $(CLEAR_VARS) +_enforce_vndk_at_runtime := false + +ifdef BOARD_VNDK_VERSION +ifneq ($(BOARD_VNDK_RUNTIME_DISABLE),true) + _enforce_vndk_at_runtime := true +endif +endif + +ifeq ($(_enforce_vndk_at_runtime),true) LOCAL_MODULE := ld.config.txt -ifeq ($(PRODUCT_FULL_TREBLE),true) +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC) +LOCAL_MODULE_STEM := $(LOCAL_MODULE) +include $(BUILD_SYSTEM)/base_rules.mk +vndk_lib_md5 := $(word 1, $(shell echo $(LLNDK_LIBRARIES) $(VNDK_SAMEPROCESS_LIBRARIES) | $(MD5SUM))) +vndk_lib_dep := $(intermediates)/$(vndk_lib_md5).dep +$(vndk_lib_dep): + $(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.dep && touch $@ + +llndk_libraries := $(subst $(space),:,$(addsuffix .so,$(LLNDK_LIBRARIES))) + +vndk_sameprocess_libraries := $(subst $(space),:,$(addsuffix .so,$(VNDK_SAMEPROCESS_LIBRARIES))) + +vndk_core_libraries := $(subst $(space),:,$(addsuffix .so,$(VNDK_CORE_LIBRARIES))) + +sanitizer_runtime_libraries := $(subst $(space),:,$(addsuffix .so,\ +$(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \ +$(UBSAN_RUNTIME_LIBRARY) \ +$(TSAN_RUNTIME_LIBRARY) \ +$(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \ +$(2ND_UBSAN_RUNTIME_LIBRARY) \ +$(2ND_TSAN_RUNTIME_LIBRARY))) + +$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(llndk_libraries) +$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(vndk_sameprocess_libraries) +$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_PRIVATE_LIBRARIES := $(llndk_private_libraries) +$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(vndk_core_libraries) +$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(sanitizer_runtime_libraries) +$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/ld.config.txt.in $(vndk_lib_dep) + @echo "Generate: $< -> $@" + @mkdir -p $(dir $@) + $(hide) sed -e 's?%LLNDK_LIBRARIES%?$(PRIVATE_LLNDK_LIBRARIES)?g' $< >$@ + $(hide) sed -i -e 's?%VNDK_SAMEPROCESS_LIBRARIES%?$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES)?g' $@ + $(hide) sed -i -e 's?%VNDK_CORE_LIBRARIES%?$(PRIVATE_VNDK_CORE_LIBRARIES)?g' $@ + $(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $@ + +vndk_lib_md5 := +vndk_lib_dep := +llndk_libraries := +vndk_sameprocess_libraries := +vndk_core_libraries := +sanitizer_runtime_libraries := +else # if _enforce_vndk_at_runtime is not true + +LOCAL_MODULE := ld.config.txt +ifeq ($(PRODUCT_FULL_TREBLE)|$(SANITIZE_TARGET),true|) LOCAL_SRC_FILES := etc/ld.config.txt else LOCAL_SRC_FILES := etc/ld.config.legacy.txt @@ -203,3 +257,46 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_ETC) LOCAL_MODULE_STEM := $(LOCAL_MODULE) include $(BUILD_PREBUILT) +endif + +####################################### +# llndk.libraries.txt +include $(CLEAR_VARS) +LOCAL_MODULE := llndk.libraries.txt +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC) +LOCAL_MODULE_STEM := $(LOCAL_MODULE) +include $(BUILD_SYSTEM)/base_rules.mk +llndk_md5 = $(word 1, $(shell echo $(LLNDK_LIBRARIES) | $(MD5SUM))) +llndk_dep = $(intermediates)/$(llndk_md5).dep +$(llndk_dep): + $(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.dep && touch $@ + +$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(LLNDK_LIBRARIES) +$(LOCAL_BUILT_MODULE): $(llndk_dep) + @echo "Generate: $@" + @mkdir -p $(dir $@) + $(hide) echo -n > $@ + $(hide) $(foreach lib,$(PRIVATE_LLNDK_LIBRARIES), \ + echo $(lib).so >> $@;) + +####################################### +# vndksp.libraries.txt +include $(CLEAR_VARS) +LOCAL_MODULE := vndksp.libraries.txt +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC) +LOCAL_MODULE_STEM := $(LOCAL_MODULE) +include $(BUILD_SYSTEM)/base_rules.mk +vndksp_md5 = $(word 1, $(shell echo $(LLNDK_LIBRARIES) | $(MD5SUM))) +vndksp_dep = $(intermediates)/$(vndksp_md5).dep +$(vndksp_dep): + $(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.dep && touch $@ + +$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(VNDK_SAMEPROCESS_LIBRARIES) +$(LOCAL_BUILT_MODULE): $(vndksp_dep) + @echo "Generate: $@" + @mkdir -p $(dir $@) + $(hide) echo -n > $@ + $(hide) $(foreach lib,$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES), \ + echo $(lib).so >> $@;)
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt index 56066bc..8aa3369 100644 --- a/rootdir/etc/ld.config.txt +++ b/rootdir/etc/ld.config.txt
@@ -7,10 +7,14 @@ dir.system = /system/bin/ dir.system = /system/xbin/ dir.vendor = /vendor/bin/ -dir.test = /data/nativetest/ -dir.test = /data/nativetest64/ -dir.test = /data/benchmarktest/ -dir.test = /data/benchmarktest64/ +dir.vendor = /data/nativetest/vendor +dir.vendor = /data/nativetest64/vendor +dir.vendor = /data/benchmarktest/vendor +dir.vendor = /data/benchmarktest64/vendor +dir.system = /data/nativetest +dir.system = /data/nativetest64 +dir.system = /data/benchmarktest +dir.system = /data/benchmarktest64 [system] additional.namespaces = sphal,vndk,rs @@ -54,7 +58,7 @@ namespace.sphal.isolated = true namespace.sphal.visible = true namespace.sphal.search.paths = /vendor/${LIB}/egl:/vendor/${LIB}/hw:/vendor/${LIB} -namespace.sphal.permitted.paths = /vendor/${LIB} +namespace.sphal.permitted.paths = /vendor/${LIB}:/system/${LIB}/vndk-sp/hw namespace.sphal.asan.search.paths = /data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}:/vendor/${LIB} namespace.sphal.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB} @@ -64,7 +68,7 @@ namespace.sphal.links = default,vndk,rs # WARNING: only NDK libs can be listed here. -namespace.sphal.link.default.shared_libs = libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libvndksupport.so +namespace.sphal.link.default.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libvndksupport.so:libz.so # WARNING: only VNDK-SP libs can be listed here. DO NOT EDIT this line. namespace.sphal.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so @@ -81,6 +85,7 @@ # to load the compiled *.so file and libmediandk.so can be used here. ############################################################################### namespace.rs.isolated = true +namespace.rs.visible = true namespace.rs.search.paths = /vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/vendor/${LIB} namespace.rs.permitted.paths = /vendor/${LIB}:/data @@ -88,7 +93,7 @@ namespace.rs.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data namespace.rs.links = default,vndk -namespace.rs.link.default.shared_libs = libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libmediandk.so:libvndksupport.so +namespace.rs.link.default.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libmediandk.so:libvndksupport.so:libz.so:libft2.so namespace.rs.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so ############################################################################### @@ -97,17 +102,18 @@ # This namespace is exclusively for vndk-sp libs. ############################################################################### namespace.vndk.isolated = true -namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/vendor/${LIB} +namespace.vndk.visible = true +namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp namespace.vndk.permitted.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl -namespace.vndk.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/vendor/${LIB}:/vendor/${LIB} +namespace.vndk.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp namespace.vndk.asan.permitted.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl # When these NDK libs are required inside this namespace, then it is redirected # to the default namespace. This is possible since their ABI is stable across # Android releases. namespace.vndk.links = default -namespace.vndk.link.default.shared_libs = android.hidl.memory@1.0-impl.so:libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libvndksupport.so +namespace.vndk.link.default.shared_libs = android.hidl.memory@1.0-impl.so:libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libvndksupport.so:libz.so ############################################################################### # Namespace config for vendor processes. In O, no restriction is enforced for @@ -117,15 +123,6 @@ ############################################################################### [vendor] namespace.default.isolated = false -namespace.default.search.paths = /vendor/${LIB}:/vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/system/${LIB} +namespace.default.search.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/system/${LIB}/vndk:/vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/system/${LIB} -namespace.default.asan.search.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/system/${LIB}:/system/${LIB} - -############################################################################### -# Namespace config for tests. No VNDK restriction is enforced for these tests. -############################################################################### -[test] -namespace.default.isolated = false -namespace.default.search.paths = /vendor/${LIB}:/vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/system/${LIB} - -namespace.default.asan.search.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/system/${LIB}:/system/${LIB} +namespace.default.asan.search.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/system/${LIB}/vndk:/system/${LIB}/vndk:/data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/system/${LIB}:/system/${LIB}
diff --git a/rootdir/etc/ld.config.txt.in b/rootdir/etc/ld.config.txt.in new file mode 100644 index 0000000..2c73056 --- /dev/null +++ b/rootdir/etc/ld.config.txt.in
@@ -0,0 +1,147 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Bionic loader config file. +# + +# Don't change the order here. The first pattern that matches with the +# absolution path of an executable is selected. +dir.system = /system/bin/ +dir.system = /system/xbin/ +dir.vendor = /vendor/bin/ +dir.vendor = /data/nativetest/vendor +dir.vendor = /data/nativetest64/vendor +dir.vendor = /data/benchmarktest/vendor +dir.vendor = /data/benchmarktest64/vendor +dir.system = /data/nativetest +dir.system = /data/nativetest64 +dir.system = /data/benchmarktest +dir.system = /data/benchmarktest64 + +[system] +additional.namespaces = sphal,vndk,rs + +############################################################################### +# "default" namespace +# +# Framework-side code runs in this namespace. Libs from /vendor partition +# can't be loaded in this namespace. +############################################################################### +namespace.default.isolated = true +namespace.default.search.paths = /system/${LIB} +# /vendor/app, /vendor/framework were added since libart should be able to dlopen +# the odex files from the directory. +namespace.default.permitted.paths = /system/${LIB}/drm:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/data:/mnt/expand + +namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB} +namespace.default.asan.permitted.paths = /data:/system/${LIB}/drm:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/mnt/expand + +############################################################################### +# "sphal" namespace +# +# SP-HAL(Sameprocess-HAL)s are the only vendor libraries that are allowed to be +# loaded inside system processes. libEGL_<chipset>.so, libGLESv2_<chipset>.so, +# android.hardware.graphics.mapper@2.0-impl.so, etc are SP-HALs. +# +# This namespace is exclusivly for SP-HALs. When the framework tries to dynami- +# cally load SP-HALs, android_dlopen_ext() is used to explicitly specifying +# that they should be searched and loaded from this namespace. +# +# Note that there is no link from the default namespace to this namespace. +############################################################################### +namespace.sphal.isolated = true +namespace.sphal.visible = true +namespace.sphal.search.paths = /vendor/${LIB}/egl:/vendor/${LIB}/hw:/vendor/${LIB} +namespace.sphal.permitted.paths = /vendor/${LIB}:/system/${LIB}/vndk-sp/hw + +namespace.sphal.asan.search.paths = /data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}:/vendor/${LIB} +namespace.sphal.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB} + +# Once in this namespace, access to libraries in /system/lib is restricted. Only +# libs listed here can be used. +namespace.sphal.links = default,vndk,rs + +# WARNING: only NDK libs can be listed here. +namespace.sphal.link.default.shared_libs = %LLNDK_LIBRARIES%:%SANITIZER_RUNTIME_LIBRARIES% + +# WARNING: only VNDK-SP libs can be listed here. DO NOT EDIT this line. +namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES% + +# Renderscript gets separate namespace +namespace.sphal.link.rs.shared_libs = libRS_internal.so + +############################################################################### +# "rs" namespace +# +# This namespace is exclusively for Renderscript internal libraries. +# This namespace has slightly looser restriction than the vndk namespace because +# of the genuine characteristics of Renderscript; /data is in the permitted path +# to load the compiled *.so file and libmediandk.so can be used here. +############################################################################### +namespace.rs.isolated = true +namespace.rs.visible = true +namespace.rs.search.paths = /vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/vendor/${LIB} +namespace.rs.permitted.paths = /vendor/${LIB}:/data + +namespace.rs.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/vendor/${LIB}:/vendor/${LIB} +namespace.rs.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data + +namespace.rs.links = default,vndk +namespace.rs.link.default.shared_libs = %LLNDK_LIBRARIES%:%SANITIZER_RUNTIME_LIBRARIES% +namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES% + +############################################################################### +# "vndk" namespace +# +# This namespace is exclusively for vndk-sp libs. +############################################################################### +namespace.vndk.isolated = true +namespace.vndk.visible = true +namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp +namespace.vndk.permitted.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl + +namespace.vndk.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp +namespace.vndk.asan.permitted.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl + +# When these NDK libs are required inside this namespace, then it is redirected +# to the default namespace. This is possible since their ABI is stable across +# Android releases. +namespace.vndk.links = default +namespace.vndk.link.default.shared_libs = %LLNDK_LIBRARIES%:%SANITIZER_RUNTIME_LIBRARIES% + +############################################################################### +# Namespace config for vendor processes. In O, no restriction is enforced for +# them. However, in O-MR1, access to /system/${LIB} will not be allowed to +# the default namespace. 'system' namespace will be added to give limited +# (LL-NDK only) access. +############################################################################### +[vendor] +additional.namespaces = system + +############################################################################### +# "default" namespace +# +# Vendor-side code runs in this namespace. +############################################################################### +namespace.default.isolated = true +namespace.default.visible = true + +namespace.default.search.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/vendor/${LIB}/vndk:/system/${LIB}/vndk:/vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp +namespace.default.permitted.paths = /vendor:/system/${LIB}/vndk:/system/${LIB}/vndk-sp + +namespace.default.asan.search.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk:/vendor/${LIB}/vndk:/data/asan/system/${LIB}/vndk:/system/${LIB}/vndk:/data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp +namespace.default.asan.permitted.paths = /data/asan/vendor:/vendor:/data/asan/system/${LIB}/vndk:/system/${LIB}/vndk:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp + +namespace.default.links = system +namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES% + +############################################################################### +# "system" namespace +# +# This is for vendor process to use LL-NDK in system partition. +############################################################################### +namespace.system.isolated = false +namespace.system.search.paths = /system/${LIB} +namespace.system.permitted.paths = /system/${LIB} + +namespace.system.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB} +namespace.system.asan.permitted.paths = /data/asan/system/${LIB}:/system/${LIB}
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt index bcdecf2..5482085 100644 --- a/rootdir/etc/public.libraries.android.txt +++ b/rootdir/etc/public.libraries.android.txt
@@ -14,6 +14,7 @@ libmediandk.so libm.so libnativewindow.so +libneuralnetworks.so libOpenMAXAL.so libOpenSLES.so libRS.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt index f4da09d..45b4bc2 100644 --- a/rootdir/etc/public.libraries.wear.txt +++ b/rootdir/etc/public.libraries.wear.txt
@@ -14,6 +14,7 @@ libmediandk.so libm.so libnativewindow.so +libneuralnetworks.so libOpenMAXAL.so libOpenSLES.so libRS.so
diff --git a/rootdir/init.rc b/rootdir/init.rc index fd2c838..2a73335 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc
@@ -36,6 +36,8 @@ mount cgroup none /dev/memcg memory # app mem cgroups, used by activity manager, lmkd and zygote mkdir /dev/memcg/apps/ 0755 system system + # cgroup for system_server and surfaceflinger + mkdir /dev/memcg/system 0550 system system start ueventd @@ -146,6 +148,9 @@ write /proc/sys/net/ipv4/conf/all/accept_redirects 0 write /proc/sys/net/ipv6/conf/all/accept_redirects 0 + # /proc/net/fib_trie leaks interface IP addresses + chmod 0400 /proc/net/fib_trie + # Create cgroup mount points for process groups mkdir /dev/cpuctl mount cgroup none /dev/cpuctl cpu @@ -225,6 +230,8 @@ mount pstore pstore /sys/fs/pstore chown system log /sys/fs/pstore/console-ramoops chmod 0440 /sys/fs/pstore/console-ramoops + chown system log /sys/fs/pstore/console-ramoops-0 + chmod 0440 /sys/fs/pstore/console-ramoops-0 chown system log /sys/fs/pstore/pmsg-ramoops-0 chmod 0440 /sys/fs/pstore/pmsg-ramoops-0 @@ -317,7 +324,6 @@ # Make sure /sys/kernel/debug (if present) is labeled properly # Note that tracefs may be mounted under debug, so we need to cross filesystems restorecon --recursive --cross-filesystems /sys/kernel/debug - chmod 0755 /sys/kernel/debug/tracing # We chown/chmod /cache again so because mount is run as root + defaults chown system cache /cache @@ -355,6 +361,10 @@ mkdir /cache/lost+found 0770 root root on late-fs + # Ensure that tracefs has the correct permissions. + # This does not work correctly if it is called in post-fs. + chmod 0755 /sys/kernel/debug/tracing + # HALs required before storage encryption can get unlocked (FBE/FDE) class_start early_hal
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc index 915d159..3168f40 100644 --- a/rootdir/init.usb.rc +++ b/rootdir/init.usb.rc
@@ -12,7 +12,7 @@ mkdir /data/adb 0700 root root # adbd is controlled via property triggers in init.<platform>.usb.rc -service adbd /sbin/adbd --root_seclabel=u:r:su:s0 +service adbd /system/bin/adbd --root_seclabel=u:r:su:s0 class core socket adbd stream 660 system system disabled
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp index e7b8cc2..b27cfad 100644 --- a/run-as/run-as.cpp +++ b/run-as/run-as.cpp
@@ -194,6 +194,7 @@ ScopedMinijail j(minijail_new()); minijail_change_uid(j.get(), uid); minijail_change_gid(j.get(), gid); + minijail_keep_supplementary_gids(j.get()); minijail_enter(j.get()); if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp index c342cf8..2bbb906 100644 --- a/sdcard/sdcard.cpp +++ b/sdcard/sdcard.cpp
@@ -317,17 +317,31 @@ LOG(FATAL) << "terminated prematurely"; } -static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path, uid_t fsuid, - gid_t fsgid, bool multi_user, userid_t userid, gid_t gid, mode_t mask) { - std::string opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d", - fsuid, fsgid, multi_user?"multiuser,":"", mask, userid, gid); +static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path, + uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid, + mode_t mask, bool derive_gid) { + std::string opts = android::base::StringPrintf( + "fsuid=%d,fsgid=%d,%s%smask=%d,userid=%d,gid=%d", fsuid, fsgid, + multi_user ? "multiuser," : "", derive_gid ? "derive_gid," : "", mask, userid, gid); if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) { - PLOG(ERROR) << "failed to mount sdcardfs filesystem"; - return false; + if (derive_gid) { + PLOG(ERROR) << "trying to mount sdcardfs filesystem without derive_gid"; + /* Maybe this isn't supported on this kernel. Try without. */ + opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d", + fsuid, fsgid, multi_user ? "multiuser," : "", mask, + userid, gid); + if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs", + MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) { + PLOG(ERROR) << "failed to mount sdcardfs filesystem"; + return false; + } + } else { + PLOG(ERROR) << "failed to mount sdcardfs filesystem"; + return false; + } } - return true; } @@ -353,7 +367,8 @@ } static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid, - gid_t gid, userid_t userid, bool multi_user, bool full_write) { + gid_t gid, userid_t userid, bool multi_user, bool full_write, + bool derive_gid) { std::string dest_path_default = "/mnt/runtime/default/" + label; std::string dest_path_read = "/mnt/runtime/read/" + label; std::string dest_path_write = "/mnt/runtime/write/" + label; @@ -363,10 +378,10 @@ // Multi-user storage is fully isolated per user, so "other" // permissions are completely masked off. if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid, - AID_SDCARD_RW, 0006) - || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY, 0027) - || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, - AID_EVERYBODY, full_write ? 0007 : 0027)) { + AID_SDCARD_RW, 0006, derive_gid) || + !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY, 0027) || + !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY, + full_write ? 0007 : 0027)) { LOG(FATAL) << "failed to sdcardfs_setup"; } } else { @@ -374,11 +389,11 @@ // the Android directories are masked off to a single user // deep inside attr_from_stat(). if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid, - AID_SDCARD_RW, 0006) - || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, - AID_EVERYBODY, full_write ? 0027 : 0022) - || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, - AID_EVERYBODY, full_write ? 0007 : 0022)) { + AID_SDCARD_RW, 0006, derive_gid) || + !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY, + full_write ? 0027 : 0022) || + !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY, + full_write ? 0007 : 0022)) { LOG(FATAL) << "failed to sdcardfs_setup"; } } @@ -435,7 +450,8 @@ << " -g: specify GID to run as" << " -U: specify user ID that owns device" << " -m: source_path is multi-user" - << " -w: runtime write mount has full write access"; + << " -w: runtime write mount has full write access" + << " -P preserve owners on the lower file system"; return 1; } @@ -447,12 +463,13 @@ userid_t userid = 0; bool multi_user = false; bool full_write = false; + bool derive_gid = false; int i; struct rlimit rlim; int fs_version; int opt; - while ((opt = getopt(argc, argv, "u:g:U:mw")) != -1) { + while ((opt = getopt(argc, argv, "u:g:U:mwG")) != -1) { switch (opt) { case 'u': uid = strtoul(optarg, NULL, 10); @@ -469,6 +486,9 @@ case 'w': full_write = true; break; + case 'G': + derive_gid = true; + break; case '?': default: return usage(); @@ -512,7 +532,7 @@ } if (should_use_sdcardfs()) { - run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write); + run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid); } else { run(source_path, label, uid, gid, userid, multi_user, full_write); }
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp index 6d35fed..9620d63 100644 --- a/shell_and_utilities/Android.bp +++ b/shell_and_utilities/Android.bp
@@ -4,7 +4,6 @@ "bzip2", "grep", "grep_vendor", - "gzip", "mkshrc", "mkshrc_vendor", "reboot",
diff --git a/storaged/Android.mk b/storaged/Android.mk index 5e6a3c0..a1abe0f 100644 --- a/storaged/Android.mk +++ b/storaged/Android.mk
@@ -9,7 +9,6 @@ libcutils \ liblog \ libsysutils \ - libpackagelistparser \ libbatteryservice \ include $(CLEAR_VARS)
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h index 514798b..fa68406 100644 --- a/storaged/include/storaged.h +++ b/storaged/include/storaged.h
@@ -256,6 +256,7 @@ uid_monitor mUidm; time_t mStarttime; sp<IBatteryPropertiesRegistrar> battery_properties; + std::unique_ptr<storage_info_t> storage_info; public: storaged_t(void); ~storaged_t() {} @@ -285,6 +286,8 @@ void init_battery_service(); virtual void batteryPropertiesChanged(struct BatteryProperties props); void binderDied(const wp<IBinder>& who); + + void report_storage_info(); }; // Eventlog tag
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h index 913c814..7d04c7a 100644 --- a/storaged/include/storaged_info.h +++ b/storaged/include/storaged_info.h
@@ -27,39 +27,46 @@ class storage_info_t { protected: FRIEND_TEST(storaged_test, storage_info_t); + // emmc lifetime uint16_t eol; // pre-eol (end of life) information uint16_t lifetime_a; // device life time estimation (type A) uint16_t lifetime_b; // device life time estimation (type B) string version; // version string + // free space + const string userdata_path = "/data"; + uint64_t userdata_total_kb; + uint64_t userdata_free_kb; + + storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0), + userdata_total_kb(0), userdata_free_kb(0) {} void publish(); + storage_info_t* s_info; public: - storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0) {} + static storage_info_t* get_storage_info(); virtual ~storage_info_t() {} - virtual bool report() = 0; + virtual void report() {}; + void refresh(); }; class emmc_info_t : public storage_info_t { private: - const string emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/"; - const string emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd"; - const char* emmc_ver_str[9] = { - "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1" - }; -public: - virtual ~emmc_info_t() {} - bool report(); bool report_sysfs(); bool report_debugfs(); +public: + static const string emmc_sysfs; + static const string emmc_debugfs; + static const char* emmc_ver_str[]; + + virtual ~emmc_info_t() {} + virtual void report(); }; class ufs_info_t : public storage_info_t { -private: - const string health_file = "/sys/devices/soc/624000.ufshc/health"; public: - virtual ~ufs_info_t() {} - bool report(); -}; + static const string health_file; -void report_storage_health(); + virtual ~ufs_info_t() {} + virtual void report(); +}; #endif /* _STORAGED_INFO_H_ */
diff --git a/storaged/main.cpp b/storaged/main.cpp index 4d1e430..6b82904 100644 --- a/storaged/main.cpp +++ b/storaged/main.cpp
@@ -49,6 +49,7 @@ storaged = new storaged_t(); storaged->init_battery_service(); + storaged->report_storage_info(); LOG_TO(SYSTEM, INFO) << "storaged: Start"; @@ -113,7 +114,6 @@ } if (flag_main_service) { // start main thread - report_storage_health(); // Start the main thread of storaged pthread_t storaged_main_thread; errno = pthread_create(&storaged_main_thread, NULL, storaged_main, NULL);
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp index 54d429c..06afea6 100644 --- a/storaged/storaged.cpp +++ b/storaged/storaged.cpp
@@ -200,6 +200,10 @@ } } +void storaged_t::report_storage_info() { + storage_info->report(); +} + /* storaged_t */ storaged_t::storaged_t(void) { if (access(MMC_DISK_STATS_PATH, R_OK) < 0 && access(SDA_DISK_STATS_PATH, R_OK) < 0) { @@ -222,6 +226,8 @@ mConfig.periodic_chores_interval_uid_io = property_get_int32("ro.storaged.uid_io.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO); + storage_info.reset(storage_info_t::get_storage_info()); + mStarttime = time(NULL); } @@ -229,6 +235,7 @@ if (mConfig.diskstats_available) { mDiskStats.update(); mDsm.update(); + storage_info->refresh(); if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) { mDiskStats.publish(); }
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp index 434bd74..b5fb13e 100644 --- a/storaged/storaged_info.cpp +++ b/storaged/storaged_info.cpp
@@ -18,6 +18,7 @@ #include <stdio.h> #include <string.h> +#include <sys/statvfs.h> #include <android-base/file.h> #include <android-base/parseint.h> @@ -30,13 +31,42 @@ using namespace std; using namespace android::base; -void report_storage_health() -{ - emmc_info_t mmc; - ufs_info_t ufs; +const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/"; +const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd"; +const char* emmc_info_t::emmc_ver_str[9] = { + "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1" +}; - mmc.report(); - ufs.report(); +const string ufs_info_t::health_file = "/sys/devices/soc/624000.ufshc/health"; + +static bool FileExists(const std::string& filename) +{ + struct stat buffer; + return stat(filename.c_str(), &buffer) == 0; +} + +storage_info_t* storage_info_t::get_storage_info() +{ + if (FileExists(emmc_info_t::emmc_sysfs) || + FileExists(emmc_info_t::emmc_debugfs)) { + return new emmc_info_t; + } + if (FileExists(ufs_info_t::health_file)) { + return new ufs_info_t; + } + return new storage_info_t; +} + +void storage_info_t::refresh() +{ + struct statvfs buf; + if (statvfs(userdata_path.c_str(), &buf) != 0) { + PLOG_TO(SYSTEM, WARNING) << "Failed to get userdata info"; + return; + } + + userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10; + userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10; } void storage_info_t::publish() @@ -46,13 +76,12 @@ << LOG_ID_EVENTS; } -bool emmc_info_t::report() +void emmc_info_t::report() { if (!report_sysfs() && !report_debugfs()) - return false; + return; publish(); - return true; } bool emmc_info_t::report_sysfs() @@ -136,21 +165,21 @@ return true; } -bool ufs_info_t::report() +void ufs_info_t::report() { string buffer; if (!ReadFileToString(health_file, &buffer)) { - return false; + return; } vector<string> lines = Split(buffer, "\n"); if (lines.empty()) { - return false; + return; } char rev[8]; if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) { - return false; + return; } version = "ufs " + string(rev); @@ -175,10 +204,9 @@ } if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) { - return false; + return; } publish(); - return true; }
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp index 5bb98e1..dd8bdd6 100644 --- a/storaged/storaged_uid_monitor.cpp +++ b/storaged/storaged_uid_monitor.cpp
@@ -22,33 +22,24 @@ #include <string> #include <unordered_map> +#include <android/content/pm/IPackageManagerNative.h> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/macros.h> #include <android-base/parseint.h> #include <android-base/strings.h> #include <android-base/stringprintf.h> +#include <binder/IServiceManager.h> #include <log/log_event_list.h> -#include <packagelistparser/packagelistparser.h> #include "storaged.h" #include "storaged_uid_monitor.h" using namespace android; using namespace android::base; +using namespace android::content::pm; -static bool packagelist_parse_cb(pkg_info* info, void* userdata) -{ - std::unordered_map<uint32_t, struct uid_info>* uids = - reinterpret_cast<std::unordered_map<uint32_t, struct uid_info>*>(userdata); - - if (uids->find(info->uid) != uids->end()) { - (*uids)[info->uid].name = info->name; - } - - packagelist_free(info); - return true; -} +static bool refresh_uid_names; std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats() { @@ -56,6 +47,38 @@ return get_uid_io_stats_locked(); }; +static void get_uid_names(const vector<int>& uids, const vector<std::string*>& uid_names) +{ + sp<IServiceManager> sm = defaultServiceManager(); + if (sm == NULL) { + LOG_TO(SYSTEM, ERROR) << "defaultServiceManager failed"; + return; + } + + sp<IBinder> binder = sm->getService(String16("package_native")); + if (binder == NULL) { + LOG_TO(SYSTEM, ERROR) << "getService package_native failed"; + return; + } + + sp<IPackageManagerNative> package_mgr = interface_cast<IPackageManagerNative>(binder); + std::vector<std::string> names; + binder::Status status = package_mgr->getNamesForUids(uids, &names); + if (!status.isOk()) { + LOG_TO(SYSTEM, ERROR) << "package_native::getNamesForUids failed: " + << status.exceptionMessage(); + return; + } + + for (uint32_t i = 0; i < uid_names.size(); i++) { + if (!names[i].empty()) { + *uid_names[i] = names[i]; + } + } + + refresh_uid_names = false; +} + std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_locked() { std::unordered_map<uint32_t, struct uid_info> uid_io_stats; @@ -67,7 +90,8 @@ std::vector<std::string> io_stats = Split(buffer, "\n"); struct uid_info u; - bool refresh_uid = false; + vector<int> uids; + vector<std::string*> uid_names; for (uint32_t i = 0; i < io_stats.size(); i++) { if (io_stats[i].empty()) { @@ -91,17 +115,19 @@ continue; } - if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) { - refresh_uid = true; - u.name = std::to_string(u.uid); - } else { - u.name = last_uid_io_stats[u.uid].name; - } uid_io_stats[u.uid] = u; + uid_io_stats[u.uid].name = std::to_string(u.uid); + uids.push_back(u.uid); + uid_names.push_back(&uid_io_stats[u.uid].name); + if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) { + refresh_uid_names = true; + } else { + uid_io_stats[u.uid].name = last_uid_io_stats[u.uid].name; + } } - if (refresh_uid) { - packagelist_parse(packagelist_parse_cb, &uid_io_stats); + if (!uids.empty() && refresh_uid_names) { + get_uid_names(uids, uid_names); } return uid_io_stats; @@ -228,13 +254,13 @@ last_uid_io_stats[uid.uid].io[BACKGROUND].write_bytes; usage.bytes[READ][FOREGROUND][charger_stat] += - (fg_rd_delta < 0) ? uid.io[FOREGROUND].read_bytes : fg_rd_delta; + (fg_rd_delta < 0) ? 0 : fg_rd_delta; usage.bytes[READ][BACKGROUND][charger_stat] += - (bg_rd_delta < 0) ? uid.io[BACKGROUND].read_bytes : bg_rd_delta; + (bg_rd_delta < 0) ? 0 : bg_rd_delta; usage.bytes[WRITE][FOREGROUND][charger_stat] += - (fg_wr_delta < 0) ? uid.io[FOREGROUND].write_bytes : fg_wr_delta; + (fg_wr_delta < 0) ? 0 : fg_wr_delta; usage.bytes[WRITE][BACKGROUND][charger_stat] += - (bg_wr_delta < 0) ? uid.io[BACKGROUND].write_bytes : bg_wr_delta; + (bg_wr_delta < 0) ? 0 : bg_wr_delta; } last_uid_io_stats = uid_io_stats;
diff --git a/toolbox/upstream-netbsd/include/sys/mtio.h b/toolbox/upstream-netbsd/include/sys/mtio.h deleted file mode 100644 index 8fb5655..0000000 --- a/toolbox/upstream-netbsd/include/sys/mtio.h +++ /dev/null
@@ -1 +0,0 @@ -#include <linux/mtio.h>
diff --git a/trusty/Android.bp b/trusty/Android.bp index 386fbe6..2fb2e19 100644 --- a/trusty/Android.bp +++ b/trusty/Android.bp
@@ -2,6 +2,5 @@ "gatekeeper", "keymaster", "libtrusty", - "nvram", "storage/*", ]
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp index a9566a1..65b271a 100644 --- a/trusty/gatekeeper/Android.bp +++ b/trusty/gatekeeper/Android.bp
@@ -22,6 +22,7 @@ cc_library_shared { name: "gatekeeper.trusty", + vendor: true, relative_install_path: "hw", @@ -43,4 +44,5 @@ "libcutils", "libtrusty", ], + header_libs: ["libhardware_headers"], }
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp index 6b9d723..0820fa0 100644 --- a/trusty/keymaster/Android.bp +++ b/trusty/keymaster/Android.bp
@@ -25,6 +25,7 @@ // and ECDSA keys. cc_binary { name: "trusty_keymaster_tipc", + vendor: true, srcs: [ "trusty_keymaster_device.cpp", "trusty_keymaster_ipc.cpp", @@ -45,6 +46,7 @@ // keystore.trusty is the HAL used by keystore on Trusty devices. cc_library_shared { name: "keystore.trusty", + vendor: true, relative_install_path: "hw", srcs: [ "module.cpp", @@ -65,4 +67,5 @@ "liblog", "libcutils", ], + header_libs: ["libhardware_headers"], }
diff --git a/trusty/keymaster/trusty_keymaster_device_test.cpp b/trusty/keymaster/trusty_keymaster_device_test.cpp index 3bb5430..e8f5c0b 100644 --- a/trusty/keymaster/trusty_keymaster_device_test.cpp +++ b/trusty/keymaster/trusty_keymaster_device_test.cpp
@@ -16,8 +16,8 @@ #include <algorithm> #include <fstream> -#include <UniquePtr.h> #include <gtest/gtest.h> +#include <nativehelper/UniquePtr.h> #include <openssl/engine.h> #include <hardware/keymaster0.h>
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp index f316da2..88d6240 100644 --- a/trusty/libtrusty/Android.bp +++ b/trusty/libtrusty/Android.bp
@@ -18,6 +18,7 @@ cc_library { name: "libtrusty", + vendor: true, srcs: ["trusty.c"], export_include_dirs: ["include"],
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp index cb00fe7..25a3cb0 100644 --- a/trusty/libtrusty/tipc-test/Android.bp +++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -14,12 +14,14 @@ cc_test { name: "tipc-test", - static_executable: true, + vendor: true, srcs: ["tipc_test.c"], static_libs: [ - "libc", "libtrusty", + ], + shared_libs: [ + "libc", "liblog", ], gtest: false,
diff --git a/trusty/nvram/Android.bp b/trusty/nvram/Android.bp deleted file mode 100644 index 15e6c3e..0000000 --- a/trusty/nvram/Android.bp +++ /dev/null
@@ -1,61 +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. -// - -// nvram.trusty is the Trusty NVRAM HAL module. -cc_library_shared { - name: "nvram.trusty", - relative_install_path: "hw", - srcs: [ - "module.c", - "trusty_nvram_device.cpp", - "trusty_nvram_implementation.cpp", - ], - - cflags: [ - "-Wall", - "-Werror", - "-Wextra", - "-fvisibility=hidden", - ], - static_libs: ["libnvram-hal"], - shared_libs: [ - "libtrusty", - "libnvram-messages", - "liblog", - ], -} - -// nvram-wipe is a helper tool for clearing NVRAM state. -cc_binary { - name: "nvram-wipe", - srcs: [ - "nvram_wipe.cpp", - "trusty_nvram_implementation.cpp", - ], - - cflags: [ - "-Wall", - "-Werror", - "-Wextra", - "-fvisibility=hidden", - ], - static_libs: ["libnvram-hal"], - shared_libs: [ - "libtrusty", - "libnvram-messages", - "liblog", - ], -}
diff --git a/trusty/nvram/module.c b/trusty/nvram/module.c deleted file mode 100644 index a2e64d3..0000000 --- a/trusty/nvram/module.c +++ /dev/null
@@ -1,39 +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 <hardware/nvram.h> - -// This function is defined in trusty_nvram_device.cpp. -int trusty_nvram_open(const hw_module_t* module, - const char* device_id, - hw_device_t** device_ptr); - -static struct hw_module_methods_t nvram_module_methods = { - .open = trusty_nvram_open, -}; - -struct nvram_module HAL_MODULE_INFO_SYM - __attribute__((visibility("default"))) = { - .common = {.tag = HARDWARE_MODULE_TAG, - .module_api_version = NVRAM_MODULE_API_VERSION_0_1, - .hal_api_version = HARDWARE_HAL_API_VERSION, - .id = NVRAM_HARDWARE_MODULE_ID, - .name = "Trusty NVRAM HAL", - .author = "The Android Open Source Project", - .methods = &nvram_module_methods, - .dso = 0, - .reserved = {}}, -};
diff --git a/trusty/nvram/nvram_wipe.cpp b/trusty/nvram/nvram_wipe.cpp deleted file mode 100644 index d0f4fad..0000000 --- a/trusty/nvram/nvram_wipe.cpp +++ /dev/null
@@ -1,66 +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 <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <nvram/messages/nvram_messages.h> - -#include "trusty_nvram_implementation.h" - -void usage(const char* program_name) { - fprintf(stderr, "Usage: %s [status|disable|wipe]\n", program_name); - exit(-1); -} - -int main(int argc, char* argv[]) { - if (argc < 2) { - usage(argv[0]); - } - - nvram::TrustyNvramImplementation nvram_proxy; - nvram::Request request; - nvram::Response response; - - if (!strcmp(argv[1], "status")) { - request.payload.Activate<nvram::COMMAND_GET_INFO>(); - nvram_proxy.Execute(request, &response); - const nvram::GetInfoResponse* get_info_response = - response.payload.get<nvram::COMMAND_GET_INFO>(); - if (response.result == NV_RESULT_SUCCESS) { - int status = get_info_response && get_info_response->wipe_disabled; - printf("Wiping disabled: %d\n", status); - return status; - } - } else if (!strcmp(argv[1], "disable")) { - request.payload.Activate<nvram::COMMAND_DISABLE_WIPE>(); - nvram_proxy.Execute(request, &response); - } else if (!strcmp(argv[1], "wipe")) { - request.payload.Activate<nvram::COMMAND_WIPE_STORAGE>(); - nvram_proxy.Execute(request, &response); - } else { - usage(argv[0]); - } - - if (response.result != NV_RESULT_SUCCESS) { - fprintf(stderr, "Command execution failure: %u\n", response.result); - return -1; - } - - return 0; -} -
diff --git a/trusty/nvram/trusty_nvram_device.cpp b/trusty/nvram/trusty_nvram_device.cpp deleted file mode 100644 index 2c50915..0000000 --- a/trusty/nvram/trusty_nvram_device.cpp +++ /dev/null
@@ -1,32 +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 <nvram/hal/nvram_device_adapter.h> - -#include "trusty_nvram_implementation.h" - -extern "C" int trusty_nvram_open(const hw_module_t* module, - const char* device_id, - hw_device_t** device_ptr) { - if (strcmp(NVRAM_HARDWARE_DEVICE_ID, device_id) != 0) { - return -EINVAL; - } - - nvram::NvramDeviceAdapter* adapter = new nvram::NvramDeviceAdapter( - module, new nvram::TrustyNvramImplementation); - *device_ptr = adapter->as_device(); - return 0; -}
diff --git a/trusty/nvram/trusty_nvram_implementation.cpp b/trusty/nvram/trusty_nvram_implementation.cpp deleted file mode 100644 index 9215c85..0000000 --- a/trusty/nvram/trusty_nvram_implementation.cpp +++ /dev/null
@@ -1,113 +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. - */ - -#define LOG_TAG "TrustyNVRAM" - -#include "trusty_nvram_implementation.h" - -#include <errno.h> -#include <string.h> -#include <unistd.h> - -#include <hardware/nvram.h> -#include <log/log.h> -#include <trusty/tipc.h> - -#include <nvram/messages/blob.h> - -namespace nvram { -namespace { - -// Character device to open for Trusty IPC connections. -const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0"; - -// App identifier of the NVRAM app. -const char kTrustyNvramAppId[] = "com.android.trusty.nvram"; - -} // namespace - -TrustyNvramImplementation::~TrustyNvramImplementation() { - if (tipc_nvram_fd_ != -1) { - tipc_close(tipc_nvram_fd_); - tipc_nvram_fd_ = -1; - } -} - -void TrustyNvramImplementation::Execute(const nvram::Request& request, - nvram::Response* response) { - if (!SendRequest(request, response)) { - response->result = NV_RESULT_INTERNAL_ERROR; - } -} - -bool TrustyNvramImplementation::Connect() { - if (tipc_nvram_fd_ != -1) { - return true; - } - - int rc = tipc_connect(kTrustyDeviceName, kTrustyNvramAppId); - if (rc < 0) { - ALOGE("Failed to connect to Trusty NVRAM app: %s\n", strerror(-rc)); - return false; - } - - tipc_nvram_fd_ = rc; - return true; -} - -bool TrustyNvramImplementation::SendRequest(const nvram::Request& request, - nvram::Response* response) { - if (!Connect()) { - return false; - } - - nvram::Blob request_buffer; - if (!nvram::Encode(request, &request_buffer)) { - ALOGE("Failed to encode NVRAM request.\n"); - return false; - } - - ssize_t rc = - write(tipc_nvram_fd_, request_buffer.data(), request_buffer.size()); - if (rc < 0) { - ALOGE("Failed to send NVRAM request: %s\n", strerror(-rc)); - return false; - } - if (static_cast<size_t>(rc) != request_buffer.size()) { - ALOGE("Failed to send full request buffer: %zd\n", rc); - return false; - } - - rc = read(tipc_nvram_fd_, response_buffer_, sizeof(response_buffer_)); - if (rc < 0) { - ALOGE("Failed to read NVRAM response: %s\n", strerror(-rc)); - return false; - } - - if (static_cast<size_t>(rc) >= sizeof(response_buffer_)) { - ALOGE("NVRAM response exceeds response buffer size.\n"); - return false; - } - - if (!nvram::Decode(response_buffer_, static_cast<size_t>(rc), response)) { - ALOGE("Failed to decode NVRAM response.\n"); - return false; - } - - return true; -} - -} // namespace nvram
diff --git a/trusty/nvram/trusty_nvram_implementation.h b/trusty/nvram/trusty_nvram_implementation.h deleted file mode 100644 index 60758f7..0000000 --- a/trusty/nvram/trusty_nvram_implementation.h +++ /dev/null
@@ -1,59 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TRUSTY_NVRAM_TRUSTY_NVRAM_IMPLEMENTATION_H_ -#define TRUSTY_NVRAM_TRUSTY_NVRAM_IMPLEMENTATION_H_ - -#include <stdint.h> - -#include <nvram/hal/nvram_device_adapter.h> -#include <nvram/messages/nvram_messages.h> - -namespace nvram { - -// |TrustyNvramImplementation| proxies requests to the Trusty NVRAM app. It -// serializes the request objects, sends it to the Trusty app and finally reads -// back the result and decodes it. -class TrustyNvramImplementation : public nvram::NvramImplementation { - public: - ~TrustyNvramImplementation() override; - - void Execute(const nvram::Request& request, - nvram::Response* response) override; - - private: - // Connects the IPC channel to the Trusty app if it is not already open. - // Returns true if the channel is open, false on errors. - bool Connect(); - - // Dispatches a command to the trust app. Returns true if successful (note - // that the response may still indicate an error on the Trusty side), false if - // there are any I/O or encoding/decoding errors. - bool SendRequest(const nvram::Request& request, - nvram::Response* response); - - // The file descriptor for the IPC connection to the Trusty app. - int tipc_nvram_fd_ = -1; - - // Response buffer. This puts a hard size limit on the responses from the - // Trusty app. 4096 matches the maximum IPC message size currently supported - // by Trusty. - uint8_t response_buffer_[4096]; -}; - -} // namespace nvram - -#endif // TRUSTY_NVRAM_TRUSTY_NVRAM_IMPLEMENTATION_H_
diff --git a/trusty/storage/interface/Android.bp b/trusty/storage/interface/Android.bp index a551c37..18b4a5f 100644 --- a/trusty/storage/interface/Android.bp +++ b/trusty/storage/interface/Android.bp
@@ -16,5 +16,6 @@ cc_library_static { name: "libtrustystorageinterface", + vendor: true, export_include_dirs: ["include"], }
diff --git a/trusty/storage/lib/Android.bp b/trusty/storage/lib/Android.bp index 5eb3f07..4e41674 100644 --- a/trusty/storage/lib/Android.bp +++ b/trusty/storage/lib/Android.bp
@@ -16,16 +16,19 @@ cc_library_static { name: "libtrustystorage", + vendor: true, srcs: ["storage.c"], export_include_dirs: ["include"], static_libs: [ - "liblog", "libtrusty", "libtrustystorageinterface", ], + shared_libs: [ + "liblog", + ], cflags: [ "-fvisibility=hidden",
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp index eb34df0..da8542d 100644 --- a/trusty/storage/proxy/Android.bp +++ b/trusty/storage/proxy/Android.bp
@@ -16,6 +16,7 @@ cc_binary { name: "storageproxyd", + vendor: true, srcs: [ "ipc.c", @@ -25,6 +26,7 @@ ], shared_libs: ["liblog"], + header_libs: ["libcutils_headers"], static_libs: [ "libtrustystorageinterface",
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c index 27e5891..41263e5 100644 --- a/trusty/storage/proxy/proxy.c +++ b/trusty/storage/proxy/proxy.c
@@ -24,7 +24,7 @@ #include <sys/stat.h> #include <unistd.h> -#include <private/android_filesystem_config.h> +#include <cutils/android_filesystem_config.h> #include "ipc.h" #include "log.h"
diff --git a/trusty/storage/tests/Android.bp b/trusty/storage/tests/Android.bp index 3eff3f2..1b003e9 100644 --- a/trusty/storage/tests/Android.bp +++ b/trusty/storage/tests/Android.bp
@@ -16,6 +16,7 @@ cc_test { name: "secure-storage-unit-test", + vendor: true, cflags: [ "-g", @@ -29,6 +30,8 @@ "libtrustystorageinterface", "libtrustystorage", "libtrusty", + ], + shared_libs: [ "liblog", ],
diff --git a/tzdatacheck/Android.bp b/tzdatacheck/Android.bp deleted file mode 100644 index 00ad141..0000000 --- a/tzdatacheck/Android.bp +++ /dev/null
@@ -1,14 +0,0 @@ -// ======================================================== -// Executable -// ======================================================== -cc_binary { - name: "tzdatacheck", - host_supported: true, - srcs: ["tzdatacheck.cpp"], - shared_libs: [ - "libbase", - "libcutils", - "liblog", - ], - cflags: ["-Werror"], -}
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp deleted file mode 100644 index 8fcd17f..0000000 --- a/tzdatacheck/tzdatacheck.cpp +++ /dev/null
@@ -1,601 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <ctype.h> -#include <errno.h> -#include <ftw.h> -#include <libgen.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <iostream> -#include <memory> -#include <string> -#include <vector> - -#include "android-base/logging.h" - -// The name of the directory that holds a staged time zone update distro. If this exists it should -// replace the one in CURRENT_DIR_NAME. -// See also libcore.tzdata.update2.TimeZoneDistroInstaller. -static const char* STAGED_DIR_NAME = "/staged"; - -// The name of the directory that holds the (optional) installed time zone update distro. -// See also libcore.tzdata.update2.TimeZoneDistroInstaller. -static const char* CURRENT_DIR_NAME = "/current"; - -// The name of a file in the staged dir that indicates the staged operation is an "uninstall". -// See also libcore.tzdata.update2.TimeZoneDistroInstaller. -static const char* UNINSTALL_TOMBSTONE_FILE_NAME = "/STAGED_UNINSTALL_TOMBSTONE"; - -// The name of the file containing the distro version information. -// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion. -static const char* DISTRO_VERSION_FILENAME = "/distro_version"; - -// distro_version is an ASCII file consisting of 17 bytes in the form: AAA.BBB|CCCCC|DDD -// AAA.BBB is the major/minor version of the distro format (e.g. 001.001), -// CCCCC is the rules version (e.g. 2016g) -// DDD is the android revision for this rules version to allow for distro corrections (e.g. 001) -// We only need the first 13 to determine if it is suitable for the device. -static const int DISTRO_VERSION_LENGTH = 13; - -// The major version of the distro format supported by this code as a null-terminated char[]. -// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion. -static const char SUPPORTED_DISTRO_MAJOR_VERSION[] = "001"; - -// The length of the distro format major version excluding the \0 -static const size_t SUPPORTED_DISTRO_MAJOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MAJOR_VERSION) - 1; - -// The minor version of the distro format supported by this code as a null-terminated char[]. -// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion. -static const char SUPPORTED_DISTRO_MINOR_VERSION[] = "001"; - -// The length of the distro format minor version excluding the \0 -static const size_t SUPPORTED_DISTRO_MINOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MINOR_VERSION) - 1; - -// The length of the distro format version. e.g. 001.001 -static const size_t SUPPORTED_DISTRO_VERSION_LEN = - SUPPORTED_DISTRO_MAJOR_VERSION_LEN + SUPPORTED_DISTRO_MINOR_VERSION_LEN + 1; - -// The length of the IANA rules version bytes. e.g. 2016a -static const size_t RULES_VERSION_LEN = 5; - -// Distro version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC -static const size_t DISTRO_VERSION_RULES_IDX = 8; - -// See also libcore.tzdata.shared2.TimeZoneDistro. -static const char* TZDATA_FILENAME = "/tzdata"; - -// tzdata file header (as much as we need for the version): -// byte[11] tzdata_version -- e.g. "tzdata2012f" -static const int TZ_HEADER_LENGTH = 11; - -static const char TZ_DATA_HEADER_PREFIX[] = "tzdata"; -static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0 - -static void usage() { - std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n" - "\n" - "Checks whether any timezone update distro in DATA_TZ_DIR is compatible with the\n" - "current Android release and better than or the same as base system timezone rules in\n" - "SYSTEM_TZ_DIR. If the timezone rules in SYSTEM_TZ_DIR are a higher version than the\n" - "one in DATA_TZ_DIR the DATA_TZ_DIR is renamed and then deleted.\n"; - exit(1); -} - -/* - * Opens a file and fills buffer with the first byteCount bytes from the file. - * If the file does not exist or cannot be opened or is too short then false is returned. - * If the bytes were read successfully then true is returned. - */ -static bool readBytes(const std::string& fileName, char* buffer, size_t byteCount) { - FILE* file = fopen(fileName.c_str(), "r"); - if (file == nullptr) { - if (errno != ENOENT) { - PLOG(WARNING) << "Error opening file " << fileName; - } - return false; - } - size_t bytesRead = fread(buffer, 1, byteCount, file); - fclose(file); - if (bytesRead != byteCount) { - LOG(WARNING) << fileName << " is too small. " << byteCount << " bytes required"; - return false; - } - return true; -} - -/* - * Checks the contents of headerBytes. Returns true if it is valid (starts with "tzdata"), false - * otherwise. - */ -static bool checkValidTzDataHeader(const std::string& fileName, const char* headerBytes) { - if (strncmp("tzdata", headerBytes, 6) != 0) { - LOG(WARNING) << fileName << " does not start with the expected bytes (tzdata)"; - return false; - } - return true; -} - -static bool checkDigits(const char* buffer, const size_t count, size_t* i) { - for (size_t j = 0; j < count; j++) { - char toCheck = buffer[(*i)++]; - if (!isdigit(toCheck)) { - return false; - } - } - return true; -} - -static bool checkValidDistroVersion(const char* buffer) { - // See DISTRO_VERSION_LENGTH comments above for a description of the format. - size_t i = 0; - if (!checkDigits(buffer, 3, &i)) { - return false; - } - if (buffer[i++] != '.') { - return false; - } - if (!checkDigits(buffer, 3, &i)) { - return false; - } - if (buffer[i++] != '|') { - return false; - } - if (!checkDigits(buffer, 4, &i)) { - return false; - } - // Ignore the last character. It is assumed to be a letter but we don't check because it's not - // obvious what would happen at 'z'. - return true; -} - -/* Return the parent directory of dirName. */ -static std::string getParentDir(const std::string& dirName) { - std::unique_ptr<char> mutable_dirname(strdup(dirName.c_str())); - return dirname(mutable_dirname.get()); -} - -/* Deletes a single file, symlink or directory. Called from nftw(). */ -static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct FTW*) { - LOG(DEBUG) << "Inspecting " << fpath; - switch (typeflag) { - case FTW_F: - case FTW_SL: - LOG(DEBUG) << "Unlinking " << fpath; - if (unlink(fpath)) { - PLOG(WARNING) << "Failed to unlink file/symlink " << fpath; - } - break; - case FTW_D: - case FTW_DP: - LOG(DEBUG) << "Removing dir " << fpath; - if (rmdir(fpath)) { - PLOG(WARNING) << "Failed to remove dir " << fpath; - } - break; - default: - LOG(WARNING) << "Unsupported file type " << fpath << ": " << typeflag; - break; - } - return 0; -} - -enum PathStatus { ERR, NONE, IS_DIR, IS_REG, UNKNOWN }; - -static PathStatus checkPath(const std::string& path) { - struct stat buf; - if (stat(path.c_str(), &buf) != 0) { - if (errno != ENOENT) { - PLOG(WARNING) << "Unable to stat " << path; - return ERR; - } - return NONE; - } - return S_ISDIR(buf.st_mode) ? IS_DIR : S_ISREG(buf.st_mode) ? IS_REG : UNKNOWN; -} - -/* - * Deletes fileToDelete and returns true if it is successful. If fileToDelete is not a file or - * cannot be accessed this method returns false. - */ -static bool deleteFile(const std::string& fileToDelete) { - // Check whether the file exists. - PathStatus pathStatus = checkPath(fileToDelete); - if (pathStatus == NONE) { - LOG(INFO) << "Path " << fileToDelete << " does not exist"; - return true; - } - if (pathStatus != IS_REG) { - LOG(WARNING) << "Path " << fileToDelete << " failed to stat() or is not a file."; - return false; - } - - // Attempt the deletion. - int rc = unlink(fileToDelete.c_str()); - if (rc != 0) { - PLOG(WARNING) << "unlink() failed for " << fileToDelete; - } - return rc == 0; -} - -/* - * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out - * of the way. If dirToDelete does not exist this function does nothing and returns true. If - * dirToDelete is not a directory or cannot be accessed this method returns false. - * - * During deletion, this function first renames the directory to a temporary name. If the temporary - * directory cannot be created, or the directory cannot be renamed, false is returned. After the - * rename, deletion of files and subdirs beneath the directory is performed on a "best effort" - * basis. Symlinks beneath the directory are not followed. - */ -static bool deleteDir(const std::string& dirToDelete) { - // Check whether the dir exists. - int pathStatus = checkPath(dirToDelete); - if (pathStatus == NONE) { - LOG(INFO) << "Path " << dirToDelete << " does not exist"; - return true; - } - if (pathStatus != IS_DIR) { - LOG(WARNING) << "Path " << dirToDelete << " failed to stat() or is not a directory."; - return false; - } - - // First, rename dirToDelete. - - std::string tempDirNameTemplate = getParentDir(dirToDelete); - tempDirNameTemplate += "/tempXXXXXX"; - - // Create an empty directory with the temporary name. For this we need a non-const char*. - std::vector<char> tempDirName(tempDirNameTemplate.length() + 1); - strcpy(&tempDirName[0], tempDirNameTemplate.c_str()); - if (mkdtemp(&tempDirName[0]) == nullptr) { - PLOG(WARNING) << "Unable to create a temporary directory: " << tempDirNameTemplate; - return false; - } - - // Rename dirToDelete to tempDirName (replacing the empty tempDirName directory created above). - int rc = rename(dirToDelete.c_str(), &tempDirName[0]); - if (rc == -1) { - PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to " - << &tempDirName[0]; - return false; - } - - // Recursively delete contents of tempDirName. - - rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */, - FTW_DEPTH | FTW_MOUNT | FTW_PHYS); - if (rc == -1) { - LOG(INFO) << "Could not delete directory: " << &tempDirName[0]; - } - return true; -} - -/* - * Deletes the ConfigInstaller metadata directory. - * TODO(nfuller). http://b/31008728 Remove this when ConfigInstaller is no longer used. - */ -static void deleteConfigUpdaterMetadataDir(const char* dataZoneInfoDir) { - // Delete the update metadata - std::string dataUpdatesDirName(dataZoneInfoDir); - dataUpdatesDirName += "/updates"; - LOG(INFO) << "Removing: " << dataUpdatesDirName; - if (!deleteDir(dataUpdatesDirName)) { - LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName - << " was not successful"; - } -} - -/* - * Deletes the timezone update distro directory. - */ -static void deleteUpdateDistroDir(const std::string& distroDirName) { - LOG(INFO) << "Removing: " << distroDirName; - if (!deleteDir(distroDirName)) { - LOG(WARNING) << "Deletion of distro dir " << distroDirName << " was not successful"; - } -} - -static void handleStagedUninstall(const std::string& dataStagedDirName, - const std::string& dataCurrentDirName, - const PathStatus dataCurrentDirStatus) { - LOG(INFO) << "Staged operation is an uninstall."; - - // Delete the current install directory. - switch (dataCurrentDirStatus) { - case NONE: - // This is unexpected: No uninstall should be staged if there is nothing to - // uninstall. Carry on anyway. - LOG(WARNING) << "No current install to delete."; - break; - case IS_DIR: - // This is normal. Delete the current install dir. - if (!deleteDir(dataCurrentDirName)) { - LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName - << " was not successful"; - // If this happens we don't know whether we were able to delete or not. We don't - // delete the staged operation so it will be retried next boot unless overridden. - return; - } - break; - case IS_REG: - default: - // This is unexpected: We can try to delete the unexpected file and carry on. - LOG(WARNING) << "Current distro dir " << dataCurrentDirName - << " is not actually a directory. Attempting deletion."; - if (!deleteFile(dataCurrentDirName)) { - LOG(WARNING) << "Could not delete " << dataCurrentDirName; - return; - } - break; - } - - // Delete the staged uninstall dir. - if (!deleteDir(dataStagedDirName)) { - LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName - << " was not successful"; - // If this happens we don't know whether we were able to delete the staged operation - // or not. - return; - } - LOG(INFO) << "Staged uninstall complete."; -} - -static void handleStagedInstall(const std::string& dataStagedDirName, - const std::string& dataCurrentDirName, - const PathStatus dataCurrentDirStatus) { - LOG(INFO) << "Staged operation is an install."; - - switch (dataCurrentDirStatus) { - case NONE: - // This is expected: This is the first install. - LOG(INFO) << "No current install to replace."; - break; - case IS_DIR: - // This is expected: We are replacing an existing install. - // Delete the current dir so we can replace it. - if (!deleteDir(dataCurrentDirName)) { - LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName - << " was not successful"; - // If this happens, we cannot proceed. - return; - } - break; - case IS_REG: - default: - // This is unexpected: We can try to delete the unexpected file and carry on. - LOG(WARNING) << "Current distro dir " << dataCurrentDirName - << " is not actually a directory. Attempting deletion."; - if (!deleteFile(dataCurrentDirName)) { - LOG(WARNING) << "Could not delete " << dataCurrentDirName; - return; - } - break; - } - - // Move the staged dir so it is the new current dir, completing the install. - LOG(INFO) << "Moving " << dataStagedDirName << " to " << dataCurrentDirName; - int rc = rename(dataStagedDirName.c_str(), dataCurrentDirName.c_str()); - if (rc == -1) { - PLOG(WARNING) << "Unable to rename directory from " << dataStagedDirName << " to " - << &dataCurrentDirName[0]; - return; - } - - LOG(INFO) << "Staged install complete."; -} -/* - * Process a staged operation if there is one. - */ -static void processStagedOperation(const std::string& dataStagedDirName, - const std::string& dataCurrentDirName) { - PathStatus dataStagedDirStatus = checkPath(dataStagedDirName); - - // Exit early for the common case. - if (dataStagedDirStatus == NONE) { - LOG(DEBUG) << "No staged time zone operation."; - return; - } - - // Check known directory names are in a good starting state. - if (dataStagedDirStatus != IS_DIR) { - LOG(WARNING) << "Staged distro dir " << dataStagedDirName - << " could not be accessed or is not a directory." - << " stagedDirStatus=" << dataStagedDirStatus; - return; - } - - // dataStagedDirStatus == IS_DIR. - - // Work out whether there is anything currently installed. - PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName); - if (dataCurrentDirStatus == ERR) { - LOG(WARNING) << "Current install dir " << dataCurrentDirName << " could not be accessed" - << " dataCurrentDirStatus=" << dataCurrentDirStatus; - return; - } - - // We must perform the staged operation. - - // Check to see if the staged directory contains an uninstall or an install operation. - std::string uninstallTombStoneFile(dataStagedDirName); - uninstallTombStoneFile += UNINSTALL_TOMBSTONE_FILE_NAME; - int uninstallTombStoneFileStatus = checkPath(uninstallTombStoneFile); - if (uninstallTombStoneFileStatus != IS_REG && uninstallTombStoneFileStatus != NONE) { - // Error case. - LOG(WARNING) << "Unable to determine if the staged operation is an uninstall."; - return; - } - if (uninstallTombStoneFileStatus == IS_REG) { - handleStagedUninstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus); - } else { - // uninstallTombStoneFileStatus == NONE meaning this is a staged install. - handleStagedInstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus); - } -} - -/* - * After a platform update it is likely that timezone data found on the system partition will be - * newer than the version found in the data partition. This tool detects this case and removes the - * version in /data. - * - * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The - * paths for the metadata and current timezone data must match. - * - * Typically on device the two args will be: - * /system/usr/share/zoneinfo /data/misc/zoneinfo - * - * See usage() for usage notes. - */ -int main(int argc, char* argv[]) { - if (argc != 3) { - usage(); - return 1; - } - - const char* systemZoneInfoDir = argv[1]; - const char* dataZoneInfoDir = argv[2]; - - std::string dataStagedDirName(dataZoneInfoDir); - dataStagedDirName += STAGED_DIR_NAME; - - std::string dataCurrentDirName(dataZoneInfoDir); - dataCurrentDirName += CURRENT_DIR_NAME; - - // Check for an process any staged operation. - // If the staged operation could not be handled we still have to validate the current installed - // directory so we do not check for errors and do not quit early. - processStagedOperation(dataStagedDirName, dataCurrentDirName); - - // Check the distro directory exists. If it does not, exit quickly: nothing to do. - PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName); - if (dataCurrentDirStatus == NONE) { - LOG(INFO) << "timezone distro dir " << dataCurrentDirName - << " does not exist. No action required."; - return 0; - } - - // If the distro directory path is not a directory or we can't stat() the path, exit with a - // warning: either there's a problem accessing storage or the world is not as it should be; - // nothing to do. - if (dataCurrentDirStatus != IS_DIR) { - LOG(WARNING) << "Current distro dir " << dataCurrentDirName - << " could not be accessed or is not a directory. result=" << dataCurrentDirStatus; - return 2; - } - - // Check the installed distro version. - std::string distroVersionFileName(dataCurrentDirName); - distroVersionFileName += DISTRO_VERSION_FILENAME; - std::vector<char> distroVersion; - distroVersion.reserve(DISTRO_VERSION_LENGTH); - bool distroVersionReadOk = - readBytes(distroVersionFileName, distroVersion.data(), DISTRO_VERSION_LENGTH); - if (!distroVersionReadOk) { - LOG(WARNING) << "distro version file " << distroVersionFileName - << " does not exist or is too short. Deleting distro dir."; - // Implies the contents of the data partition is corrupt in some way. Try to clean up. - deleteConfigUpdaterMetadataDir(dataZoneInfoDir); - deleteUpdateDistroDir(dataCurrentDirName); - return 3; - } - - if (!checkValidDistroVersion(distroVersion.data())) { - LOG(WARNING) << "distro version file " << distroVersionFileName - << " is not valid. Deleting distro dir."; - // Implies the contents of the data partition is corrupt in some way. Try to clean up. - deleteConfigUpdaterMetadataDir(dataZoneInfoDir); - deleteUpdateDistroDir(dataCurrentDirName); - return 4; - } - - std::string actualDistroVersion = - std::string(distroVersion.data(), SUPPORTED_DISTRO_VERSION_LEN); - // Check the first 3 bytes of the distro version: these are the major version (e.g. 001). - // It must match the one we support exactly to be ok. - if (strncmp( - &distroVersion[0], - SUPPORTED_DISTRO_MAJOR_VERSION, - SUPPORTED_DISTRO_MAJOR_VERSION_LEN) != 0) { - - LOG(INFO) << "distro version file " << distroVersionFileName - << " major version is not the required version " << SUPPORTED_DISTRO_MAJOR_VERSION - << ", was \"" << actualDistroVersion << "\". Deleting distro dir."; - // This implies there has been an OTA and the installed distro is not compatible with the - // new version of Android. Remove the installed distro. - deleteConfigUpdaterMetadataDir(dataZoneInfoDir); - deleteUpdateDistroDir(dataCurrentDirName); - return 5; - } - - // Check the last 3 bytes of the distro version: these are the minor version (e.g. 001). - // If the version in the distro is < the minor version required by this device it cannot be - // used. - if (strncmp( - &distroVersion[4], - SUPPORTED_DISTRO_MINOR_VERSION, - SUPPORTED_DISTRO_MINOR_VERSION_LEN) < 0) { - - LOG(INFO) << "distro version file " << distroVersionFileName - << " minor version is not the required version " << SUPPORTED_DISTRO_MINOR_VERSION - << ", was \"" << actualDistroVersion << "\". Deleting distro dir."; - // This implies there has been an OTA and the installed distro is not compatible with the - // new version of Android. Remove the installed distro. - deleteConfigUpdaterMetadataDir(dataZoneInfoDir); - deleteUpdateDistroDir(dataCurrentDirName); - return 5; - } - - // Read the system rules version out of the /system tzdata file. - std::string systemTzDataFileName(systemZoneInfoDir); - systemTzDataFileName += TZDATA_FILENAME; - std::vector<char> systemTzDataHeader; - systemTzDataHeader.reserve(TZ_HEADER_LENGTH); - bool systemFileExists = - readBytes(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH); - if (!systemFileExists) { - // Implies the contents of the system partition is corrupt in some way. Nothing we can do. - LOG(WARNING) << systemTzDataFileName << " does not exist or could not be opened"; - return 6; - } - if (!checkValidTzDataHeader(systemTzDataFileName, systemTzDataHeader.data())) { - // Implies the contents of the system partition is corrupt in some way. Nothing we can do. - LOG(WARNING) << systemTzDataFileName << " does not have a valid header."; - return 7; - } - - // Compare the distro rules version against the system rules version. - if (strncmp( - &systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN], - &distroVersion[DISTRO_VERSION_RULES_IDX], - RULES_VERSION_LEN) <= 0) { - LOG(INFO) << "Found an installed distro but it is valid. No action taken."; - // Implies there is an installed update, but it is good. - return 0; - } - - // Implies there has been an OTA and the system version of the timezone rules is now newer - // than the version installed in /data. Remove the installed distro. - LOG(INFO) << "timezone distro in " << dataCurrentDirName << " is older than data in " - << systemTzDataFileName << "; fixing..."; - - deleteConfigUpdaterMetadataDir(dataZoneInfoDir); - deleteUpdateDistroDir(dataCurrentDirName); - return 0; -}