Merge "init: rename mke2fs tools with _static suffix" into oc-dr1-dev
am: 5c5c544e80
Change-Id: Ifee5b6516fe3aee03df845a46421753295cbc740
diff --git a/adb/adb.cpp b/adb/adb.cpp
index bfb1144..ff7b71f 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"
@@ -656,6 +657,26 @@
#endif
+static void ReportServerStartupFailure(pid_t pid) {
+ fprintf(stderr, "ADB server didn't ACK\n");
+ fprintf(stderr, "Full server startup log: %s\n", GetLogFilePath().c_str());
+ fprintf(stderr, "Server had pid: %d\n", pid);
+
+ unique_fd fd(adb_open(GetLogFilePath().c_str(), O_RDONLY));
+ if (fd == -1) return;
+
+ // Let's not show more than 128KiB of log...
+ adb_lseek(fd, -128 * 1024, SEEK_END);
+ std::string content;
+ if (!android::base::ReadFdToString(fd, &content)) return;
+
+ std::string header = android::base::StringPrintf("--- adb starting (pid %d) ---", pid);
+ std::vector<std::string> lines = android::base::Split(content, "\n");
+ int i = lines.size() - 1;
+ while (i >= 0 && lines[i] != header) --i;
+ while (static_cast<size_t>(i) < lines.size()) fprintf(stderr, "%s\n", lines[i++].c_str());
+}
+
int launch_server(const std::string& socket_spec) {
#if defined(_WIN32)
/* we need to start the server in the background */
@@ -835,7 +856,8 @@
memcmp(temp, expected, expected_length) == 0) {
got_ack = true;
} else {
- fprintf(stderr, "ADB server didn't ACK\n");
+ ReportServerStartupFailure(GetProcessId(process_handle.get()));
+ return -1;
}
} else {
const DWORD err = GetLastError();
@@ -909,12 +931,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 +944,7 @@
return -1;
}
if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
- fprintf(stderr, "ADB server didn't ACK\n" );
+ ReportServerStartupFailure(pid);
return -1;
}
}
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/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/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/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/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/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/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index edc7be5..996d714 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;
}
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/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/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 0af6159..7fd67c2 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -21,18 +21,6 @@
},
local_include_dirs: ["include/"],
cppflags: ["-Werror"],
- static_libs: [
- "liblogwrap",
- "libfec",
- "libfec_rs",
- "libbase",
- "libcrypto_utils",
- "libcrypto",
- "libext4_utils",
- "libsquashfs_utils",
- "libselinux",
- "libavb",
- ],
}
cc_library_static {
@@ -44,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: {
@@ -60,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..62de6b6 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -476,10 +476,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);
}
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 8904995..8fa9370 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;
}
@@ -825,10 +856,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,
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..d7cbfe1
--- /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(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);
+
+__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/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index d26530b..676ee41 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -116,6 +116,10 @@
{ "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE },
{ "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE },
{ "Cold", BATTERY_HEALTH_COLD },
+ // battery health values from JEITA spec
+ { "Warm", BATTERY_HEALTH_GOOD },
+ { "Cool", BATTERY_HEALTH_GOOD },
+ { "Hot", BATTERY_HEALTH_OVERHEAT },
{ NULL, 0 },
};
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 49a534c..c76762d 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -33,6 +33,7 @@
#include <functional>
#include <android-base/file.h>
+#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <sys/socket.h>
@@ -76,8 +77,6 @@
#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
-#define LAST_KMSG_PATH "/proc/last_kmsg"
-#define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops"
#define LAST_KMSG_MAX_SZ (32 * 1024)
#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
@@ -217,14 +216,21 @@
LOGW("\n");
LOGW("*************** LAST KMSG ***************\n");
LOGW("\n");
- buf = (char *)load_file(LAST_KMSG_PSTORE_PATH, &sz);
+ const char* kmsg[] = {
+ // clang-format off
+ "/sys/fs/pstore/console-ramoops-0",
+ "/sys/fs/pstore/console-ramoops",
+ "/proc/last_kmsg",
+ // clang-format on
+ };
+ for (size_t i = 0; i < arraysize(kmsg); ++i) {
+ buf = (char*)load_file(kmsg[i], &sz);
+ if (buf && sz) break;
+ }
if (!buf || !sz) {
- buf = (char *)load_file(LAST_KMSG_PATH, &sz);
- if (!buf || !sz) {
- LOGW("last_kmsg not found. Cold reset?\n");
- goto out;
- }
+ LOGW("last_kmsg not found. Cold reset?\n");
+ goto out;
}
len = min(sz, LAST_KMSG_MAX_SZ);
diff --git a/include/ziparchive b/include/ziparchive
new file mode 120000
index 0000000..d8fa424
--- /dev/null
+++ b/include/ziparchive
@@ -0,0 +1 @@
+../libziparchive/include/ziparchive
\ No newline at end of file
diff --git a/init/Android.bp b/init/Android.bp
index af1e9d3..fce424e 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -20,7 +20,6 @@
sanitize: {
misc_undefined: ["integer"],
},
- tidy_checks: ["-misc-forwarding-reference-overload"],
cppflags: [
"-DLOG_UEVENTS=0",
"-Wall",
diff --git a/init/Android.mk b/init/Android.mk
index 866ea34..8fc354a 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -38,8 +38,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 \
diff --git a/init/action.cpp b/init/action.cpp
index 21abe02..6900391 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,13 +18,14 @@
#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) {}
@@ -98,10 +99,10 @@
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_ms
+ << "ms.";
}
}
@@ -351,3 +352,6 @@
action_manager_->AddAction(std::move(action_));
}
}
+
+} // namespace init
+} // namespace android
diff --git a/init/action.h b/init/action.h
index d006c50..5cb50a7 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);
@@ -126,4 +129,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 bdd86ed..921ea94 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -43,7 +43,6 @@
#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 +67,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) {
@@ -552,11 +554,10 @@
}
}
- std::string prop_name = android::base::StringPrintf("ro.boottime.init.mount_all.%s",
- prop_post_fix);
+ std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
Timer t;
int ret = mount_fstab(fstabfile, mount_mode);
- property_set(prop_name.c_str(), std::to_string(t.duration_ms()).c_str());
+ property_set(prop_name, std::to_string(t.duration_ms()));
if (import_rc) {
/* Paths of .rc files are specified at the 2nd argument and beyond */
@@ -584,9 +585,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 +668,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 +944,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..215d2ea 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;
@@ -276,7 +286,7 @@
// skip path to the parent driver
std::string path = uevent.path.substr(parent_device.length());
- if (!android::base::StartsWith(path, "/usb")) return {};
+ if (!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.
@@ -334,9 +344,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());
}
@@ -380,8 +390,8 @@
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 +403,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());
}
}
@@ -408,11 +418,11 @@
const char* base = "/dev/block/";
make_dir(base, 0755, sehandle_);
- std::string name = android::base::Basename(uevent.path);
+ std::string name = Basename(uevent.path);
std::string devpath = base + name;
std::vector<std::string> links;
- if (android::base::StartsWith(uevent.path, "/devices")) {
+ if (StartsWith(uevent.path, "/devices")) {
links = GetBlockDeviceSymlinks(uevent);
}
@@ -425,7 +435,7 @@
std::string devpath;
- if (android::base::StartsWith(uevent.subsystem, "usb")) {
+ if (StartsWith(uevent.subsystem, "usb")) {
if (uevent.subsystem == "usb") {
if (!uevent.device_name.empty()) {
devpath = "/dev/" + uevent.device_name;
@@ -435,7 +445,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,10 +456,10 @@
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);
@@ -481,3 +491,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..5105ad7 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);
@@ -133,4 +136,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..57d8e0f 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -24,6 +24,9 @@
using namespace std::string_literals;
+namespace android {
+namespace init {
+
class DeviceHandlerTester {
public:
void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent,
@@ -400,3 +403,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..8cd5cc5 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -30,10 +30,16 @@
#include "util.h"
+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);
@@ -130,3 +136,6 @@
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 f4546a6..b498b8d 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,13 @@
#include "util.h"
#include "watchdogd.h"
+using namespace std::string_literals;
+
using android::base::boot_clock;
using android::base::GetProperty;
-using android::base::StringPrintf;
+
+namespace android {
+namespace init {
struct selabel_handle *sehandle;
struct selabel_handle *sehandle_prop;
@@ -219,7 +221,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_ms()));
return 0;
}
@@ -449,14 +451,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);
}
}
@@ -487,7 +489,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);
}
}
@@ -511,8 +513,7 @@
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);
}
}
@@ -908,7 +909,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);
@@ -1025,7 +1025,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 };
@@ -1180,3 +1180,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 6725a70..479b771 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.
@@ -39,4 +44,7 @@
void DumpState();
+} // namespace init
+} // namespace android
+
#endif /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 3781a22..e10c3b2 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -38,6 +38,9 @@
using namespace std::chrono_literals;
+namespace android {
+namespace init {
+
// Class Declarations
// ------------------
class FirstStageMount {
@@ -497,3 +500,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..e76d589 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -25,6 +25,9 @@
#include "parser.h"
#include "util.h"
+namespace android {
+namespace init {
+
Parser::Parser() {
}
@@ -159,3 +162,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..38b8275 100644
--- a/init/init_parser_test.cpp
+++ b/init/init_parser_test.cpp
@@ -24,6 +24,9 @@
#include <string>
#include <vector>
+namespace android {
+namespace init {
+
TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
ServiceManager& sm = ServiceManager::GetInstance();
std::vector<std::string> args;
@@ -141,3 +144,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..b7907e5 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,12 +39,12 @@
#include <sys/_system_properties.h>
#include <memory>
+#include <queue>
#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <bootimg.h>
#include <fs_mgr.h>
@@ -55,11 +55,12 @@
#include "init.h"
#include "util.h"
-using android::base::StringPrintf;
-
#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 +163,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 +177,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 +205,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)
@@ -647,7 +721,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 +751,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 cdfc698..34c98a7 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>
@@ -48,11 +48,15 @@
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
+#include "capabilities.h"
#include "property_service.h"
#include "service.h"
using android::base::StringPrintf;
+namespace android {
+namespace init {
+
// represents umount status during reboot / shutdown.
enum UmountStat {
/* umount succeeded. */
@@ -159,9 +163,38 @@
LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration_ms()) << ":" << 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);
@@ -468,3 +501,6 @@
DoReboot(cmd, command, reboot_target, run_fsck);
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 1a6474b..f2e5d22 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -45,10 +45,16 @@
#include "util.h"
using android::base::boot_clock;
+using android::base::GetProperty;
+using android::base::Join;
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;
@@ -198,13 +204,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 +235,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.
+ int64_t securebits = prctl(PR_GET_SECUREBITS);
+ if (securebits == -1) {
+ 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 +334,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; });
}
@@ -526,9 +538,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;
}
@@ -696,13 +707,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.
@@ -716,7 +727,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;
@@ -757,7 +768,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);
@@ -776,9 +787,9 @@
}
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");
@@ -955,8 +966,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;
@@ -1085,6 +1095,8 @@
} else if (pid == -1) {
PLOG(ERROR) << "waitpid failed";
return false;
+ } else if (PropertyChildReap(pid)) {
+ return true;
}
Service* svc = FindServiceByPid(pid);
@@ -1092,14 +1104,12 @@
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_s());
}
} else {
- name = android::base::StringPrintf("Untracked pid %d", pid);
+ name = StringPrintf("Untracked pid %d", pid);
}
if (WIFEXITED(status)) {
@@ -1174,3 +1184,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 b9c270a..3c7dc74 100644
--- a/init/service.h
+++ b/init/service.h
@@ -55,8 +55,8 @@
#define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups
-class Action;
-class ServiceManager;
+namespace android {
+namespace init {
struct ServiceEnvironmentInfo {
ServiceEnvironmentInfo();
@@ -236,4 +236,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..81a0572 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -29,7 +29,6 @@
#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 +99,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 +160,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);
}
@@ -276,3 +276,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/util.cpp b/init/util.cpp
index 75f81b9..4b1894f 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -50,6 +50,9 @@
using android::base::boot_clock;
using namespace std::literals::string_literals;
+namespace android {
+namespace init {
+
// 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.
@@ -397,3 +400,6 @@
}
return false;
}
+
+} // namespace init
+} // namespace android
diff --git a/init/util.h b/init/util.h
index 1ad6b77..346953f 100644
--- a/init/util.h
+++ b/init/util.h
@@ -35,6 +35,9 @@
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);
@@ -78,4 +81,7 @@
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/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/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/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/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/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/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..a1f74c3 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -15,16 +15,17 @@
*/
#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 "HeapWalker.h"
@@ -37,30 +38,33 @@
#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);
+ 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 +72,17 @@
};
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) {
MEM_ALOGI("searching process %d for allocations", pid_);
allocator::vector<Mapping> heap_mappings{mappings};
allocator::vector<Mapping> anon_mappings{mappings};
@@ -118,8 +123,8 @@
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 +132,6 @@
return false;
}
-
allocator::vector<Range> leaked1{allocator_};
heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
@@ -152,12 +156,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 +187,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 +208,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 +247,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 +259,7 @@
return true;
}
-template<typename T>
+template <typename T>
static inline const char* plural(T val) {
return (val == 1) ? "" : "s";
}
@@ -403,7 +406,6 @@
}
std::string Leak::ToString(bool log_contents) const {
-
std::ostringstream oss;
oss << " " << std::dec << size;
@@ -492,8 +494,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 +513,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 +527,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/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/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/processgroup.cpp b/libprocessgroup/processgroup.cpp
index f5d4e1c..9b8248e 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -43,9 +43,6 @@
using namespace std::chrono_literals;
-// Uncomment line below use memory cgroups for keeping track of (forked) PIDs
-// #define USE_MEMCG 1
-
#define MEM_CGROUP_PATH "/dev/memcg/apps"
#define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
#define ACCT_CGROUP_PATH "/acct"
@@ -91,7 +88,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 +96,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)
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/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index 1b6076f..6f76e76 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;
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index eb2b902..8776db7 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -48,6 +48,8 @@
srcs: [
"ArmExidx.cpp",
"DwarfCfa.cpp",
+ "DwarfDebugFrame.cpp",
+ "DwarfEhFrame.cpp",
"DwarfMemory.cpp",
"DwarfOp.cpp",
"DwarfSection.cpp",
@@ -55,16 +57,17 @@
"ElfInterface.cpp",
"ElfInterfaceArm.cpp",
"Log.cpp",
- "Regs.cpp",
"MapInfo.cpp",
"Maps.cpp",
"Memory.cpp",
+ "Regs.cpp",
"Symbols.cpp",
],
shared_libs: [
"libbase",
"liblog",
+ "liblzma",
],
}
@@ -73,17 +76,6 @@
defaults: ["libunwindstack_common"],
}
-cc_library {
- name: "libunwindstack_debug",
- defaults: ["libunwindstack_common"],
-
- cflags: [
- "-UNDEBUG",
- "-O0",
- "-g",
- ],
-}
-
//-------------------------------------------------------------------------
// Unit Tests
//-------------------------------------------------------------------------
@@ -96,6 +88,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",
@@ -124,6 +118,7 @@
shared_libs: [
"libbase",
"liblog",
+ "liblzma",
],
static_libs: [
@@ -147,15 +142,10 @@
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/elf32.xz",
+ "tests/elf64.xz",
],
}
@@ -169,6 +159,7 @@
shared_libs: [
"libunwindstack",
"libbase",
+ "liblzma",
],
static_libs: [
@@ -186,3 +177,25 @@
"unwind_info.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",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+
+ srcs: [
+ "tests/GenGnuDebugdata.cpp",
+ ],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 12adf57..5c7caca 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>
@@ -23,6 +22,7 @@
#include <android-base/stringprintf.h>
#include "ArmExidx.h"
+#include "Check.h"
#include "Log.h"
#include "Machine.h"
#include "Memory.h"
@@ -173,7 +173,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 +232,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 +258,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 +419,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 +439,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 +469,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 +550,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 +605,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 +624,7 @@
}
inline bool ArmExidx::DecodePrefix_11(uint8_t byte) {
- assert((byte >> 6) == 0x3);
+ CHECK((byte >> 6) == 0x3);
switch ((byte >> 3) & 0x7) {
case 0:
diff --git a/libunwindstack/Check.h b/libunwindstack/Check.h
new file mode 100644
index 0000000..e0c48c7
--- /dev/null
+++ b/libunwindstack/Check.h
@@ -0,0 +1,30 @@
+/*
+ * 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 "Log.h"
+
+#define CHECK(assertion) \
+ if (__builtin_expect(!(assertion), false)) { \
+ log(0, "%s:%d: %s\n", __FILE__, __LINE__, #assertion); \
+ abort(); \
+ }
+
+#endif // _LIBUNWINDSTACK_ERROR_H
diff --git a/libunwindstack/DwarfDebugFrame.cpp b/libunwindstack/DwarfDebugFrame.cpp
new file mode 100644
index 0000000..3ac02fc
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.cpp
@@ -0,0 +1,311 @@
+/*
+ * 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 "DwarfDebugFrame.h"
+#include "DwarfMemory.h"
+#include "DwarfSection.h"
+#include "DwarfStructs.h"
+#include "Memory.h"
+
+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>;
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
new file mode 100644
index 0000000..320368c
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -0,0 +1,76 @@
+/*
+ * 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 "DwarfSection.h"
+
+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_;
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrame.cpp
new file mode 100644
index 0000000..937b6bd
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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 "Check.h"
+#include "DwarfEhFrame.h"
+#include "DwarfMemory.h"
+#include "DwarfSection.h"
+#include "DwarfStructs.h"
+#include "Memory.h"
+
+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>;
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
new file mode 100644
index 0000000..e6909ed
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.h
@@ -0,0 +1,89 @@
+/*
+ * 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 "DwarfSection.h"
+
+// 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_;
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
index 11806ea..a984fc6 100644
--- a/libunwindstack/DwarfMemory.cpp
+++ b/libunwindstack/DwarfMemory.cpp
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-#include <assert.h>
#include <stdint.h>
#include <string>
+#include "Check.h"
#include "DwarfEncoding.h"
#include "DwarfMemory.h"
#include "Memory.h"
@@ -100,8 +100,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) {
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 272b5f0..ad1447a 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -49,6 +49,28 @@
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::IsValidElf(Memory* memory) {
if (memory == nullptr) {
return false;
diff --git a/libunwindstack/Elf.h b/libunwindstack/Elf.h
index 7bf45b8..cff9dc4 100644
--- a/libunwindstack/Elf.h
+++ b/libunwindstack/Elf.h
@@ -45,12 +45,16 @@
return valid_ && interface_->GetSoname(name);
}
- bool GetFunctionName(uint64_t, std::string*, uint64_t*) {
- return false;
+ bool 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 Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
- return valid_ && interface_->Step(rel_pc, regs, process_memory);
+ return valid_ && (interface_->Step(rel_pc, regs, process_memory) ||
+ (gnu_debugdata_interface_ &&
+ gnu_debugdata_interface_->Step(rel_pc, regs, process_memory)));
}
ElfInterface* CreateInterfaceFromMemory(Memory* memory);
@@ -65,6 +69,8 @@
ElfInterface* interface() { return interface_.get(); }
+ ElfInterface* gnu_debugdata_interface() { return gnu_debugdata_interface_.get(); }
+
static bool IsValidElf(Memory* memory);
protected:
@@ -73,6 +79,9 @@
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_;
};
#endif // _LIBUNWINDSTACK_ELF_H
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 087457c..7d38ee0 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -20,9 +20,99 @@
#include <memory>
#include <string>
+#include <7zCrc.h>
+#include <Xz.h>
+#include <XzCrc64.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEhFrame.h"
+#include "DwarfSection.h"
#include "ElfInterface.h"
+#include "Log.h"
#include "Memory.h"
#include "Regs.h"
+#include "Symbols.h"
+
+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 +124,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 +220,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 +260,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 +330,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 +382,8 @@
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*);
diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/ElfInterface.h
index 944146c..d0d0d28 100644
--- a/libunwindstack/ElfInterface.h
+++ b/libunwindstack/ElfInterface.h
@@ -25,9 +25,12 @@
#include <unordered_map>
#include <vector>
+#include "DwarfSection.h"
+
// Forward declarations.
class Memory;
class Regs;
+class Symbols;
struct LoadInfo {
uint64_t offset;
@@ -44,7 +47,7 @@
class ElfInterface {
public:
ElfInterface(Memory* memory) : memory_(memory) {}
- virtual ~ElfInterface() = default;
+ virtual ~ElfInterface();
virtual bool Init() = 0;
@@ -68,10 +71,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 +95,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 +119,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 +135,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,15 +155,14 @@
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);
}
};
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index b369c43..b869918 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -71,15 +71,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;
diff --git a/libunwindstack/Memory.h b/libunwindstack/Memory.h
index f9f6d56..e353cab 100644
--- a/libunwindstack/Memory.h
+++ b/libunwindstack/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>
+#include "Check.h"
+
class Memory {
public:
Memory() = default;
@@ -130,7 +131,7 @@
public:
MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
: memory_(memory), begin_(begin), length_(end - begin) {
- assert(end > begin);
+ CHECK(end > begin);
}
virtual ~MemoryRange() { delete memory_; }
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index e7d10b2..da22b07 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,6 +21,7 @@
#include <vector>
+#include "Check.h"
#include "Elf.h"
#include "ElfInterface.h"
#include "Machine.h"
@@ -43,7 +43,7 @@
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:
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index 86c1233..edc2187 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#include <assert.h>
#include <elf.h>
#include <stdint.h>
#include <string>
+#include "Check.h"
#include "Memory.h"
#include "Symbols.h"
@@ -58,7 +58,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);
}
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
new file mode 100644
index 0000000..94a9974
--- /dev/null
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -0,0 +1,456 @@
+/*
+ * 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 "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+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);
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
new file mode 100644
index 0000000..4e538d4
--- /dev/null
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -0,0 +1,409 @@
+/*
+ * 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 "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+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);
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index c31903d..0f56ba8 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -67,9 +67,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 +611,313 @@
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>();
+}
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 25fec8e..22cade2 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -15,6 +15,10 @@
*/
#include <elf.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <gtest/gtest.h>
@@ -26,10 +30,6 @@
#define PT_ARM_EXIDX 0x70000001
#endif
-#if !defined(EM_AARCH64)
-#define EM_AARCH64 183
-#endif
-
class ElfTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -43,17 +43,18 @@
ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
ehdr->e_ident[EI_VERSION] = EV_CURRENT;
ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
+
+ ehdr->e_type = ET_DYN;
+ ehdr->e_version = EV_CURRENT;
}
- void InitElf32(uint32_t type) {
+ void InitElf32(uint32_t machine) {
Elf32_Ehdr ehdr;
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_machine = machine;
ehdr.e_entry = 0;
ehdr.e_phoff = 0x100;
ehdr.e_shoff = 0;
@@ -64,7 +65,7 @@
ehdr.e_shentsize = sizeof(Elf32_Shdr);
ehdr.e_shnum = 0;
ehdr.e_shstrndx = 0;
- if (type == EM_ARM) {
+ if (machine == EM_ARM) {
ehdr.e_flags = 0x5000200;
ehdr.e_phnum = 2;
}
@@ -82,7 +83,7 @@
phdr.p_align = 0x1000;
memory_->SetMemory(0x100, &phdr, sizeof(phdr));
- if (type == EM_ARM) {
+ if (machine == EM_ARM) {
memset(&phdr, 0, sizeof(phdr));
phdr.p_type = PT_ARM_EXIDX;
phdr.p_offset = 0x30000;
@@ -96,15 +97,13 @@
}
}
- void InitElf64(uint32_t type) {
+ void InitElf64(uint32_t machine) {
Elf64_Ehdr ehdr;
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_machine = machine;
ehdr.e_entry = 0;
ehdr.e_phoff = 0x100;
ehdr.e_shoff = 0;
@@ -130,6 +129,12 @@
memory_->SetMemory(0x100, &phdr, sizeof(phdr));
}
+ template <typename Ehdr, typename Shdr>
+ void GnuDebugdataInitFail(Ehdr* ehdr);
+
+ template <typename Ehdr, typename Shdr>
+ void GnuDebugdataInit(Ehdr* ehdr);
+
MemoryFake* memory_;
};
@@ -208,3 +213,154 @@
ASSERT_EQ(ELFCLASS64, elf.class_type());
ASSERT_TRUE(elf.interface() != nullptr);
}
+
+template <typename Ehdr, typename Shdr>
+void ElfTest::GnuDebugdataInitFail(Ehdr* ehdr) {
+ Elf elf(memory_);
+
+ uint64_t offset = 0x2000;
+
+ ehdr->e_shoff = offset;
+ ehdr->e_shnum = 3;
+ ehdr->e_shentsize = sizeof(Shdr);
+ ehdr->e_shstrndx = 2;
+ memory_->SetMemory(0, ehdr, sizeof(*ehdr));
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ memory_->SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr->e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0x100;
+ 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;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x200000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_->SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr->e_shentsize;
+
+ memory_->SetMemory(0xf100, ".gnu_debugdata", sizeof(".gnu_debugdata"));
+
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+ EXPECT_EQ(0x5000U, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x800U, elf.interface()->gnu_debugdata_size());
+
+ elf.InitGnuDebugdata();
+}
+
+TEST_F(ElfTest, gnu_debugdata_init_fail32) {
+ Elf32_Ehdr ehdr;
+ InitEhdr<Elf32_Ehdr>(&ehdr);
+ ehdr.e_ident[EI_CLASS] = ELFCLASS32;
+ ehdr.e_machine = EM_ARM;
+
+ GnuDebugdataInitFail<Elf32_Ehdr, Elf32_Shdr>(&ehdr);
+}
+
+TEST_F(ElfTest, gnu_debugdata_init_fail64) {
+ Elf64_Ehdr ehdr;
+ InitEhdr<Elf64_Ehdr>(&ehdr);
+ ehdr.e_ident[EI_CLASS] = ELFCLASS64;
+ ehdr.e_machine = EM_AARCH64;
+
+ GnuDebugdataInitFail<Elf64_Ehdr, Elf64_Shdr>(&ehdr);
+}
+
+template <typename Ehdr, typename Shdr>
+void ElfTest::GnuDebugdataInit(Ehdr* ehdr) {
+ Elf elf(memory_);
+
+ uint64_t offset = 0x2000;
+
+ ehdr->e_shoff = offset;
+ ehdr->e_shnum = 3;
+ ehdr->e_shentsize = sizeof(Shdr);
+ ehdr->e_shstrndx = 2;
+ memory_->SetMemory(0, ehdr, sizeof(*ehdr));
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ memory_->SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr->e_shentsize;
+
+ uint64_t gnu_offset = offset;
+ offset += ehdr->e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x200000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_->SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr->e_shentsize;
+
+ memory_->SetMemory(0xf100, ".gnu_debugdata", sizeof(".gnu_debugdata"));
+
+ // Read in the compressed elf data and put it in our fake memory.
+ std::string name("tests/");
+ if (sizeof(Ehdr) == sizeof(Elf32_Ehdr)) {
+ 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);
+ ssize_t 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);
+ memory_->SetMemory(0x5000, buf);
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0x100;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_size = bytes;
+ memory_->SetMemory(gnu_offset, &shdr, sizeof(shdr));
+
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+ EXPECT_EQ(0x5000U, elf.interface()->gnu_debugdata_offset());
+
+ elf.InitGnuDebugdata();
+ ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(ElfTest, gnu_debugdata_init32) {
+ Elf32_Ehdr ehdr;
+ InitEhdr<Elf32_Ehdr>(&ehdr);
+ ehdr.e_ident[EI_CLASS] = ELFCLASS32;
+ ehdr.e_machine = EM_ARM;
+
+ GnuDebugdataInit<Elf32_Ehdr, Elf32_Shdr>(&ehdr);
+}
+
+TEST_F(ElfTest, gnu_debugdata_init64) {
+ Elf64_Ehdr ehdr;
+ InitEhdr<Elf64_Ehdr>(&ehdr);
+ ehdr.e_ident[EI_CLASS] = ELFCLASS64;
+ ehdr.e_machine = EM_AARCH64;
+
+ GnuDebugdataInit<Elf64_Ehdr, Elf64_Shdr>(&ehdr);
+}
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/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
index c846ad7..6e47dc0 100644
--- a/libunwindstack/tests/MapInfoTest.cpp
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -75,7 +75,7 @@
// 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,
@@ -212,8 +212,8 @@
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);
+ std::unique_ptr<Elf> elf(info.GetElf(getpid(), false));
+ ASSERT_TRUE(elf.get() != nullptr);
ASSERT_FALSE(elf->valid());
ASSERT_EQ(0, munmap(map, 1024));
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index ee5ba01..469afcc 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -26,21 +26,13 @@
#include "MemoryFake.h"
-class MemoryRangeTest : public ::testing::Test {
- protected:
- void SetUp() override {
- memory_ = new MemoryFake;
- }
-
- 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 +41,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,7 +64,7 @@
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));
diff --git a/libunwindstack/tests/elf32.xz b/libunwindstack/tests/elf32.xz
new file mode 100644
index 0000000..f25d433
--- /dev/null
+++ b/libunwindstack/tests/elf32.xz
Binary files differ
diff --git a/libunwindstack/tests/elf64.xz b/libunwindstack/tests/elf64.xz
new file mode 100644
index 0000000..eb1618e
--- /dev/null
+++ b/libunwindstack/tests/elf64.xz
Binary files differ
diff --git a/libunwindstack/unwind_info.cpp b/libunwindstack/unwind_info.cpp
index 6f158b0..da1522b 100644
--- a/libunwindstack/unwind_info.cpp
+++ b/libunwindstack/unwind_info.cpp
@@ -26,6 +26,8 @@
#include <unistd.h>
#include "ArmExidx.h"
+#include "DwarfSection.h"
+#include "DwarfStructs.h"
#include "Elf.h"
#include "ElfInterface.h"
#include "ElfInterfaceArm.h"
@@ -46,12 +48,14 @@
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()) {
+ 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(addr + load_bias, &entry)) {
+ if (!interface->FindEntry(pc, &entry)) {
printf(" Cannot find entry for address.\n");
continue;
}
@@ -75,6 +79,27 @@
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 main(int argc, char** argv) {
if (argc != 2) {
printf("Need to pass the name of an elf file to the program.\n");
@@ -117,5 +142,38 @@
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;
}
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/libziparchive/Android.bp b/libziparchive/Android.bp
index 287a99c..84a9a2f 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -52,17 +52,26 @@
],
}
-
cc_library {
name: "libziparchive",
host_supported: true,
- vendor_available:true,
+ vendor_available: true,
- defaults: ["libziparchive_defaults", "libziparchive_flags"],
- shared_libs: ["liblog", "libbase"],
+ defaults: [
+ "libziparchive_defaults",
+ "libziparchive_flags",
+ ],
+ shared_libs: [
+ "liblog",
+ "libbase",
+ ],
+ export_include_dirs: ["include"],
target: {
android: {
- shared_libs: ["libz", "libutils"],
+ shared_libs: [
+ "libz",
+ "libutils",
+ ],
},
host: {
static_libs: ["libutils"],
@@ -88,7 +97,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 +162,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 78de40a..17c268b 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -27,6 +27,7 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
#include <memory>
@@ -48,6 +49,10 @@
using android::base::get_unaligned;
+// Used to turn on crc checks - verify that the content CRC matches the values
+// specified in the local file header and the central directory.
+static const bool kCrcChecksEnabled = false;
+
// This is for windows. If we don't open a file in binary mode, weird
// things will happen.
#ifndef O_BINARY
@@ -57,68 +62,6 @@
// The maximum number of bytes to scan backwards for the EOCD start.
static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
-static const char* kErrorMessages[] = {
- "Unknown return code.",
- "Iteration ended",
- "Zlib error",
- "Invalid file",
- "Invalid handle",
- "Duplicate entries in archive",
- "Empty archive",
- "Entry not found",
- "Invalid offset",
- "Inconsistent information",
- "Invalid entry name",
- "I/O Error",
- "File mapping failed"
-};
-
-static const int32_t kErrorMessageUpperBound = 0;
-
-static const int32_t kIterationEnd = -1;
-
-// We encountered a Zlib error when inflating a stream from this file.
-// Usually indicates file corruption.
-static const int32_t kZlibError = -2;
-
-// The input file cannot be processed as a zip archive. Usually because
-// it's too small, too large or does not have a valid signature.
-static const int32_t kInvalidFile = -3;
-
-// An invalid iteration / ziparchive handle was passed in as an input
-// argument.
-static const int32_t kInvalidHandle = -4;
-
-// The zip archive contained two (or possibly more) entries with the same
-// name.
-static const int32_t kDuplicateEntry = -5;
-
-// The zip archive contains no entries.
-static const int32_t kEmptyArchive = -6;
-
-// The specified entry was not found in the archive.
-static const int32_t kEntryNotFound = -7;
-
-// The zip archive contained an invalid local file header pointer.
-static const int32_t kInvalidOffset = -8;
-
-// The zip archive contained inconsistent entry information. This could
-// be because the central directory & local file header did not agree, or
-// if the actual uncompressed length or crc32 do not match their declared
-// values.
-static const int32_t kInconsistentInformation = -9;
-
-// An invalid entry name was encountered.
-static const int32_t kInvalidEntryName = -10;
-
-// An I/O related system call (read, lseek, ftruncate, map) failed.
-static const int32_t kIoError = -11;
-
-// We were not able to mmap the central directory or entry contents.
-static const int32_t kMmapFailed = -12;
-
-static const int32_t kErrorMessageLowerBound = -13;
-
/*
* A Read-only Zip archive.
*
@@ -172,8 +115,7 @@
* Convert a ZipEntry to a hash table index, verifying that it's in a
* valid range.
*/
-static int64_t EntryToIndex(const ZipString* hash_table,
- const uint32_t hash_table_size,
+static int64_t EntryToIndex(const ZipString* hash_table, const uint32_t hash_table_size,
const ZipString& name) {
const uint32_t hash = ComputeHash(name);
@@ -194,7 +136,7 @@
/*
* Add a new entry to the hash table.
*/
-static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
+static int32_t AddToHash(ZipString* hash_table, const uint64_t hash_table_size,
const ZipString& name) {
const uint64_t hash = ComputeHash(name);
uint32_t ent = hash & (hash_table_size - 1);
@@ -218,13 +160,12 @@
}
static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
- off64_t file_length, off64_t read_amount,
- uint8_t* scan_buffer) {
+ off64_t file_length, off64_t read_amount, uint8_t* scan_buffer) {
const off64_t search_start = file_length - read_amount;
- if(!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
- ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed",
- static_cast<int64_t>(read_amount), static_cast<int64_t>(search_start));
+ if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
+ ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed", static_cast<int64_t>(read_amount),
+ static_cast<int64_t>(search_start));
return kIoError;
}
@@ -255,8 +196,7 @@
* Verify that there's no trailing space at the end of the central directory
* and its comment.
*/
- const off64_t calculated_length = eocd_offset + sizeof(EocdRecord)
- + eocd->comment_length;
+ const off64_t calculated_length = eocd_offset + sizeof(EocdRecord) + eocd->comment_length;
if (calculated_length != file_length) {
ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
static_cast<int64_t>(file_length - calculated_length));
@@ -269,7 +209,7 @@
*/
if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
- eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
+ eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
#if defined(__ANDROID__)
if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) {
android_errorWriteLog(0x534e4554, "31251826");
@@ -282,8 +222,8 @@
return kEmptyArchive;
}
- ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
- eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
+ ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32, eocd->num_records,
+ eocd->cd_size, eocd->cd_start_offset);
/*
* It all looks good. Create a mapping for the CD, and set the fields
@@ -312,7 +252,6 @@
* num_entries
*/
static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
-
// Test file length. We use lseek64 to make sure the file
// is small enough to be a zip file (Its size must be less than
// 0xffffffff bytes).
@@ -349,8 +288,8 @@
}
std::vector<uint8_t> scan_buffer(read_amount);
- int32_t result = MapCentralDirectory0(debug_file_name, archive, file_length, read_amount,
- scan_buffer.data());
+ int32_t result =
+ MapCentralDirectory0(debug_file_name, archive, file_length, read_amount, scan_buffer.data());
return result;
}
@@ -371,8 +310,8 @@
* least one unused entry to avoid an infinite loop during creation.
*/
archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
- archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
- sizeof(ZipString)));
+ archive->hash_table =
+ reinterpret_cast<ZipString*>(calloc(archive->hash_table_size, sizeof(ZipString)));
if (archive->hash_table == nullptr) {
ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
archive->hash_table_size, sizeof(ZipString));
@@ -394,8 +333,7 @@
return -1;
}
- const CentralDirectoryRecord* cdr =
- reinterpret_cast<const CentralDirectoryRecord*>(ptr);
+ const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
return -1;
@@ -404,7 +342,7 @@
const off64_t local_header_offset = cdr->local_file_header_offset;
if (local_header_offset >= archive->directory_offset) {
ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
- static_cast<int64_t>(local_header_offset), i);
+ static_cast<int64_t>(local_header_offset), i);
return -1;
}
@@ -414,8 +352,10 @@
const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
if (file_name + file_name_length > cd_end) {
- ALOGW("Zip: file name boundary exceeds the central directory range, file_name_length: "
- "%" PRIx16 ", cd_length: %zu", file_name_length, cd_length);
+ ALOGW(
+ "Zip: file name boundary exceeds the central directory range, file_name_length: "
+ "%" PRIx16 ", cd_length: %zu",
+ file_name_length, cd_length);
return -1;
}
/* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
@@ -427,8 +367,7 @@
ZipString entry_name;
entry_name.name = file_name;
entry_name.name_length = file_name_length;
- const int add_result = AddToHash(archive->hash_table,
- archive->hash_table_size, entry_name);
+ const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name);
if (add_result != 0) {
ALOGW("Zip: Error adding entry to hash table %d", add_result);
return add_result;
@@ -436,8 +375,7 @@
ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
- ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16,
- ptr - cd_ptr, cd_length, i);
+ ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, ptr - cd_ptr, cd_length, i);
return -1;
}
}
@@ -446,8 +384,7 @@
return 0;
}
-static int32_t OpenArchiveInternal(ZipArchive* archive,
- const char* debug_file_name) {
+static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) {
int32_t result = -1;
if ((result = MapCentralDirectory(debug_file_name, archive)) != 0) {
return result;
@@ -460,8 +397,8 @@
return 0;
}
-int32_t OpenArchiveFd(int fd, const char* debug_file_name,
- ZipArchiveHandle* handle, bool assume_ownership) {
+int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
+ bool assume_ownership) {
ZipArchive* archive = new ZipArchive(fd, assume_ownership);
*handle = archive;
return OpenArchiveInternal(archive, debug_file_name);
@@ -481,7 +418,7 @@
}
int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name,
- ZipArchiveHandle *handle) {
+ ZipArchiveHandle* handle) {
ZipArchive* archive = new ZipArchive(address, length);
*handle = archive;
return OpenArchiveInternal(archive, debug_file_name);
@@ -496,8 +433,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;
@@ -507,15 +443,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
@@ -533,8 +476,7 @@
return kInvalidOffset;
}
- const CentralDirectoryRecord *cdr =
- reinterpret_cast<const CentralDirectoryRecord*>(ptr);
+ const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
// The offset of the start of the central directory in the zipfile.
// We keep this lying around so that we can sanity check all our lengths
@@ -562,46 +504,62 @@
uint8_t lfh_buf[sizeof(LocalFileHeader)];
if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
ALOGW("Zip: failed reading lfh name from offset %" PRId64,
- static_cast<int64_t>(local_header_offset));
+ static_cast<int64_t>(local_header_offset));
return kIoError;
}
- const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
+ const LocalFileHeader* lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
if (lfh->lfh_signature != LocalFileHeader::kSignature) {
ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
- static_cast<int64_t>(local_header_offset));
+ static_cast<int64_t>(local_header_offset));
return kInvalidOffset;
}
// Paranoia: Match the values specified in the local file header
// to those specified in the central directory.
- // Verify that the central directory and local file header have the same general purpose bit
- // flags set.
- if (lfh->gpb_flags != cdr->gpb_flags) {
- ALOGW("Zip: gpb flag mismatch. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
+ // Warn if central directory and local file header don't agree on the use
+ // of a trailing Data Descriptor. The reference implementation is inconsistent
+ // and appears to use the LFH value during extraction (unzip) but the CD value
+ // while displayng information about archives (zipinfo). The spec remains
+ // silent on this inconsistency as well.
+ //
+ // For now, always use the version from the LFH but make sure that the values
+ // specified in the central directory match those in the data descriptor.
+ //
+ // NOTE: It's also worth noting that unzip *does* warn about inconsistencies in
+ // bit 11 (EFS: The language encoding flag, marking that filename and comment are
+ // encoded using UTF-8). This implementation does not check for the presence of
+ // that flag and always enforces that entry names are valid UTF-8.
+ if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) {
+ ALOGW("Zip: gpb flag mismatch at bit 3. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
cdr->gpb_flags, lfh->gpb_flags);
- return kInconsistentInformation;
}
// If there is no trailing data descriptor, verify that the central directory and local file
// header agree on the crc, compressed, and uncompressed sizes of the entry.
if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
data->has_data_descriptor = 0;
- if (data->compressed_length != lfh->compressed_size
- || data->uncompressed_length != lfh->uncompressed_size
- || data->crc32 != lfh->crc32) {
- ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
- ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
- data->compressed_length, data->uncompressed_length, data->crc32,
- lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
+ if (data->compressed_length != lfh->compressed_size ||
+ data->uncompressed_length != lfh->uncompressed_size || data->crc32 != lfh->crc32) {
+ ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+ "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
+ data->compressed_length, data->uncompressed_length, data->crc32, lfh->compressed_size,
+ lfh->uncompressed_size, lfh->crc32);
return kInconsistentInformation;
}
} else {
data->has_data_descriptor = 1;
}
+ // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
+ if ((cdr->version_made_by >> 8) == 3) {
+ data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
+ } else {
+ data->unix_mode = 0777;
+ }
+
// Check that the local file header name matches the declared
// name in the central directory.
if (lfh->file_name_length == nameLen) {
@@ -626,8 +584,8 @@
return kInconsistentInformation;
}
- const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
- + lfh->file_name_length + lfh->extra_field_length;
+ const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader) +
+ lfh->file_name_length + lfh->extra_field_length;
if (data_offset > cd_offset) {
ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
return kInvalidOffset;
@@ -635,16 +593,17 @@
if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
- static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
+ static_cast<int64_t>(data_offset), data->compressed_length,
+ static_cast<int64_t>(cd_offset));
return kInvalidOffset;
}
if (data->method == kCompressStored &&
- static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
- ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
- static_cast<int64_t>(data_offset), data->uncompressed_length,
- static_cast<int64_t>(cd_offset));
- return kInvalidOffset;
+ static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
+ ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
+ static_cast<int64_t>(data_offset), data->uncompressed_length,
+ static_cast<int64_t>(cd_offset));
+ return kInvalidOffset;
}
data->offset = data_offset;
@@ -659,8 +618,7 @@
ZipString suffix;
ZipArchive* archive;
- IterationHandle(const ZipString* in_prefix,
- const ZipString* in_suffix) {
+ IterationHandle(const ZipString* in_prefix, const ZipString* in_suffix) {
if (in_prefix) {
uint8_t* name_copy = new uint8_t[in_prefix->name_length];
memcpy(name_copy, in_prefix->name, in_prefix->name_length);
@@ -687,8 +645,7 @@
}
};
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
- const ZipString* optional_prefix,
+int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
const ZipString* optional_suffix) {
ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
@@ -701,7 +658,7 @@
cookie->position = 0;
cookie->archive = archive;
- *cookie_ptr = cookie ;
+ *cookie_ptr = cookie;
return 0;
}
@@ -709,16 +666,14 @@
delete reinterpret_cast<IterationHandle*>(cookie);
}
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
- ZipEntry* data) {
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data) {
const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
if (entryName.name_length == 0) {
ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
return kInvalidEntryName;
}
- const int64_t ent = EntryToIndex(archive->hash_table,
- archive->hash_table_size, entryName);
+ const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName);
if (ent < 0) {
ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
@@ -746,10 +701,8 @@
for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
if (hash_table[i].name != NULL &&
- (handle->prefix.name_length == 0 ||
- hash_table[i].StartsWith(handle->prefix)) &&
- (handle->suffix.name_length == 0 ||
- hash_table[i].EndsWith(handle->suffix))) {
+ (handle->prefix.name_length == 0 || hash_table[i].StartsWith(handle->prefix)) &&
+ (handle->suffix.name_length == 0 || hash_table[i].EndsWith(handle->suffix))) {
handle->position = (i + 1);
const int error = FindEntry(archive, i, data);
if (!error) {
@@ -769,8 +722,10 @@
public:
virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
virtual ~Writer() {}
+
protected:
Writer() = default;
+
private:
DISALLOW_COPY_AND_ASSIGN(Writer);
};
@@ -780,14 +735,12 @@
// the data appended to it.
class MemoryWriter : public Writer {
public:
- MemoryWriter(uint8_t* buf, size_t size) : Writer(),
- buf_(buf), size_(size), bytes_written_(0) {
- }
+ MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
virtual bool Append(uint8_t* buf, size_t buf_size) override {
if (bytes_written_ + buf_size > size_) {
- ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
- size_, bytes_written_ + buf_size);
+ ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", size_,
+ bytes_written_ + buf_size);
return false;
}
@@ -806,7 +759,6 @@
// The file will be truncated to the end of the written data.
class FileWriter : public Writer {
public:
-
// Creates a FileWriter for |fd| and prepare to write |entry| to it,
// guaranteeing that the file descriptor is valid and that there's enough
// space on the volume to write out the entry completely and that the file
@@ -865,8 +817,8 @@
virtual bool Append(uint8_t* buf, size_t buf_size) override {
if (total_bytes_written_ + buf_size > declared_length_) {
- ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
- declared_length_, total_bytes_written_ + buf_size);
+ ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", declared_length_,
+ total_bytes_written_ + buf_size);
return false;
}
@@ -879,13 +831,10 @@
return result;
}
+
private:
- FileWriter(const int fd, const size_t declared_length) :
- Writer(),
- fd_(fd),
- declared_length_(declared_length),
- total_bytes_written_(0) {
- }
+ FileWriter(const int fd, const size_t declared_length)
+ : Writer(), fd_(fd), declared_length_(declared_length), total_bytes_written_(0) {}
const int fd_;
const size_t declared_length_;
@@ -928,8 +877,7 @@
zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
if (zerr != Z_OK) {
if (zerr == Z_VERSION_ERROR) {
- ALOGE("Installed zlib is not compatible with linked version (%s)",
- ZLIB_VERSION);
+ ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
} else {
ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
}
@@ -938,13 +886,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 */
@@ -964,19 +913,19 @@
/* uncompress the data */
zerr = inflate(&zstream, Z_NO_FLUSH);
if (zerr != Z_OK && zerr != Z_STREAM_END) {
- ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
- zerr, zstream.next_in, zstream.avail_in,
- zstream.next_out, zstream.avail_out);
+ ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, zstream.next_in,
+ zstream.avail_in, zstream.next_out, zstream.avail_out);
return kZlibError;
}
/* write when we're full or when we're done */
- if (zstream.avail_out == 0 ||
- (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
+ if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
const size_t write_size = zstream.next_out - &write_buf[0];
if (!writer->Append(&write_buf[0], write_size)) {
// The file might have declared a bogus length.
return kInconsistentInformation;
+ } else {
+ crc = crc32(crc, &write_buf[0], write_size);
}
zstream.next_out = &write_buf[0];
@@ -984,14 +933,19 @@
}
} while (zerr == Z_OK);
- assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+ assert(zerr == Z_STREAM_END); /* other errors should've been caught */
- // stream.adler holds the crc32 value for such streams.
- *crc_out = zstream.adler;
+ // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
+ // "feature" of zlib to tell it there won't be a zlib file header. zlib
+ // doesn't bother calculating the checksum in that scenario. We just do
+ // it ourselves above because there are no additional gains to be made by
+ // having zlib calculate it for us, since they do it by calling crc32 in
+ // the same manner that we have above.
+ *crc_out = crc;
if (zstream.total_out != uncompressed_length || compressed_length != 0) {
- ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
- zstream.total_out, uncompressed_length);
+ ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out,
+ uncompressed_length);
return kInconsistentInformation;
}
@@ -999,7 +953,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);
@@ -1029,8 +983,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;
@@ -1050,15 +1003,14 @@
}
if (!return_value && entry->has_data_descriptor) {
- return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry);
+ return_value = ValidateDataDescriptor(archive->mapped_zip, entry);
if (return_value) {
return return_value;
}
}
- // TODO: Fix this check by passing the right flags to inflate2 so that
- // it calculates the CRC for us.
- if (entry->crc32 != crc && false) {
+ // Validate that the CRC matches the calculated value.
+ if (kCrcChecksEnabled && (entry->crc32 != static_cast<uint32_t>(crc))) {
ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
return kInconsistentInformation;
}
@@ -1066,14 +1018,12 @@
return return_value;
}
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
- uint8_t* begin, uint32_t size) {
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size) {
std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
return ExtractToWriter(handle, entry, writer.get());
}
-int32_t ExtractEntryToFile(ZipArchiveHandle handle,
- ZipEntry* entry, int fd) {
+int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd) {
std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
if (writer.get() == nullptr) {
return kIoError;
@@ -1083,19 +1033,24 @@
}
const char* ErrorCodeString(int32_t error_code) {
- if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
- return kErrorMessages[error_code * -1];
+ // Make sure that the number of entries in kErrorMessages and ErrorCodes
+ // match.
+ static_assert((-kLastErrorCode + 1) == arraysize(kErrorMessages),
+ "(-kLastErrorCode + 1) != arraysize(kErrorMessages)");
+
+ const uint32_t idx = -error_code;
+ if (idx < arraysize(kErrorMessages)) {
+ return kErrorMessages[idx];
}
- return kErrorMessages[0];
+ return "Unknown return code";
}
int GetFileDescriptor(const ZipArchiveHandle handle) {
return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
}
-ZipString::ZipString(const char* entry_name)
- : name(reinterpret_cast<const uint8_t*>(entry_name)) {
+ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) {
size_t len = strlen(entry_name);
CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
name_length = static_cast<uint16_t>(len);
@@ -1104,10 +1059,8 @@
#if !defined(_WIN32)
class ProcessWriter : public Writer {
public:
- ProcessWriter(ProcessZipEntryFunction func, void* cookie) : Writer(),
- proc_function_(func),
- cookie_(cookie) {
- }
+ ProcessWriter(ProcessZipEntryFunction func, void* cookie)
+ : Writer(), proc_function_(func), cookie_(cookie) {}
virtual bool Append(uint8_t* buf, size_t buf_size) override {
return proc_function_(buf, buf_size, cookie_);
@@ -1124,7 +1077,7 @@
return ExtractToWriter(handle, entry, &writer);
}
-#endif //!defined(_WIN32)
+#endif //! defined(_WIN32)
int MappedZipFile::GetFileDescriptor() const {
if (!has_fd_) {
@@ -1167,8 +1120,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;
}
@@ -1179,7 +1131,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;
}
@@ -1205,7 +1157,6 @@
return false;
}
return ReadData(buf, len);
-
}
void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
@@ -1216,13 +1167,13 @@
bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
size_t cd_size) {
if (mapped_zip.HasFd()) {
- if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(),
- cd_start_offset, cd_size, true /* read only */)) {
+ if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(), cd_start_offset,
+ cd_size, true /* read only */)) {
return false;
}
CHECK_EQ(directory_map->getDataLength(), cd_size);
- central_directory.Initialize(directory_map->getDataPtr(), 0/*offset*/, cd_size);
+ central_directory.Initialize(directory_map->getDataPtr(), 0 /*offset*/, cd_size);
} else {
if (mapped_zip.GetBasePtr() == nullptr) {
ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
@@ -1230,9 +1181,10 @@
}
if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
mapped_zip.GetFileLength()) {
- ALOGE("Zip: Failed to map central directory, offset exceeds mapped memory region ("
- "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
- static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
+ ALOGE(
+ "Zip: Failed to map central directory, offset exceeds mapped memory region ("
+ "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
+ static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
return false;
}
@@ -1240,3 +1192,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 493a0ce..dbc14f0 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "zip_archive_private.h"
+
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
@@ -42,21 +44,13 @@
static const std::string kBadFilenameZip = "bad_filename.zip";
static const std::string kUpdateZip = "dummy-update.zip";
-static const std::vector<uint8_t> kATxtContents {
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
- '\n'
-};
+static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a',
+ 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
-static const std::vector<uint8_t> kATxtContentsCompressed {
- 'K', 'L', 'J', 'N', 'I', 'M', 'K', 207, 'H',
- 132, 210, '\\', '\0'
-};
+static const std::vector<uint8_t> kATxtContentsCompressed{'K', 'L', 'J', 'N', 'I', 'M', 'K',
+ 207, 'H', 132, 210, '\\', '\0'};
-static const std::vector<uint8_t> kBTxtContents {
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
- '\n'
-};
+static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
static const std::string kATxtName("a.txt");
static const std::string kBTxtName("b.txt");
@@ -65,14 +59,12 @@
static const std::string kLargeCompressTxtName("compress.txt");
static const std::string kLargeUncompressTxtName("uncompress.txt");
-static int32_t OpenArchiveWrapper(const std::string& name,
- ZipArchiveHandle* handle) {
+static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) {
const std::string abs_path = test_data_dir + "/" + name;
return OpenArchive(abs_path.c_str(), handle);
}
-static void AssertNameEquals(const std::string& name_str,
- const ZipString& name) {
+static void AssertNameEquals(const std::string& name_str, const ZipString& name) {
ASSERT_EQ(name_str.size(), name.name_length);
ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
}
@@ -343,47 +335,36 @@
}
static const uint32_t kEmptyEntriesZip[] = {
- 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000,
- 0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13,
- 0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b,
- 0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000,
- 0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974,
- 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
- 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
+ 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000,
+ 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875,
+ 0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863,
+ 0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081,
+ 0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904,
+ 0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000};
// This is a zip file containing a single entry (ab.txt) that contains
// 90072 repetitions of the string "ab\n" and has an uncompressed length
// of 270216 bytes.
static const uint16_t kAbZip[] = {
- 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0,
- 0x2cda, 0x011b, 0x0000, 0x1f88, 0x0004, 0x0006, 0x001c, 0x6261,
- 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
- 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000,
- 0xc2ed, 0x0d31, 0x0000, 0x030c, 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa,
- 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
- 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02,
- 0x1403, 0x0000, 0x0800, 0xd200, 0x9851, 0xb046, 0xdac4, 0x1b2c,
- 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
- 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574,
- 0x0554, 0x0300, 0x097c, 0x553a, 0x7875, 0x000b, 0x0401, 0x4289,
- 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
- 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000
-};
+ 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88,
+ 0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
+ 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c,
+ 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200,
+ 0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
+ 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a,
+ 0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
+ 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000};
static const std::string kAbTxtName("ab.txt");
static const size_t kAbUncompressedSize = 270216;
@@ -404,7 +385,6 @@
uint8_t buffer[1];
ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
-
TemporaryFile tmp_output_file;
ASSERT_NE(-1, tmp_output_file.fd);
ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
@@ -418,7 +398,7 @@
TemporaryFile tmp_file;
ASSERT_NE(-1, tmp_file.fd);
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
- sizeof(kAbZip) - 1));
+ sizeof(kAbZip) - 1));
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle));
@@ -446,8 +426,7 @@
// the same as the memory buffer we extracted directly to.
std::vector<uint8_t> file_contents(kAbUncompressedSize);
ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
- ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0],
- file_contents.size()));
+ ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
ASSERT_EQ(file_contents, buffer);
for (int i = 0; i < 90072; ++i) {
@@ -463,7 +442,7 @@
ASSERT_NE(-1, tmp_file.fd);
// Create a file with 8 bytes of random garbage.
- static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
+ static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'};
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
@@ -474,7 +453,7 @@
TEST(ziparchive, ExtractToFile) {
TemporaryFile tmp_file;
ASSERT_NE(-1, tmp_file.fd);
- const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
+ const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
const size_t data_size = sizeof(data);
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
@@ -488,7 +467,6 @@
ASSERT_EQ(0, FindEntry(handle, name, &entry));
ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
-
// Assert that the first 8 bytes of the file haven't been clobbered.
uint8_t read_buffer[data_size];
ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
@@ -497,10 +475,9 @@
// Assert that the remainder of the file contains the incompressed data.
std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
- ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
- entry.uncompressed_length));
- ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(),
- kATxtContents.size()));
+ ASSERT_TRUE(
+ android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), entry.uncompressed_length));
+ ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
// Assert that the total length of the file is sane
ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
@@ -517,7 +494,7 @@
// Memory map the file first and open the archive from the memory region.
android::FileMap file_map;
- file_map.create(zip_path.c_str(), fd, 0/*offset*/, sb.st_size, true);
+ file_map.create(zip_path.c_str(), fd, 0 /*offset*/, sb.st_size, true);
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFromMemory(file_map.getDataPtr(), file_map.getDataLength(),
zip_path.c_str(), &handle));
@@ -533,9 +510,8 @@
}
#endif
-static void ZipArchiveStreamTest(
- ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
- bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
+static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
+ bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
ZipString name;
SetZipString(&name, entry_name);
ASSERT_EQ(0, FindEntry(handle, name, entry));
@@ -564,9 +540,9 @@
ASSERT_EQ(total_size, read_data->size());
}
-static void ZipArchiveStreamTestUsingContents(
- const std::string& zip_file, const std::string& entry_name,
- const std::vector<uint8_t>& contents, bool raw) {
+static void ZipArchiveStreamTestUsingContents(const std::string& zip_file,
+ const std::string& entry_name,
+ const std::vector<uint8_t>& contents, bool raw) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
@@ -580,7 +556,8 @@
CloseArchive(handle);
}
-static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file, const std::string& entry_name) {
+static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file,
+ const std::string& entry_name) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
@@ -642,13 +619,109 @@
CloseArchive(handle);
}
+// Generated using the following Java program:
+// public static void main(String[] foo) throws Exception {
+// FileOutputStream fos = new
+// FileOutputStream("/tmp/data_descriptor.zip");
+// ZipOutputStream zos = new ZipOutputStream(fos);
+// ZipEntry ze = new ZipEntry("name");
+// ze.setMethod(ZipEntry.DEFLATED);
+// zos.putNextEntry(ze);
+// zos.write("abdcdefghijk".getBytes());
+// zos.closeEntry();
+// zos.close();
+// }
+//
+// cat /tmp/data_descriptor.zip | xxd -i
+//
+static const std::vector<uint8_t> kDataDescriptorZipFile{
+ 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
+ 0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
+ //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
+ 0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
+ 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
+ 0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
+ 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+// The offsets of the data descriptor in this file, so we can mess with
+// them later in the test.
+static constexpr uint32_t kDataDescriptorOffset = 48;
+static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
+static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
+
+static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
+ std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle));
+
+ // This function expects a variant of kDataDescriptorZipFile, for look for
+ // an entry whose name is "name" and whose size is 12 (contents =
+ // "abdcdefghijk").
+ ZipEntry entry;
+ ZipString empty_name;
+ SetZipString(&empty_name, "name");
+
+ ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+ ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
+
+ entry_out->resize(12);
+ (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, ValidDataDescriptors) {
+ std::vector<uint8_t> entry;
+ int32_t error_code = 0;
+ ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
+
+ ASSERT_EQ(0, error_code);
+ ASSERT_EQ(12u, entry.size());
+ ASSERT_EQ('a', entry[0]);
+ ASSERT_EQ('k', entry[11]);
+}
+
+TEST(ziparchive, InvalidDataDescriptors) {
+ std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
+ invalid_csize[kCSizeOffset] = 0xfe;
+
+ std::vector<uint8_t> entry;
+ int32_t error_code = 0;
+ ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+ ASSERT_EQ(kInconsistentInformation, error_code);
+
+ std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
+ invalid_csize[kSizeOffset] = 0xfe;
+
+ error_code = 0;
+ entry.clear();
+ ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+ ASSERT_EQ(kInconsistentInformation, error_code);
+}
+
+TEST(ziparchive, ErrorCodeString) {
+ ASSERT_STREQ("Success", ErrorCodeString(0));
+
+ // Out of bounds.
+ ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
+ ASSERT_STREQ("Unknown return code", ErrorCodeString(-13));
+
+ ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
+}
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
- static struct option options[] = {
- { "test_data_dir", required_argument, nullptr, 't' },
- { nullptr, 0, nullptr, 0 }
- };
+ static struct option options[] = {{"test_data_dir", required_argument, nullptr, 't'},
+ {nullptr, 0, nullptr, 0}};
while (true) {
int option_index;
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 6d28bdb..6ad3366 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -16,11 +16,11 @@
#include "ziparchive/zip_writer.h"
-#include <cstdio>
#include <sys/param.h>
#include <sys/stat.h>
#include <zlib.h>
-#define DEF_MEM_LEVEL 8 // normally in zutil.h?
+#include <cstdio>
+#define DEF_MEM_LEVEL 8 // normally in zutil.h?
#include <memory>
#include <vector>
@@ -33,13 +33,13 @@
#include "zip_archive_common.h"
#if !defined(powerof2)
-#define powerof2(x) ((((x)-1)&(x))==0)
+#define powerof2(x) ((((x)-1) & (x)) == 0)
#endif
/* Zip compression methods we support */
enum {
- kCompressStored = 0, // no compression
- kCompressDeflated = 8, // standard deflate
+ kCompressStored = 0, // no compression
+ kCompressDeflated = 8, // standard deflate
};
// Size of the output buffer used for compression.
@@ -67,10 +67,7 @@
static const int32_t kInvalidAlignment = -6;
static const char* sErrorCodes[] = {
- "Invalid state",
- "IO error",
- "Invalid entry name",
- "Zlib error",
+ "Invalid state", "IO error", "Invalid entry name", "Zlib error",
};
const char* ZipWriter::ErrorCodeString(int32_t error_code) {
@@ -85,9 +82,13 @@
delete stream;
}
-ZipWriter::ZipWriter(FILE* f) : file_(f), seekable_(false), current_offset_(0),
- state_(State::kWritingZip), z_stream_(nullptr, DeleteZStream),
- buffer_(kBufSize) {
+ZipWriter::ZipWriter(FILE* f)
+ : file_(f),
+ seekable_(false),
+ current_offset_(0),
+ state_(State::kWritingZip),
+ z_stream_(nullptr, DeleteZStream),
+ buffer_(kBufSize) {
// Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls
// will fail as well.
struct stat file_stats;
@@ -96,13 +97,14 @@
}
}
-ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
- seekable_(writer.seekable_),
- current_offset_(writer.current_offset_),
- state_(writer.state_),
- files_(std::move(writer.files_)),
- z_stream_(std::move(writer.z_stream_)),
- buffer_(std::move(writer.buffer_)){
+ZipWriter::ZipWriter(ZipWriter&& writer)
+ : file_(writer.file_),
+ seekable_(writer.seekable_),
+ current_offset_(writer.current_offset_),
+ state_(writer.state_),
+ files_(std::move(writer.files_)),
+ z_stream_(std::move(writer.z_stream_)),
+ buffer_(std::move(writer.buffer_)) {
writer.file_ = nullptr;
writer.state_ = State::kError;
}
@@ -154,10 +156,10 @@
struct tm* ptm;
#if !defined(_WIN32)
- struct tm tm_result;
- ptm = localtime_r(&when, &tm_result);
+ struct tm tm_result;
+ ptm = localtime_r(&when, &tm_result);
#else
- ptm = localtime(&when);
+ ptm = localtime(&when);
#endif
int year = ptm->tm_year;
@@ -193,8 +195,8 @@
dst->extra_field_length = src.padding_length;
}
-int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
- time_t time, uint32_t alignment) {
+int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
+ uint32_t alignment) {
if (state_ != State::kWritingZip) {
return kInvalidState;
}
@@ -252,9 +254,8 @@
return HandleError(kIoError);
}
- if (file_entry.padding_length != 0 &&
- fwrite(zero_padding.data(), 1, file_entry.padding_length, file_)
- != file_entry.padding_length) {
+ if (file_entry.padding_length != 0 && fwrite(zero_padding.data(), 1, file_entry.padding_length,
+ file_) != file_entry.padding_length) {
return HandleError(kIoError);
}
@@ -292,7 +293,7 @@
CHECK(state_ == State::kWritingZip);
// Initialize the z_stream for compression.
- z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream);
+ z_stream_ = std::unique_ptr<z_stream, void (*)(z_stream*)>(new z_stream(), DeleteZStream);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
@@ -331,8 +332,8 @@
return result;
}
- current_file_entry_.crc32 = crc32(current_file_entry_.crc32,
- reinterpret_cast<const Bytef*>(data), len);
+ current_file_entry_.crc32 =
+ crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len);
current_file_entry_.uncompressed_size += len;
return kNoError;
}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 5b526a4..c284273 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "ziparchive/zip_archive.h"
#include "ziparchive/zip_writer.h"
+#include "ziparchive/zip_archive.h"
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
@@ -135,17 +135,6 @@
CloseArchive(handle);
}
-static void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
- memset(tm, 0, sizeof(struct tm));
- tm->tm_hour = (zip_time >> 11) & 0x1f;
- tm->tm_min = (zip_time >> 5) & 0x3f;
- tm->tm_sec = (zip_time & 0x1f) << 1;
-
- tm->tm_year = ((zip_time >> 25) & 0x7f) + 80;
- tm->tm_mon = ((zip_time >> 21) & 0xf) - 1;
- tm->tm_mday = (zip_time >> 16) & 0x1f;
-}
-
static struct tm MakeTm() {
struct tm tm;
memset(&tm, 0, sizeof(struct tm));
@@ -177,8 +166,7 @@
ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
EXPECT_EQ(0, data.offset & 0x03);
- struct tm mod;
- ConvertZipTimeToTm(data.mod_time, &mod);
+ struct tm mod = data.GetModificationTime();
EXPECT_EQ(tm.tm_sec, mod.tm_sec);
EXPECT_EQ(tm.tm_min, mod.tm_min);
EXPECT_EQ(tm.tm_hour, mod.tm_hour);
@@ -228,8 +216,7 @@
ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
EXPECT_EQ(0, data.offset & 0xfff);
- struct tm mod;
- ConvertZipTimeToTm(data.mod_time, &mod);
+ struct tm mod = data.GetModificationTime();
EXPECT_EQ(tm.tm_sec, mod.tm_sec);
EXPECT_EQ(tm.tm_min, mod.tm_min);
EXPECT_EQ(tm.tm_hour, mod.tm_hour);
@@ -374,7 +361,7 @@
ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
std::vector<uint8_t> data;
- data.resize(1024*1024, 0xef);
+ data.resize(1024 * 1024, 0xef);
ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
ASSERT_EQ(0, writer.FinishEntry());
@@ -395,8 +382,9 @@
ZipArchiveHandle handle,
ZipEntry* zip_entry) {
if (expected.size() != zip_entry->uncompressed_length) {
- return ::testing::AssertionFailure() << "uncompressed entry size "
- << zip_entry->uncompressed_length << " does not match expected size " << expected.size();
+ return ::testing::AssertionFailure()
+ << "uncompressed entry size " << zip_entry->uncompressed_length
+ << " does not match expected size " << expected.size();
}
std::string actual;
@@ -409,7 +397,7 @@
if (expected != actual) {
return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
- << "' does not match expected '" << expected << "'";
+ << "' does not match expected '" << expected << "'";
}
return ::testing::AssertionSuccess();
}
diff --git a/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/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index cd80212..4397b14 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
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c32d41b..6b3f3dd 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 0755 system system
start ueventd
@@ -225,6 +227,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
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/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;
-}