Merge "Revert "Mark libmetricslogger vendor_available.""
diff --git a/adb/Android.bp b/adb/Android.bp
new file mode 100644
index 0000000..41f7b89
--- /dev/null
+++ b/adb/Android.bp
@@ -0,0 +1,51 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+python_binary_host {
+ name: "adb_integration_test_adb",
+ main: "test_adb.py",
+ srcs: [
+ "test_adb.py",
+ ],
+ libs: [
+ "adb_py",
+ ],
+ version: {
+ py2: {
+ enabled: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ },
+}
+
+python_binary_host {
+ name: "adb_integration_test_device",
+ main: "test_device.py",
+ srcs: [
+ "test_device.py",
+ ],
+ libs: [
+ "adb_py",
+ ],
+ version: {
+ py2: {
+ enabled: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ },
+}
diff --git a/adb/Android.mk b/adb/Android.mk
index 382c8cb..1f6f194 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -387,4 +387,9 @@
include $(BUILD_EXECUTABLE)
+# adb integration test
+# =========================================================
+$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_adb.BUILT))
+$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_device.BUILT))
+
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adb/adb.cpp b/adb/adb.cpp
index a7706a0..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;
}
}
@@ -1257,6 +1276,10 @@
void adb_notify_device_scan_complete() {
{
std::lock_guard<std::mutex> lock(init_mutex);
+ if (device_scan_complete) {
+ return;
+ }
+
device_scan_complete = true;
}
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index f5d0f02..e533a00 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -125,7 +125,7 @@
static int _adb_connect(const std::string& service, std::string* error) {
D("_adb_connect: %s", service.c_str());
- if (service.empty() || service.size() > MAX_PAYLOAD_V1) {
+ if (service.empty() || service.size() > MAX_PAYLOAD) {
*error = android::base::StringPrintf("bad service name length (%zd)",
service.size());
return -1;
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index ca8729e..38e3116 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -31,7 +31,7 @@
bool SendProtocolString(int fd, const std::string& s) {
unsigned int length = s.size();
- if (length > MAX_PAYLOAD_V1 - 4) {
+ if (length > MAX_PAYLOAD - 4) {
errno = EMSGSIZE;
return false;
}
diff --git a/adb/adb_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 c1d5549..f764a0e 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -17,7 +17,10 @@
#ifndef _ADB_UTILS_H_
#define _ADB_UTILS_H_
+#include <condition_variable>
+#include <mutex>
#include <string>
+#include <vector>
#include <android-base/macros.h>
@@ -53,4 +56,39 @@
bool forward_targets_are_valid(const std::string& source, const std::string& dest,
std::string* error);
+// A thread-safe blocking queue.
+template <typename T>
+class BlockingQueue {
+ std::mutex mutex;
+ std::condition_variable cv;
+ std::vector<T> queue;
+
+ public:
+ void Push(const T& t) {
+ {
+ std::unique_lock<std::mutex> lock(mutex);
+ queue.push_back(t);
+ }
+ cv.notify_one();
+ }
+
+ template <typename Fn>
+ void PopAll(Fn fn) {
+ std::vector<T> popped;
+
+ {
+ std::unique_lock<std::mutex> lock(mutex);
+ cv.wait(lock, [this]() { return !queue.empty(); });
+ popped = std::move(queue);
+ queue.clear();
+ }
+
+ for (const T& t : popped) {
+ fn(t);
+ }
+ }
+};
+
+std::string GetLogFilePath();
+
#endif
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 62798cd..f0d0ce7 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -39,33 +39,7 @@
#include "sysdeps/chrono.h"
#include "transport.h"
-static std::string GetLogFilePath() {
-#if defined(_WIN32)
- const char log_name[] = "adb.log";
- WCHAR temp_path[MAX_PATH];
-
- // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
- DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
- if (nchars >= arraysize(temp_path) || nchars == 0) {
- // If string truncation or some other error.
- fatal("cannot retrieve temporary file path: %s\n",
- android::base::SystemErrorCodeToString(GetLastError()).c_str());
- }
-
- std::string temp_path_utf8;
- if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
- fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
- }
-
- return temp_path_utf8 + log_name;
-#else
- const char* tmp_dir = getenv("TMPDIR");
- if (tmp_dir == nullptr) tmp_dir = "/tmp";
- return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
-#endif
-}
-
-static void setup_daemon_logging(void) {
+static void setup_daemon_logging() {
const std::string log_file_path(GetLogFilePath());
int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
if (fd == -1) {
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index fc32469..e7f44c6 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -37,6 +37,7 @@
#include <android-base/strings.h>
#include "adb.h"
+#include "adb_utils.h"
#include "transport.h"
#include "usb.h"
@@ -179,10 +180,6 @@
if (port_count < 0) return "";
return StringPrintf("/dev/bus/usb/%03u/%03u", libusb_get_bus_number(device), ports[0]);
}
-
-static bool is_device_accessible(libusb_device* device) {
- return access(get_device_dev_path(device).c_str(), R_OK | W_OK) == 0;
-}
#endif
static bool endpoint_is_output(uint8_t endpoint) {
@@ -376,9 +373,10 @@
{
std::unique_lock<std::mutex> lock(usb_handles_mutex);
usb_handles[device_address] = std::move(result);
- }
- register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), writable);
+ register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(),
+ writable);
+ }
LOG(INFO) << "registered new usb device '" << device_serial << "'";
}
@@ -389,20 +387,15 @@
// Android's host linux libusb uses netlink instead of udev for device hotplug notification,
// which means we can get hotplug notifications before udev has updated ownership/perms on the
// device. Since we're not going to be able to link against the system's libudev any time soon,
- // hack around this by checking for accessibility in a loop.
- ++connecting_devices;
+ // hack around this by inserting a sleep.
auto thread = std::thread([device]() {
std::string device_path = get_device_dev_path(device);
- auto start = std::chrono::steady_clock::now();
- while (std::chrono::steady_clock::now() - start < 500ms) {
- if (is_device_accessible(device)) {
- break;
- }
- std::this_thread::sleep_for(10ms);
- }
+ std::this_thread::sleep_for(1s);
process_device(device);
- --connecting_devices;
+ if (--connecting_devices == 0) {
+ adb_notify_device_scan_complete();
+ }
});
thread.detach();
#else
@@ -420,18 +413,40 @@
if (!it->second->device_handle) {
// If the handle is null, we were never able to open the device.
unregister_usb_transport(it->second.get());
+ usb_handles.erase(it);
+ } else {
+ // Closure of the transport will erase the usb_handle.
}
- usb_handles.erase(it);
+ }
+}
+
+static auto& hotplug_queue = *new BlockingQueue<std::pair<libusb_hotplug_event, libusb_device*>>();
+static void hotplug_thread() {
+ adb_thread_setname("libusb hotplug");
+ while (true) {
+ hotplug_queue.PopAll([](std::pair<libusb_hotplug_event, libusb_device*> pair) {
+ libusb_hotplug_event event = pair.first;
+ libusb_device* device = pair.second;
+ if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
+ device_connected(device);
+ } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
+ device_disconnected(device);
+ }
+ });
}
}
static int hotplug_callback(libusb_context*, libusb_device* device, libusb_hotplug_event event,
void*) {
+ // We're called with the libusb lock taken. Call these on a separate thread outside of this
+ // function so that the usb_handle mutex is always taken before the libusb mutex.
+ static std::once_flag once;
+ std::call_once(once, []() { std::thread(hotplug_thread).detach(); });
+
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
- device_connected(device);
- } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
- device_disconnected(device);
+ ++connecting_devices;
}
+ hotplug_queue.Push({event, device});
return 0;
}
@@ -453,13 +468,6 @@
LOG(FATAL) << "failed to register libusb hotplug callback";
}
- // Wait for all of the connecting devices to finish.
- while (connecting_devices != 0) {
- std::this_thread::sleep_for(10ms);
- }
-
- adb_notify_device_scan_complete();
-
// Spawn a thread for libusb_handle_events.
std::thread([]() {
adb_thread_setname("libusb");
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 68ae4af..c9f1ee9 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -599,6 +599,13 @@
std::string service_string = ShellServiceString(use_shell_protocol,
type_arg, command);
+ // Old devices can't handle a service string that's longer than MAX_PAYLOAD_V1.
+ // Use |use_shell_protocol| to determine whether to allow a command longer than that.
+ if (service_string.size() > MAX_PAYLOAD_V1 && !use_shell_protocol) {
+ fprintf(stderr, "error: shell command too long\n");
+ return 1;
+ }
+
// Make local stdin raw if the device allocates a PTY, which happens if:
// 1. We are explicitly asking for a PTY shell, or
// 2. We don't specify shell type and are starting an interactive session.
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 14ad1ff..e0143c6 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -686,7 +686,7 @@
}
len = unhex(p->data, 4);
- if ((len < 1) || (len > MAX_PAYLOAD_V1)) {
+ if ((len < 1) || (len > MAX_PAYLOAD)) {
D("SS(%d): bad size (%d)", s->id, len);
goto fail;
}
diff --git a/adb/test_device.py b/adb/test_device.py
index 737d0c2..9e1a2ec 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -342,6 +342,13 @@
out = self.device.shell(['echo', 'foo'])[0]
self.assertEqual(out, 'foo' + self.device.linesep)
+ def test_shell_command_length(self):
+ # Devices that have shell_v2 should be able to handle long commands.
+ if self.device.has_shell_protocol():
+ rc, out, err = self.device.shell_nocheck(['echo', 'x' * 16384])
+ self.assertEqual(rc, 0)
+ self.assertTrue(out == ('x' * 16384 + '\n'))
+
def test_shell_nocheck_failure(self):
rc, out, _ = self.device.shell_nocheck(['false'])
self.assertNotEqual(rc, 0)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 308ee8d..2bbbefd 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -603,15 +603,15 @@
static void transport_unref(atransport* t) {
CHECK(t != nullptr);
- std::lock_guard<std::mutex> lock(transport_lock);
- CHECK_GT(t->ref_count, 0u);
- t->ref_count--;
- if (t->ref_count == 0) {
+ size_t old_refcount = t->ref_count--;
+ CHECK_GT(old_refcount, 0u);
+
+ if (old_refcount == 1u) {
D("transport: %s unref (kicking and closing)", t->serial);
t->close(t);
remove_transport(t);
} else {
- D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
+ D("transport: %s unref (count=%zu)", t->serial, old_refcount - 1);
}
}
diff --git a/adb/transport.h b/adb/transport.h
index 57fc988..4a89ed9 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -61,7 +61,7 @@
// class in one go is a very large change. Given how bad our testing is,
// it's better to do this piece by piece.
- atransport(ConnectionState state = kCsOffline) : connection_state_(state) {
+ atransport(ConnectionState state = kCsOffline) : ref_count(0), connection_state_(state) {
transport_fde = {};
protocol_version = A_VERSION;
max_payload = MAX_PAYLOAD;
@@ -88,7 +88,7 @@
int fd = -1;
int transport_socket = -1;
fdevent transport_fde;
- size_t ref_count = 0;
+ std::atomic<size_t> ref_count;
uint32_t sync_token = 0;
bool online = false;
TransportType type = kTransportAny;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index a9e583f..6cedd92 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -388,6 +388,25 @@
D("transport: qemu_socket_thread() exiting");
return;
}
+
+// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka
+// goldfish) as the transport. This can either be explicitly set by the
+// service.adb.transport property, or be inferred from ro.kernel.qemu that is
+// set to "1" for ranchu/goldfish.
+static bool use_qemu_goldfish() {
+ // Legacy way to detect if adbd should use the goldfish pipe is to check for
+ // ro.kernel.qemu, keep that behaviour for backward compatibility.
+ if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+ return true;
+ }
+ // If service.adb.transport is present and is set to "goldfish", use the
+ // QEMUD pipe.
+ if (android::base::GetProperty("service.adb.transport", "") == "goldfish") {
+ return true;
+ }
+ return false;
+}
+
#endif // !ADB_HOST
void local_init(int port)
@@ -401,13 +420,7 @@
#else
// For the adbd daemon in the system image we need to distinguish
// between the device, and the emulator.
- if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
- // Running inside the emulator: use QEMUD pipe as the transport.
- func = qemu_socket_thread;
- } else {
- // Running inside the device: use TCP socket as the transport.
- func = server_socket_thread;
- }
+ func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread;
debug_name = "server";
#endif // !ADB_HOST
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 47094b8..2f46920 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -192,7 +192,7 @@
#if defined(_WIN32) || !ADB_HOST
return false;
#else
- static bool disable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "0") == 0;
- return !disable;
+ static bool enable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "1") == 0;
+ return enable;
#endif
}
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
index 5eedf3b..b6bf701 100644
--- a/base/chrono_utils.cpp
+++ b/base/chrono_utils.cpp
@@ -33,5 +33,10 @@
#endif // __ANDROID__
}
+std::ostream& operator<<(std::ostream& os, const Timer& t) {
+ os << t.duration().count() << "ms";
+ return os;
+}
+
} // namespace base
} // namespace android
diff --git a/base/chrono_utils_test.cpp b/base/chrono_utils_test.cpp
index 057132d..da442f4 100644
--- a/base/chrono_utils_test.cpp
+++ b/base/chrono_utils_test.cpp
@@ -19,6 +19,9 @@
#include <time.h>
#include <chrono>
+#include <sstream>
+#include <string>
+#include <thread>
#include <gtest/gtest.h>
@@ -42,5 +45,36 @@
EXPECT_EQ(now, boot_seconds);
}
+template <typename T>
+void ExpectAboutEqual(T expected, T actual) {
+ auto expected_upper_bound = expected * 1.05f;
+ auto expected_lower_bound = expected * .95;
+ EXPECT_GT(expected_upper_bound, actual);
+ EXPECT_LT(expected_lower_bound, actual);
+}
+
+TEST(ChronoUtilsTest, TimerDurationIsSane) {
+ auto start = boot_clock::now();
+ Timer t;
+ std::this_thread::sleep_for(50ms);
+ auto stop = boot_clock::now();
+ auto stop_timer = t.duration();
+
+ auto expected = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
+ ExpectAboutEqual(expected, stop_timer);
+}
+
+TEST(ChronoUtilsTest, TimerOstream) {
+ Timer t;
+ std::this_thread::sleep_for(50ms);
+ auto stop_timer = t.duration().count();
+ std::stringstream os;
+ os << t;
+ decltype(stop_timer) stop_timer_from_stream;
+ os >> stop_timer_from_stream;
+ EXPECT_NE(0, stop_timer);
+ ExpectAboutEqual(stop_timer, stop_timer_from_stream);
+}
+
} // namespace base
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
index 0086425..7679d4c 100644
--- a/base/include/android-base/chrono_utils.h
+++ b/base/include/android-base/chrono_utils.h
@@ -18,6 +18,9 @@
#define ANDROID_BASE_CHRONO_UTILS_H
#include <chrono>
+#include <sstream>
+
+using namespace std::chrono_literals;
namespace android {
namespace base {
@@ -31,6 +34,20 @@
static time_point now();
};
+class Timer {
+ public:
+ Timer() : start_(boot_clock::now()) {}
+
+ std::chrono::milliseconds duration() const {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_);
+ }
+
+ private:
+ boot_clock::time_point start_;
+};
+
+std::ostream& operator<<(std::ostream& os, const Timer& t);
+
} // namespace base
} // namespace android
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index fa0d922..548b286 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -176,15 +176,19 @@
// Provides an expression that evaluates to the truthiness of `x`, automatically
// aborting if `c` is true.
#define ABORT_AFTER_LOG_EXPR_IF(c, x) (((c) && ::android::base::LogAbortAfterFullExpr()) || (x))
+// Note to the static analyzer that we always execute FATAL logs in practice.
+#define MUST_LOG_MESSAGE(severity) (SEVERITY_LAMBDA(severity) == ::android::base::FATAL)
#else
#define ABORT_AFTER_LOG_FATAL
#define ABORT_AFTER_LOG_EXPR_IF(c, x) (x)
+#define MUST_LOG_MESSAGE(severity) false
#endif
#define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x)
// Defines whether the given severity will be logged or silently swallowed.
#define WOULD_LOG(severity) \
- UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity())
+ (UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity()) || \
+ MUST_LOG_MESSAGE(severity))
// Get an ostream that can be used for logging at the given severity and to the default
// destination.
@@ -315,7 +319,7 @@
// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
// CHECK should be used unless profiling identifies a CHECK as being in
// performance critical code.
-#if defined(NDEBUG)
+#if defined(NDEBUG) && !defined(__clang_analyzer__)
static constexpr bool kEnableDChecks = false;
#else
static constexpr bool kEnableDChecks = true;
@@ -339,7 +343,7 @@
if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
#define DCHECK_STRNE(s1, s2) \
if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
-#if defined(NDEBUG)
+#if defined(NDEBUG) && !defined(__clang_analyzer__)
#define DCHECK_CONSTEXPR(x, out, dummy)
#else
#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
index 2d5a6f6..c9cc1ab 100755
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -22,6 +22,8 @@
#else
// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
#include <fcntl.h> // open
+#include <stdio.h> // fopen
+#include <sys/stat.h> // mkdir
#include <unistd.h> // unlink
#endif
@@ -53,6 +55,19 @@
// Convert a UTF-8 std::string (including any embedded NULL characters) to
// UTF-16. Returns whether the conversion was done successfully.
bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
+
+// Convert a file system path, represented as a NULL-terminated string of
+// UTF-8 characters, to a UTF-16 string representing the same file system
+// path using the Windows extended-lengh path representation.
+//
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH:
+// ```The Windows API has many functions that also have Unicode versions to
+// permit an extended-length path for a maximum total path length of 32,767
+// characters. To specify an extended-length path, use the "\\?\" prefix.
+// For example, "\\?\D:\very long path".```
+//
+// Returns whether the conversion was done successfully.
+bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16);
#endif
// The functions in the utf8 namespace take UTF-8 strings. For Windows, these
@@ -73,9 +88,13 @@
namespace utf8 {
#ifdef _WIN32
+FILE* fopen(const char* name, const char* mode);
+int mkdir(const char* name, mode_t mode);
int open(const char* name, int flags, ...);
int unlink(const char* name);
#else
+using ::fopen;
+using ::mkdir;
using ::open;
using ::unlink;
#endif
diff --git a/base/utf8.cpp b/base/utf8.cpp
index 3cca700..5984fb0 100644
--- a/base/utf8.cpp
+++ b/base/utf8.cpp
@@ -19,7 +19,9 @@
#include "android-base/utf8.h"
#include <fcntl.h>
+#include <stdio.h>
+#include <algorithm>
#include <string>
#include "android-base/logging.h"
@@ -153,12 +155,58 @@
return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
}
+static bool isDriveLetter(wchar_t c) {
+ return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z');
+}
+
+bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16) {
+ if (!UTF8ToWide(utf8, utf16)) {
+ return false;
+ }
+ // Note: Although most Win32 File I/O API are limited to MAX_PATH (260
+ // characters), the CreateDirectory API is limited to 248 characters.
+ if (utf16->length() >= 248) {
+ // If path is of the form "x:\" or "x:/"
+ if (isDriveLetter((*utf16)[0]) && (*utf16)[1] == L':' &&
+ ((*utf16)[2] == L'\\' || (*utf16)[2] == L'/')) {
+ // Append long path prefix, and make sure there are no unix-style
+ // separators to ensure a fully compliant Win32 long path string.
+ utf16->insert(0, LR"(\\?\)");
+ std::replace(utf16->begin(), utf16->end(), L'/', L'\\');
+ }
+ }
+ return true;
+}
+
// Versions of standard library APIs that support UTF-8 strings.
namespace utf8 {
+FILE* fopen(const char* name, const char* mode) {
+ std::wstring name_utf16;
+ if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
+ return nullptr;
+ }
+
+ std::wstring mode_utf16;
+ if (!UTF8ToWide(mode, &mode_utf16)) {
+ return nullptr;
+ }
+
+ return _wfopen(name_utf16.c_str(), mode_utf16.c_str());
+}
+
+int mkdir(const char* name, mode_t mode) {
+ std::wstring name_utf16;
+ if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
+ return -1;
+ }
+
+ return _wmkdir(name_utf16.c_str());
+}
+
int open(const char* name, int flags, ...) {
std::wstring name_utf16;
- if (!UTF8ToWide(name, &name_utf16)) {
+ if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
return -1;
}
@@ -175,7 +223,7 @@
int unlink(const char* name) {
std::wstring name_utf16;
- if (!UTF8ToWide(name, &name_utf16)) {
+ if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
return -1;
}
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
index ae8fc8c..fcb25c3 100644
--- a/base/utf8_test.cpp
+++ b/base/utf8_test.cpp
@@ -18,7 +18,12 @@
#include <gtest/gtest.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
#include "android-base/macros.h"
+#include "android-base/test_utils.h"
+#include "android-base/unique_fd.h"
namespace android {
namespace base {
@@ -408,5 +413,76 @@
EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
}
+TEST(UTF8PathToWindowsLongPathTest, DontAddPrefixIfShorterThanMaxPath) {
+ std::string utf8 = "c:\\mypath\\myfile.txt";
+
+ std::wstring wide;
+ EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+ EXPECT_EQ(std::string::npos, wide.find(LR"(\\?\)"));
+}
+
+TEST(UTF8PathToWindowsLongPathTest, AddPrefixIfLongerThanMaxPath) {
+ std::string utf8 = "c:\\mypath";
+ while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+ utf8 += "\\mypathsegment";
+ }
+
+ std::wstring wide;
+ EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+ EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
+ EXPECT_EQ(std::string::npos, wide.find(L"/"));
+}
+
+TEST(UTF8PathToWindowsLongPathTest, AddPrefixAndFixSeparatorsIfLongerThanMaxPath) {
+ std::string utf8 = "c:/mypath";
+ while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+ utf8 += "/mypathsegment";
+ }
+
+ std::wstring wide;
+ EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+ EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
+ EXPECT_EQ(std::string::npos, wide.find(L"/"));
+}
+
+namespace utf8 {
+
+TEST(Utf8FilesTest, CanCreateOpenAndDeleteFileWithLongPath) {
+ TemporaryDir td;
+
+ // Create long directory path
+ std::string utf8 = td.path;
+ while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+ utf8 += "\\mypathsegment";
+ EXPECT_EQ(0, mkdir(utf8.c_str(), 0));
+ }
+
+ // Create file
+ utf8 += "\\test-file.bin";
+ int flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
+ int mode = 0666;
+ android::base::unique_fd fd(open(utf8.c_str(), flags, mode));
+ EXPECT_NE(-1, fd.get());
+
+ // Close file
+ fd.reset();
+ EXPECT_EQ(-1, fd.get());
+
+ // Open file with fopen
+ FILE* file = fopen(utf8.c_str(), "rb");
+ EXPECT_NE(nullptr, file);
+
+ if (file) {
+ fclose(file);
+ }
+
+ // Delete file
+ EXPECT_EQ(0, unlink(utf8.c_str()));
+}
+
+} // namespace utf8
} // namespace base
} // namespace android
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index d4e215e..344fa9a 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -85,12 +85,13 @@
fprintf(stderr, "Usage: %s [options]\n", cmd);
fprintf(stderr,
"options include:\n"
- " -h, --help Show this help\n"
- " -l, --log Log all metrics to logstorage\n"
- " -p, --print Dump the boot event records to the console\n"
- " -r, --record Record the timestamp of a named boot event\n"
- " --value Optional value to associate with the boot event\n"
- " --record_boot_reason Record the reason why the device booted\n"
+ " -h, --help Show this help\n"
+ " -l, --log Log all metrics to logstorage\n"
+ " -p, --print Dump the boot event records to the console\n"
+ " -r, --record Record the timestamp of a named boot event\n"
+ " --value Optional value to associate with the boot event\n"
+ " --record_boot_complete Record metrics related to the time for the device boot\n"
+ " --record_boot_reason Record the reason why the device booted\n"
" --record_time_since_factory_reset Record the time since the device was reset\n");
}
@@ -201,8 +202,10 @@
BootEventRecordStore boot_event_store;
BootEventRecordStore::BootEventRecord record;
- if (!boot_event_store.GetBootEvent(kBuildDateKey, &record) ||
- build_date != record.second) {
+ if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {
+ boot_complete_prefix = "factory_reset_" + boot_complete_prefix;
+ boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+ } else if (build_date != record.second) {
boot_complete_prefix = "ota_" + boot_complete_prefix;
boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
}
@@ -241,7 +244,7 @@
for (const auto& stageTiming : stages) {
// |stageTiming| is of the form 'stage:time'.
auto stageTimingValues = android::base::Split(stageTiming, ":");
- DCHECK_EQ(2, stageTimingValues.size());
+ DCHECK_EQ(2U, stageTimingValues.size());
std::string stageName = stageTimingValues[0];
int32_t time_ms;
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 3a80b50..f86aaa0 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -11,6 +11,11 @@
local_include_dirs: ["include"],
}
+cc_library_headers {
+ name: "libdebuggerd_common_headers",
+ export_include_dirs: ["common/include"]
+}
+
cc_library_shared {
name: "libtombstoned_client",
defaults: ["debuggerd_defaults"],
@@ -19,15 +24,18 @@
"util.cpp",
],
+ header_libs: ["libdebuggerd_common_headers"],
+
static_libs: [
- "libasync_safe"
+ "libasync_safe",
],
shared_libs: [
- "libcutils",
"libbase",
+ "libcutils",
],
+ export_header_lib_headers: ["libdebuggerd_common_headers"],
export_include_dirs: ["tombstoned/include"]
}
@@ -40,12 +48,15 @@
"util.cpp",
],
+ header_libs: ["libdebuggerd_common_headers"],
+
whole_static_libs: [
"libasync_safe",
"libcutils",
"libbase",
],
+ export_header_lib_headers: ["libdebuggerd_common_headers"],
export_include_dirs: ["tombstoned/include"]
}
@@ -55,11 +66,14 @@
defaults: ["debuggerd_defaults"],
srcs: ["handler/debuggerd_handler.cpp"],
+ header_libs: ["libdebuggerd_common_headers"],
+
whole_static_libs: [
"libasync_safe",
"libdebuggerd",
],
+ export_header_lib_headers: ["libdebuggerd_common_headers"],
export_include_dirs: ["include"],
}
@@ -107,11 +121,14 @@
"util.cpp",
],
+ header_libs: ["libdebuggerd_common_headers"],
+
shared_libs: [
"libbase",
"libcutils",
],
+ export_header_lib_headers: ["libdebuggerd_common_headers"],
export_include_dirs: ["include"],
}
@@ -191,7 +208,8 @@
"libbase",
"libcutils",
"libdebuggerd_client",
- "liblog"
+ "liblog",
+ "libnativehelper"
],
static_libs: [
@@ -256,7 +274,7 @@
"libbase",
"libdebuggerd_client",
"liblog",
- "libselinux",
+ "libprocinfo",
],
local_include_dirs: ["include"],
@@ -271,6 +289,8 @@
],
defaults: ["debuggerd_defaults"],
+ header_libs: ["libdebuggerd_common_headers"],
+
static_libs: [
"libbase",
"libcutils",
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 4ce038c..cb7cbbe 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -28,7 +28,9 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
@@ -40,10 +42,12 @@
using android::base::unique_fd;
-static bool send_signal(pid_t pid, bool backtrace) {
+static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
+ const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : DEBUGGER_SIGNAL;
sigval val;
- val.sival_int = backtrace;
- if (sigqueue(pid, DEBUGGER_SIGNAL, val) != 0) {
+ val.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0;
+
+ if (sigqueue(pid, signal, val) != 0) {
PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid;
return false;
}
@@ -58,8 +62,8 @@
tv->tv_usec = static_cast<long>(microseconds.count());
}
-bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
- unsigned int timeout_ms) {
+bool debuggerd_trigger_dump(pid_t pid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
+ unique_fd output_fd) {
LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
unique_fd sockfd;
const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
@@ -102,7 +106,7 @@
return false;
}
- InterceptRequest req = {.pid = pid };
+ InterceptRequest req = {.pid = pid, .dump_type = dump_type};
if (!set_timeout(sockfd)) {
PLOG(ERROR) << "libdebugger_client: failed to set timeout";
return false;
@@ -115,6 +119,20 @@
return false;
}
+ std::string pipe_size_str;
+ int pipe_buffer_size = 1024 * 1024;
+ if (android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
+ pipe_size_str = android::base::Trim(pipe_size_str);
+
+ if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
+ LOG(FATAL) << "failed to parse pipe max size '" << pipe_size_str << "'";
+ }
+ }
+
+ if (fcntl(pipe_read.get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
+ PLOG(ERROR) << "failed to set pipe buffer size";
+ }
+
if (send_fd(set_timeout(sockfd), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
return false;
@@ -140,8 +158,7 @@
return false;
}
- bool backtrace = dump_type == kDebuggerdBacktrace;
- if (!send_signal(pid, backtrace)) {
+ if (!send_signal(pid, dump_type)) {
return false;
}
@@ -210,15 +227,16 @@
return true;
}
-int dump_backtrace_to_file(pid_t tid, int fd) {
- return dump_backtrace_to_file_timeout(tid, fd, 0);
+int dump_backtrace_to_file(pid_t tid, DebuggerdDumpType dump_type, int fd) {
+ return dump_backtrace_to_file_timeout(tid, dump_type, 0, fd);
}
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
+int dump_backtrace_to_file_timeout(pid_t tid, DebuggerdDumpType dump_type, int timeout_secs,
+ int fd) {
android::base::unique_fd copy(dup(fd));
if (copy == -1) {
return -1;
}
int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
- return debuggerd_trigger_dump(tid, std::move(copy), kDebuggerdBacktrace, timeout_ms) ? 0 : -1;
+ return debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
}
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index 8f97db1..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);
@@ -67,7 +80,8 @@
// Wait for a bit to let the child spawn all of its threads.
std::this_thread::sleep_for(250ms);
- ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(pipe_write), kDebuggerdBacktrace, 10000));
+ ASSERT_TRUE(
+ debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 10000, std::move(pipe_write)));
// Immediately kill the forked child, to make sure that the dump didn't return early.
ASSERT_EQ(0, kill(forkpid, SIGKILL)) << strerror(errno);
@@ -107,5 +121,6 @@
unique_fd output_read, output_write;
ASSERT_TRUE(Pipe(&output_read, &output_write));
- ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(output_write), kDebuggerdBacktrace, 0));
+ ASSERT_TRUE(
+ debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 0, std::move(output_write)));
}
diff --git a/debuggerd/common/include/dump_type.h b/debuggerd/common/include/dump_type.h
new file mode 100644
index 0000000..203269e
--- /dev/null
+++ b/debuggerd/common/include/dump_type.h
@@ -0,0 +1,49 @@
+#pragma once
+
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+
+#include <ostream>
+
+enum DebuggerdDumpType : uint8_t {
+ kDebuggerdNativeBacktrace,
+ kDebuggerdTombstone,
+ kDebuggerdJavaBacktrace,
+ kDebuggerdAnyIntercept
+};
+
+inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) {
+ switch (rhs) {
+ case kDebuggerdNativeBacktrace:
+ stream << "kDebuggerdNativeBacktrace";
+ break;
+ case kDebuggerdTombstone:
+ stream << "kDebuggerdTombstone";
+ break;
+ case kDebuggerdJavaBacktrace:
+ stream << "kDebuggerdJavaBacktrace";
+ break;
+ case kDebuggerdAnyIntercept:
+ stream << "kDebuggerdAnyIntercept";
+ break;
+ default:
+ stream << "[unknown]";
+ }
+
+ return stream;
+}
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index be28079..4b1e51d 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -41,8 +41,12 @@
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
#include <log/log.h>
+#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"
@@ -99,8 +103,10 @@
return true;
}
-static bool activity_manager_notify(int pid, int signal, const std::string& amfd_data) {
- android::base::unique_fd amfd(socket_local_client("/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
+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) {
PLOG(ERROR) << "unable to connect to activity manager";
return false;
@@ -150,13 +156,13 @@
_exit(1);
}
-static void abort_handler(pid_t target, const bool& tombstoned_connected,
+static void abort_handler(pid_t target, const bool tombstoned_connected,
unique_fd& tombstoned_socket, unique_fd& output_fd,
const char* abort_msg) {
// If we abort before we get an output fd, contact tombstoned to let any
// potential listeners know that we failed.
if (!tombstoned_connected) {
- if (!tombstoned_connect(target, &tombstoned_socket, &output_fd)) {
+ if (!tombstoned_connect(target, &tombstoned_socket, &output_fd, kDebuggerdAnyIntercept)) {
// We failed to connect, not much we can do.
LOG(ERROR) << "failed to connected to tombstoned to report failure";
_exit(1);
@@ -174,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;
@@ -192,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;
@@ -207,12 +216,19 @@
action.sa_handler = signal_handler;
debuggerd_register_handlers(&action);
- if (argc != 3) {
- return 1;
+ sigset_t mask;
+ sigemptyset(&mask);
+ if (sigprocmask(SIG_SETMASK, &mask, nullptr) != 0) {
+ PLOG(FATAL) << "failed to set signal mask";
+ }
+
+ if (argc != 4) {
+ LOG(FATAL) << "Wrong number of args: " << argc << " (expected 4)";
}
pid_t main_tid;
pid_t pseudothread_tid;
+ int dump_type;
if (!android::base::ParseInt(argv[1], &main_tid, 1, std::numeric_limits<pid_t>::max())) {
LOG(FATAL) << "invalid main tid: " << argv[1];
@@ -222,6 +238,10 @@
LOG(FATAL) << "invalid pseudothread tid: " << argv[2];
}
+ if (!android::base::ParseInt(argv[3], &dump_type, 0, 1)) {
+ LOG(FATAL) << "invalid requested dump type: " << argv[3];
+ }
+
if (target == 1) {
LOG(FATAL) << "target died before we could attach (received main tid = " << main_tid << ")";
}
@@ -248,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();
@@ -257,47 +279,63 @@
exit(0);
}
+ ATRACE_NAME("after reparent");
+
// Die if we take too long.
- alarm(20);
+ //
+ // Note: processes with many threads and minidebug-info can take a bit to
+ // unwind, do not make this too small. b/62828735
+ alarm(5);
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));
@@ -305,8 +343,12 @@
// Drop our capabilities now that we've attached to the threads we care about.
drop_capabilities();
- LOG(INFO) << "obtaining output fd from tombstoned";
- tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd);
+ {
+ ATRACE_NAME("tombstoned_connect");
+ const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type);
+ LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum;
+ tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum);
+ }
// Write a '\1' to stdout to tell the crashing process to resume.
// It also restores the value of PR_SET_DUMPABLE at this point.
@@ -335,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;
@@ -359,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);
}
@@ -401,7 +448,10 @@
}
if (fatal_signal) {
- activity_manager_notify(target, signo, amfd_data);
+ // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout.
+ if (target_info.name != "system_server" || target_info.uid != AID_SYSTEM) {
+ activity_manager_notify(target, signo, amfd_data);
+ }
}
// Close stdout before we notify tombstoned of completion.
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 6970201..f57349b 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -134,6 +134,14 @@
free(buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
}
+noinline void leak() {
+ while (true) {
+ void* mapping =
+ mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ static_cast<volatile char*>(mapping)[0] = 'a';
+ }
+}
+
noinline void sigsegv_non_null() {
int* a = (int *)(&do_action);
*a = 42;
@@ -160,8 +168,8 @@
fprintf(stderr, " stack-overflow recurse until the stack overflows\n");
fprintf(stderr, " nostack crash with a NULL stack pointer\n");
fprintf(stderr, "\n");
- fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n");
fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n");
+ fprintf(stderr, " leak leak memory until we get OOM-killed\n");
fprintf(stderr, "\n");
fprintf(stderr, " abort call abort()\n");
fprintf(stderr, " assert call assert() without a function\n");
@@ -265,6 +273,8 @@
return pthread_join(0, nullptr);
} else if (!strcasecmp(arg, "heap-usage")) {
abuse_heap();
+ } else if (!strcasecmp(arg, "leak")) {
+ leak();
} else if (!strcasecmp(arg, "SIGSEGV-unmapped")) {
char* map = reinterpret_cast<char*>(mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0));
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 4997dd6..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,14 +66,32 @@
usage(1);
}
+ if (getuid() != 0) {
+ errx(1, "root is required");
+ }
+
+ // Check to see if the process exists and that we can actually send a signal to it.
+ android::procinfo::ProcessInfo proc_info;
+ if (!android::procinfo::GetProcessInfo(pid, &proc_info)) {
+ err(1, "failed to fetch info for process %d", pid);
+ }
+
+ if (proc_info.state == android::procinfo::kProcessStateZombie) {
+ errx(1, "process %d is a zombie", pid);
+ }
+
+ if (kill(pid, 0) != 0) {
+ err(1, "cannot send signal to process %d", pid);
+ }
+
unique_fd piperead, pipewrite;
if (!Pipe(&piperead, &pipewrite)) {
err(1, "failed to create pipe");
}
std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
- if (!debuggerd_trigger_dump(pid, std::move(pipewrite),
- backtrace_only ? kDebuggerdBacktrace : kDebuggerdTombstone, 0)) {
+ if (!debuggerd_trigger_dump(pid, backtrace_only ? kDebuggerdNativeBacktrace : kDebuggerdTombstone,
+ 0, std::move(pipewrite))) {
redirect_thread.join();
errx(1, "failed to dump process %d", pid);
}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index f17724a..b51fc66 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -88,14 +88,19 @@
} \
} while (0)
-static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd) {
+#define ASSERT_BACKTRACE_FRAME(result, frame_name) \
+ ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX \
+ R"(/libc.so \()" frame_name R"(\+)")
+
+static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
+ InterceptStatus* status, DebuggerdDumpType intercept_type) {
intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
if (intercept_fd->get() == -1) {
FAIL() << "failed to contact tombstoned: " << strerror(errno);
}
- InterceptRequest req = {.pid = target_pid};
+ InterceptRequest req = {.pid = target_pid, .dump_type = intercept_type};
unique_fd output_pipe_write;
if (!Pipe(output_fd, &output_pipe_write)) {
@@ -118,6 +123,8 @@
FAIL() << "failed to set pipe size: " << strerror(errno);
}
+ ASSERT_GE(pipe_buffer_size, 1024 * 1024);
+
if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
}
@@ -133,7 +140,7 @@
<< ", received " << rc;
}
- ASSERT_EQ(InterceptStatus::kRegistered, response.status);
+ *status = response.status;
}
class CrasherTest : public ::testing::Test {
@@ -146,7 +153,7 @@
CrasherTest();
~CrasherTest();
- void StartIntercept(unique_fd* output_fd);
+ void StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type = kDebuggerdTombstone);
// Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
void FinishIntercept(int* result);
@@ -172,12 +179,14 @@
android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
}
-void CrasherTest::StartIntercept(unique_fd* output_fd) {
+void CrasherTest::StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type) {
if (crasher_pid == -1) {
FAIL() << "crasher hasn't been started";
}
- tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd);
+ InterceptStatus status;
+ tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &status, intercept_type);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
}
void CrasherTest::FinishIntercept(int* result) {
@@ -302,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 \(tgkill)");
+ ASSERT_BACKTRACE_FRAME(result, "abort");
}
TEST_F(CrasherTest, signal) {
@@ -428,7 +437,7 @@
StartProcess([]() {
abort();
});
- StartIntercept(&output_fd);
+ StartIntercept(&output_fd, kDebuggerdNativeBacktrace);
std::this_thread::sleep_for(500ms);
@@ -438,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));
@@ -449,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 \(tgkill)");
+ ASSERT_BACKTRACE_FRAME(result, "abort");
}
TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
@@ -469,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 \(tgkill)");
+ ASSERT_BACKTRACE_FRAME(result, "abort");
}
TEST_F(CrasherTest, capabilities) {
@@ -526,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) {
@@ -557,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) {
@@ -595,11 +604,13 @@
pid_t pid = 123'456'789 + i;
unique_fd intercept_fd, output_fd;
- tombstoned_intercept(pid, &intercept_fd, &output_fd);
+ InterceptStatus status;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
{
unique_fd tombstoned_socket, input_fd;
- ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+ ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone));
ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
}
@@ -627,7 +638,9 @@
pid_t pid = pid_base + dump;
unique_fd intercept_fd, output_fd;
- tombstoned_intercept(pid, &intercept_fd, &output_fd);
+ InterceptStatus status;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
// Pretend to crash, and then immediately close the socket.
unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
@@ -658,11 +671,13 @@
pid_t pid = pid_base + dump;
unique_fd intercept_fd, output_fd;
- tombstoned_intercept(pid, &intercept_fd, &output_fd);
+ InterceptStatus status;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, status);
{
unique_fd tombstoned_socket, input_fd;
- ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+ ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone));
ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
tombstoned_notify_completion(tombstoned_socket.get());
}
@@ -682,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/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index a9c9862..43104ec 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -151,7 +151,7 @@
// Fetch output fd from tombstoned.
unique_fd tombstone_socket, output_fd;
- if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd)) {
+ if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdNativeBacktrace)) {
goto exit;
}
@@ -215,7 +215,8 @@
}
unique_fd tombstone_socket, output_fd;
- bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd);
+ bool tombstoned_connected =
+ tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdTombstone);
debuggerd_fallback_tombstone(output_fd.get(), ucontext, info, abort_message);
if (tombstoned_connected) {
tombstoned_notify_completion(tombstone_socket.get());
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 8fd6e11..55cd03e 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -50,6 +50,8 @@
#include <async_safe/log.h>
+#include "dump_type.h"
+
// see man(2) prctl, specifically the section about PR_GET_NAME
#define MAX_TASK_NAME_LEN (16)
@@ -253,6 +255,14 @@
// process.
static void* pseudothread_stack;
+static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) {
+ if (thread_info->signal_number == DEBUGGER_SIGNAL && thread_info->info->si_value.sival_int) {
+ return kDebuggerdNativeBacktrace;
+ }
+
+ return kDebuggerdTombstone;
+}
+
static int debuggerd_dispatch_pseudothread(void* arg) {
debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
@@ -285,11 +295,15 @@
char main_tid[10];
char pseudothread_tid[10];
+ char debuggerd_dump_type[10];
async_safe_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid);
async_safe_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d",
thread_info->pseudothread_tid);
+ async_safe_format_buffer(debuggerd_dump_type, sizeof(debuggerd_dump_type), "%d",
+ get_dump_type(thread_info));
- execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, nullptr);
+ execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
+ nullptr);
fatal_errno("exec failed");
} else {
diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h
index 01de57b..b7284b0 100644
--- a/debuggerd/include/debuggerd/client.h
+++ b/debuggerd/include/debuggerd/client.h
@@ -22,15 +22,13 @@
#include <android-base/unique_fd.h>
-enum DebuggerdDumpType {
- kDebuggerdBacktrace,
- kDebuggerdTombstone,
-};
+#include "dump_type.h"
// Trigger a dump of specified process to output_fd.
// output_fd is consumed, timeout of 0 will wait forever.
-bool debuggerd_trigger_dump(pid_t pid, android::base::unique_fd output_fd,
- enum DebuggerdDumpType dump_type, unsigned int timeout_ms);
+bool debuggerd_trigger_dump(pid_t pid, enum DebuggerdDumpType dump_type, unsigned int timeout_ms,
+ android::base::unique_fd output_fd);
-int dump_backtrace_to_file(pid_t tid, int fd);
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
+int dump_backtrace_to_file(pid_t tid, enum DebuggerdDumpType dump_type, int output_fd);
+int dump_backtrace_to_file_timeout(pid_t tid, enum DebuggerdDumpType dump_type, int timeout_secs,
+ int output_fd);
diff --git a/debuggerd/libdebuggerd/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/protocol.h b/debuggerd/protocol.h
index 144efc8..7e1961e 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -18,6 +18,8 @@
#include <stdint.h>
+#include "dump_type.h"
+
// Sockets in the ANDROID_SOCKET_NAMESPACE_RESERVED namespace.
// Both sockets are SOCK_SEQPACKET sockets, so no explicit length field is needed.
constexpr char kTombstonedCrashSocketName[] = "tombstoned_crash";
@@ -40,6 +42,7 @@
};
struct DumpRequest {
+ DebuggerdDumpType dump_type;
int32_t pid;
};
@@ -54,10 +57,15 @@
// Comes with a file descriptor via SCM_RIGHTS.
// This packet should be sent before an actual dump happens.
struct InterceptRequest {
+ DebuggerdDumpType dump_type;
int32_t pid;
};
enum class InterceptStatus : uint8_t {
+ // Returned when an intercept of a different type has already been
+ // registered (and is active) for a given PID.
+ kFailedAlreadyRegistered,
+ // Returned in all other failure cases.
kFailed,
kStarted,
kRegistered,
diff --git a/debuggerd/tombstoned/include/tombstoned/tombstoned.h b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
index 908517d..6403dbe 100644
--- a/debuggerd/tombstoned/include/tombstoned/tombstoned.h
+++ b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
@@ -20,7 +20,9 @@
#include <android-base/unique_fd.h>
+#include "dump_type.h"
+
bool tombstoned_connect(pid_t pid, android::base::unique_fd* tombstoned_socket,
- android::base::unique_fd* output_fd, bool is_native_crash = true);
+ android::base::unique_fd* output_fd, DebuggerdDumpType dump_type);
bool tombstoned_notify_completion(int tombstoned_socket);
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 4d4eb9e..24960bc 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -61,11 +61,24 @@
reason = "due to input";
}
- LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " terminated " << reason;
+ LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " and type "
+ << intercept->dump_type << " terminated: " << reason;
intercept_manager->intercepts.erase(it);
}
}
+static bool is_intercept_request_valid(const InterceptRequest& request) {
+ if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) {
+ return false;
+ }
+
+ if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) {
+ return false;
+ }
+
+ return true;
+}
+
static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
auto intercept = reinterpret_cast<Intercept*>(arg);
InterceptManager* intercept_manager = intercept->intercept_manager;
@@ -103,23 +116,24 @@
rcv_fd.reset(moved_fd);
// We trust the other side, so only do minimal validity checking.
- if (intercept_request.pid <= 0 || intercept_request.pid > std::numeric_limits<pid_t>::max()) {
+ if (!is_intercept_request_valid(intercept_request)) {
InterceptResponse response = {};
response.status = InterceptStatus::kFailed;
- snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
- intercept_request.pid);
+ snprintf(response.error_message, sizeof(response.error_message), "invalid intercept request");
TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
goto fail;
}
intercept->intercept_pid = intercept_request.pid;
+ intercept->dump_type = intercept_request.dump_type;
// Check if it's already registered.
if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
InterceptResponse response = {};
- response.status = InterceptStatus::kFailed;
+ response.status = InterceptStatus::kFailedAlreadyRegistered;
snprintf(response.error_message, sizeof(response.error_message),
- "pid %" PRId32 " already intercepted", intercept_request.pid);
+ "pid %" PRId32 " already intercepted, type %d", intercept_request.pid,
+ intercept_request.dump_type);
TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
LOG(WARNING) << response.error_message;
goto fail;
@@ -138,7 +152,8 @@
intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
intercept->registered = true;
- LOG(INFO) << "tombstoned registered intercept for pid " << intercept_request.pid;
+ LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type "
+ << intercept_request.dump_type;
// Register a different read event on the socket so that we can remove intercepts if the socket
// closes (e.g. if a user CTRL-C's the process that requested the intercept).
@@ -174,16 +189,27 @@
intercept_socket);
}
-bool InterceptManager::GetIntercept(pid_t pid, android::base::unique_fd* out_fd) {
+bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
+ android::base::unique_fd* out_fd) {
auto it = this->intercepts.find(pid);
if (it == this->intercepts.end()) {
return false;
}
+ if (dump_type == kDebuggerdAnyIntercept) {
+ LOG(INFO) << "found registered intercept of type " << it->second->dump_type
+ << " for requested type kDebuggerdAnyIntercept";
+ } else if (it->second->dump_type != dump_type) {
+ LOG(WARNING) << "found non-matching intercept of type " << it->second->dump_type
+ << " for requested type: " << dump_type;
+ return false;
+ }
+
auto intercept = std::move(it->second);
this->intercepts.erase(it);
- LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
+ LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid
+ << " and type " << intercept->dump_type;
InterceptResponse response = {};
response.status = InterceptStatus::kStarted;
TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
diff --git a/debuggerd/tombstoned/intercept_manager.h b/debuggerd/tombstoned/intercept_manager.h
index cb5db62..a11d565 100644
--- a/debuggerd/tombstoned/intercept_manager.h
+++ b/debuggerd/tombstoned/intercept_manager.h
@@ -25,6 +25,8 @@
#include <android-base/unique_fd.h>
+#include "dump_type.h"
+
struct InterceptManager;
struct Intercept {
@@ -39,6 +41,7 @@
pid_t intercept_pid = -1;
android::base::unique_fd output_fd;
bool registered = false;
+ DebuggerdDumpType dump_type = kDebuggerdNativeBacktrace;
};
struct InterceptManager {
@@ -50,5 +53,5 @@
InterceptManager(InterceptManager& copy) = delete;
InterceptManager(InterceptManager&& move) = delete;
- bool GetIntercept(pid_t pid, android::base::unique_fd* out_fd);
+ bool GetIntercept(pid_t pid, DebuggerdDumpType dump_type, android::base::unique_fd* out_fd);
};
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 05df9f2..93c7fb5 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -23,23 +23,28 @@
#include <array>
#include <deque>
+#include <string>
#include <unordered_map>
+#include <utility>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/thread.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
#include "debuggerd/handler.h"
+#include "dump_type.h"
#include "protocol.h"
#include "util.h"
#include "intercept_manager.h"
+using android::base::GetIntProperty;
using android::base::StringPrintf;
using android::base::unique_fd;
@@ -50,12 +55,24 @@
kCrashStatusQueued,
};
-struct Crash;
+// Ownership of Crash is a bit messy.
+// It's either owned by an active event that must have a timeout, or owned by
+// queued_requests, in the case that multiple crashes come in at the same time.
+struct Crash {
+ ~Crash() { event_free(crash_event); }
-class CrashType {
+ unique_fd crash_fd;
+ pid_t crash_pid;
+ event* crash_event = nullptr;
+ std::string crash_path;
+
+ DebuggerdDumpType crash_type;
+};
+
+class CrashQueue {
public:
- CrashType(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,
- size_t max_concurrent_dumps)
+ CrashQueue(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,
+ size_t max_concurrent_dumps)
: file_name_prefix_(file_name_prefix),
dir_path_(dir_path),
dir_fd_(open(dir_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)),
@@ -74,23 +91,42 @@
find_oldest_artifact();
}
- unique_fd get_output_fd() {
+ static CrashQueue* for_crash(const Crash* crash) {
+ return (crash->crash_type == kDebuggerdJavaBacktrace) ? for_anrs() : for_tombstones();
+ }
+
+ static CrashQueue* for_tombstones() {
+ static CrashQueue queue("/data/tombstones", "tombstone_" /* file_name_prefix */,
+ GetIntProperty("tombstoned.max_tombstone_count", 10),
+ 1 /* max_concurrent_dumps */);
+ return &queue;
+ }
+
+ static CrashQueue* for_anrs() {
+ static CrashQueue queue("/data/anr", "trace_" /* file_name_prefix */,
+ GetIntProperty("tombstoned.max_anr_count", 64),
+ 4 /* max_concurrent_dumps */);
+ return &queue;
+ }
+
+ std::pair<unique_fd, std::string> get_output() {
unique_fd result;
- char buf[PATH_MAX];
- snprintf(buf, sizeof(buf), "%s%02d", file_name_prefix_.c_str(), next_artifact_);
+ std::string file_name = StringPrintf("%s%02d", file_name_prefix_.c_str(), next_artifact_);
+
// Unlink and create the file, instead of using O_TRUNC, to avoid two processes
// interleaving their output in case we ever get into that situation.
- if (unlinkat(dir_fd_, buf, 0) != 0 && errno != ENOENT) {
- PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << buf;
+ if (unlinkat(dir_fd_, file_name.c_str(), 0) != 0 && errno != ENOENT) {
+ PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << "/" << file_name;
}
- result.reset(openat(dir_fd_, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
+ result.reset(openat(dir_fd_, file_name.c_str(),
+ O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
if (result == -1) {
- PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << buf;
+ PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << "/" << file_name;
}
next_artifact_ = (next_artifact_ + 1) % max_artifacts_;
- return result;
+ return {std::move(result), dir_path_ + "/" + file_name};
}
bool maybe_enqueue_crash(Crash* crash) {
@@ -114,17 +150,13 @@
void on_crash_completed() { --num_concurrent_dumps_; }
- static CrashType* const tombstone;
- static CrashType* const java_trace;
-
private:
void find_oldest_artifact() {
size_t oldest_tombstone = 0;
time_t oldest_time = std::numeric_limits<time_t>::max();
for (size_t i = 0; i < max_artifacts_; ++i) {
- std::string path = android::base::StringPrintf("%s/%s%02zu", dir_path_.c_str(),
- file_name_prefix_.c_str(), i);
+ std::string path = StringPrintf("%s/%s%02zu", dir_path_.c_str(), file_name_prefix_.c_str(), i);
struct stat st;
if (stat(path.c_str(), &st) != 0) {
if (errno == ENOENT) {
@@ -158,34 +190,11 @@
std::deque<Crash*> queued_requests_;
- DISALLOW_COPY_AND_ASSIGN(CrashType);
+ DISALLOW_COPY_AND_ASSIGN(CrashQueue);
};
// Whether java trace dumps are produced via tombstoned.
-static constexpr bool kJavaTraceDumpsEnabled = false;
-
-/* static */ CrashType* const CrashType::tombstone =
- new CrashType("/data/tombstones", "tombstone_" /* file_name_prefix */, 10 /* max_artifacts */,
- 1 /* max_concurrent_dumps */);
-
-/* static */ CrashType* const CrashType::java_trace =
- (kJavaTraceDumpsEnabled ? new CrashType("/data/anr", "anr_" /* file_name_prefix */,
- 64 /* max_artifacts */, 4 /* max_concurrent_dumps */)
- : nullptr);
-
-// Ownership of Crash is a bit messy.
-// It's either owned by an active event that must have a timeout, or owned by
-// queued_requests, in the case that multiple crashes come in at the same time.
-struct Crash {
- ~Crash() { event_free(crash_event); }
-
- unique_fd crash_fd;
- pid_t crash_pid;
- event* crash_event = nullptr;
-
- // Not owned by |Crash|.
- CrashType* crash_type = nullptr;
-};
+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*);
@@ -194,10 +203,8 @@
static void perform_request(Crash* crash) {
unique_fd output_fd;
- // Note that java traces are not interceptible.
- if ((crash->crash_type == CrashType::java_trace) ||
- !intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
- output_fd = crash->crash_type->get_output_fd();
+ if (!intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd)) {
+ std::tie(output_fd, crash->crash_path) = CrashQueue::for_crash(crash)->get_output();
}
TombstonedCrashPacket response = {
@@ -220,7 +227,7 @@
event_add(crash->crash_event, &timeout);
}
- crash->crash_type->on_crash_started();
+ CrashQueue::for_crash(crash)->on_crash_started();
return;
fail:
@@ -228,22 +235,22 @@
}
static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
- void* crash_type) {
+ void*) {
event_base* base = evconnlistener_get_base(listener);
Crash* crash = new Crash();
+ // TODO: Make sure that only java crashes come in on the java socket
+ // and only native crashes on the native socket.
struct timeval timeout = { 1, 0 };
event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
crash->crash_fd.reset(sockfd);
crash->crash_event = crash_event;
- crash->crash_type = static_cast<CrashType*>(crash_type);
event_add(crash_event, &timeout);
}
static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
ssize_t rc;
Crash* crash = static_cast<Crash*>(arg);
- CrashType* type = crash->crash_type;
TombstonedCrashPacket request = {};
@@ -271,7 +278,13 @@
goto fail;
}
- if (type == CrashType::tombstone) {
+ crash->crash_type = request.packet.dump_request.dump_type;
+ if (crash->crash_type < 0 || crash->crash_type > kDebuggerdAnyIntercept) {
+ LOG(WARNING) << "unexpected crash dump type: " << crash->crash_type;
+ goto fail;
+ }
+
+ if (crash->crash_type != kDebuggerdJavaBacktrace) {
crash->crash_pid = request.packet.dump_request.pid;
} else {
// Requests for java traces are sent from untrusted processes, so we
@@ -290,7 +303,7 @@
LOG(INFO) << "received crash request for pid " << crash->crash_pid;
- if (type->maybe_enqueue_crash(crash)) {
+ if (CrashQueue::for_crash(crash)->maybe_enqueue_crash(crash)) {
LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
} else {
perform_request(crash);
@@ -307,7 +320,7 @@
Crash* crash = static_cast<Crash*>(arg);
TombstonedCrashPacket request = {};
- crash->crash_type->on_crash_completed();
+ CrashQueue::for_crash(crash)->on_crash_completed();
if ((ev & EV_READ) == 0) {
goto fail;
@@ -329,12 +342,23 @@
goto fail;
}
+ if (!crash->crash_path.empty()) {
+ 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:
- CrashType* type = crash->crash_type;
+ CrashQueue* queue = CrashQueue::for_crash(crash);
delete crash;
// If there's something queued up, let them proceed.
- type->maybe_dequeue_crashes(perform_request);
+ queue->maybe_dequeue_crashes(perform_request);
}
int main(int, char* []) {
@@ -366,7 +390,7 @@
intercept_manager = new InterceptManager(base, intercept_socket);
evconnlistener* tombstone_listener = evconnlistener_new(
- base, crash_accept_cb, CrashType::tombstone, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
+ base, crash_accept_cb, CrashQueue::for_tombstones(), -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
if (!tombstone_listener) {
LOG(FATAL) << "failed to create evconnlistener for tombstones.";
}
@@ -379,7 +403,7 @@
evutil_make_socket_nonblocking(java_trace_socket);
evconnlistener* java_trace_listener = evconnlistener_new(
- base, crash_accept_cb, CrashType::java_trace, -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket);
+ base, crash_accept_cb, CrashQueue::for_anrs(), -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket);
if (!java_trace_listener) {
LOG(FATAL) << "failed to create evconnlistener for java traces.";
}
diff --git a/debuggerd/tombstoned/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
index 39dc6eb..bdb4c1a 100644
--- a/debuggerd/tombstoned/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -31,10 +31,11 @@
using android::base::unique_fd;
bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
- bool is_native_crash) {
- unique_fd sockfd(socket_local_client(
- (is_native_crash ? kTombstonedCrashSocketName : kTombstonedJavaTraceSocketName),
- ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+ DebuggerdDumpType dump_type) {
+ unique_fd sockfd(
+ socket_local_client((dump_type != kDebuggerdJavaBacktrace ? kTombstonedCrashSocketName
+ : kTombstonedJavaTraceSocketName),
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
if (sockfd == -1) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s",
strerror(errno));
@@ -44,6 +45,7 @@
TombstonedCrashPacket packet = {};
packet.packet_type = CrashPacketType::kDumpRequest;
packet.packet.dump_request.pid = pid;
+ packet.packet.dump_request.dump_type = dump_type;
if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write DumpRequest packet: %s",
strerror(errno));
diff --git a/demangle/Android.bp b/demangle/Android.bp
index 96ab57d..e55c886 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -29,6 +29,7 @@
cc_library {
name: "libdemangle",
defaults: ["libdemangle_defaults"],
+ vendor_available: true,
srcs: [
"Demangler.cpp",
diff --git a/demangle/Android.mk b/demangle/Android.mk
new file mode 100644
index 0000000..e3cfc2a
--- /dev/null
+++ b/demangle/Android.mk
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := demangle_fuzzer
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := \
+ Demangler.cpp \
+ demangle_fuzzer.cpp \
+
+LOCAL_CFLAGS := \
+ -Wall \
+ -Werror \
+ -Wextra \
+
+include $(BUILD_FUZZ_TEST)
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
index fb68119..f56a9be 100644
--- a/demangle/DemangleTest.cpp
+++ b/demangle/DemangleTest.cpp
@@ -22,7 +22,14 @@
#include "Demangler.h"
-TEST(DemangleTest, VoidArgumentTest) {
+TEST(DemangleTest, IllegalArgumentModifiers) {
+ Demangler demangler;
+
+ ASSERT_EQ("_Zpp4FUNKK", demangler.Parse("_Zpp4FUNKK"));
+ ASSERT_EQ("_Zpp4FUNVV", demangler.Parse("_Zpp4FUNVV"));
+}
+
+TEST(DemangleTest, VoidArgument) {
Demangler demangler;
ASSERT_EQ("func()", demangler.Parse("_ZN4funcEv"));
@@ -189,6 +196,14 @@
ASSERT_EQ("value(one, signed char)", demangler.Parse("_Z5value3onea"));
}
+TEST(DemangleTest, FunctionStartsWithLPlusNumber) {
+ Demangler demangler;
+
+ ASSERT_EQ("value(char, int)", demangler.Parse("_ZL5valueci"));
+ ASSERT_EQ("abcdefjklmn(signed char)", demangler.Parse("_ZL11abcdefjklmna"));
+ ASSERT_EQ("value(one, signed char)", demangler.Parse("_ZL5value3onea"));
+}
+
TEST(DemangleTest, StdTypes) {
Demangler demangler;
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index 77cfd3b..c0a96aa 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -542,9 +542,8 @@
} else {
suffix = " volatile";
}
- if (name[-1] == 'K' || name[-1] == 'V') {
+ if (!cur_state_.suffixes.empty() && (name[-1] == 'K' || name[-1] == 'V')) {
// Special case, const/volatile apply as a single entity.
- assert(!cur_state_.suffixes.empty());
size_t index = cur_state_.suffixes.size();
cur_state_.suffixes[index-1].insert(0, suffix);
} else {
@@ -699,6 +698,8 @@
if (std::isdigit(*name)) {
name = GetStringFromLength(name, &function_name_);
+ } else if (*name == 'L' && std::isdigit(name[1])) {
+ name = GetStringFromLength(name + 1, &function_name_);
} else {
name = AppendOperatorString(name);
function_name_ = cur_state_.str;
@@ -723,7 +724,8 @@
&& static_cast<size_t>(cur_name - name) < max_length) {
cur_name = (this->*parse_func_)(cur_name);
}
- if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty()) {
+ if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty() ||
+ !cur_state_.suffixes.empty()) {
return name;
}
diff --git a/demangle/demangle_fuzzer.cpp b/demangle/demangle_fuzzer.cpp
new file mode 100644
index 0000000..83fafc2
--- /dev/null
+++ b/demangle/demangle_fuzzer.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include "Demangler.h"
+
+extern "C" void LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::vector<char> data_str(size + 1);
+ memcpy(data_str.data(), data, size);
+ data_str[size] = '\0';
+
+ Demangler demangler;
+ std::string demangled_name = demangler.Parse(data_str.data());
+ if (size != 0 && data_str[0] != '\0' && demangled_name.empty()) {
+ abort();
+ }
+}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3e890c7..271ca95 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -53,6 +53,7 @@
#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <sparse/sparse.h>
#include <ziparchive/zip_archive.h>
@@ -1350,7 +1351,8 @@
struct fastboot_buffer buf;
const char* errMsg = nullptr;
const struct fs_generator* gen = nullptr;
- int fd;
+ TemporaryFile output;
+ unique_fd fd;
unsigned int limit = INT_MAX;
if (target_sparse_limit > 0 && target_sparse_limit < limit) {
@@ -1403,22 +1405,23 @@
return;
}
- fd = make_temporary_fd();
- if (fd == -1) return;
-
unsigned eraseBlkSize, logicalBlkSize;
eraseBlkSize = fb_get_flash_block_size(transport, "erase-block-size");
logicalBlkSize = fb_get_flash_block_size(transport, "logical-block-size");
- if (fs_generator_generate(gen, fd, size, initial_dir, eraseBlkSize, logicalBlkSize)) {
+ if (fs_generator_generate(gen, output.path, size, initial_dir,
+ eraseBlkSize, logicalBlkSize)) {
fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
- close(fd);
return;
}
- if (!load_buf_fd(transport, fd, &buf)) {
+ fd.reset(open(output.path, O_RDONLY));
+ if (fd == -1) {
+ fprintf(stderr, "Cannot open generated image: %s\n", strerror(errno));
+ return;
+ }
+ if (!load_buf_fd(transport, fd.release(), &buf)) {
fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
- close(fd);
return;
}
flash_buf(partition, &buf);
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 5d9ccfe..99ca7dd 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -4,6 +4,7 @@
#include "make_f2fs.h"
#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -11,12 +12,20 @@
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/unique_fd.h>
#include <ext4_utils/make_ext4fs.h>
#include <sparse/sparse.h>
-static int generate_ext4_image(int fd, long long partSize, const std::string& initial_dir,
+using android::base::unique_fd;
+
+static int generate_ext4_image(const char* fileName, long long partSize, const std::string& initial_dir,
unsigned eraseBlkSize, unsigned logicalBlkSize)
{
+ unique_fd fd(open(fileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
+ if (fd == -1) {
+ fprintf(stderr, "Unable to open output file for EXT4 filesystem: %s\n", strerror(errno));
+ return -1;
+ }
if (initial_dir.empty()) {
make_ext4fs_sparse_fd_align(fd, partSize, NULL, NULL, eraseBlkSize, logicalBlkSize);
} else {
@@ -27,11 +36,16 @@
}
#ifdef USE_F2FS
-static int generate_f2fs_image(int fd, long long partSize, const std::string& initial_dir,
+static int generate_f2fs_image(const char* fileName, long long partSize, const std::string& initial_dir,
unsigned /* unused */, unsigned /* unused */)
{
if (!initial_dir.empty()) {
- fprintf(stderr, "Unable to set initial directory on F2FS filesystem\n");
+ fprintf(stderr, "Unable to set initial directory on F2FS filesystem: %s\n", strerror(errno));
+ return -1;
+ }
+ unique_fd fd(open(fileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
+ if (fd == -1) {
+ fprintf(stderr, "Unable to open output file for F2FS filesystem: %s\n", strerror(errno));
return -1;
}
return make_f2fs_sparse_fd(fd, partSize, NULL, NULL);
@@ -42,7 +56,7 @@
const char* fs_type; //must match what fastboot reports for partition type
//returns 0 or error value
- int (*generate)(int fd, long long partSize, const std::string& initial_dir,
+ int (*generate)(const char* fileName, long long partSize, const std::string& initial_dir,
unsigned eraseBlkSize, unsigned logicalBlkSize);
} generators[] = {
@@ -61,8 +75,8 @@
return nullptr;
}
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
+int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
const std::string& initial_dir, unsigned eraseBlkSize, unsigned logicalBlkSize)
{
- return gen->generate(tmpFileNo, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
+ return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
}
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 0a5f5a4..c6baa7f 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -7,7 +7,7 @@
struct fs_generator;
const struct fs_generator* fs_get_generator(const std::string& fs_type);
-int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
+int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
const std::string& initial_dir, unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0);
#endif
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 0af6159..608917a 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: [
+ "liblogwrap",
+ "libfec",
+ "libfec_rs",
+ "libbase",
+ "libcrypto_utils",
+ "libcrypto",
+ "libext4_utils",
+ "libsquashfs_utils",
+ "libselinux",
+ "libavb",
+ "libfstab",
+ ],
+ export_static_lib_headers: [
+ "libfstab",
+ ],
+ whole_static_libs: [
+ "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 73bdc7a..ff9b84f 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -32,6 +32,7 @@
#include <unistd.h>
#include <memory>
+#include <thread>
#include <android-base/file.h>
#include <android-base/properties.h>
@@ -78,43 +79,33 @@
FS_STAT_E2FSCK_F_ALWAYS = 0x0004,
FS_STAT_UNCLEAN_SHUTDOWN = 0x0008,
FS_STAT_QUOTA_ENABLED = 0x0010,
- FS_STAT_TUNE2FS_FAILED = 0x0020,
FS_STAT_RO_MOUNT_FAILED = 0x0040,
FS_STAT_RO_UNMOUNT_FAILED = 0x0080,
FS_STAT_FULL_MOUNT_FAILED = 0x0100,
FS_STAT_E2FSCK_FAILED = 0x0200,
FS_STAT_E2FSCK_FS_FIXED = 0x0400,
FS_STAT_EXT4_INVALID_MAGIC = 0x0800,
+ FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
+ FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
+ FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,
};
-/*
- * gettime() - returns the time in seconds of the system's monotonic clock or
- * zero on error.
- */
-static time_t gettime(void)
-{
- struct timespec ts;
- int ret;
+// TODO: switch to inotify()
+bool fs_mgr_wait_for_file(const std::string& filename,
+ const std::chrono::milliseconds relative_timeout) {
+ auto start_time = std::chrono::steady_clock::now();
- ret = clock_gettime(CLOCK_MONOTONIC, &ts);
- if (ret < 0) {
- PERROR << "clock_gettime(CLOCK_MONOTONIC) failed";
- return 0;
+ while (true) {
+ if (!access(filename.c_str(), F_OK) || errno != ENOENT) {
+ return true;
+ }
+
+ std::this_thread::sleep_for(50ms);
+
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed > relative_timeout) return false;
}
-
- return ts.tv_sec;
-}
-
-static int wait_for_file(const char *filename, int timeout)
-{
- struct stat info;
- time_t timeout_time = gettime() + timeout;
- int ret = -1;
-
- while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
- usleep(10000);
-
- return ret;
}
static void log_fs_stat(const char* blk_device, int fs_stat)
@@ -128,10 +119,16 @@
}
}
+static bool is_extfs(const std::string& fs_type) {
+ return fs_type == "ext4" || fs_type == "ext3" || fs_type == "ext2";
+}
+
static bool should_force_check(int fs_stat) {
- return fs_stat & (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
- FS_STAT_TUNE2FS_FAILED | FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED |
- FS_STAT_FULL_MOUNT_FAILED | FS_STAT_E2FSCK_FAILED);
+ return fs_stat &
+ (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
+ FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED | FS_STAT_FULL_MOUNT_FAILED |
+ FS_STAT_E2FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |
+ FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED);
}
static void check_fs(const char *blk_device, char *fs_type, char *target, int *fs_stat)
@@ -144,7 +141,7 @@
const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device};
/* Check for the types of filesystems we know how to check */
- if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
+ if (is_extfs(fs_type)) {
if (*fs_stat & FS_STAT_EXT4_INVALID_MAGIC) { // will fail, so do not try
return;
}
@@ -242,186 +239,208 @@
return;
}
-/* Function to read the primary superblock */
-static int read_super_block(int fd, struct ext4_super_block *sb)
-{
- off64_t ret;
-
- ret = lseek64(fd, 1024, SEEK_SET);
- if (ret < 0)
- return ret;
-
- ret = read(fd, sb, sizeof(*sb));
- if (ret < 0)
- return ret;
- if (ret != sizeof(*sb))
- return ret;
-
- return 0;
-}
-
-static ext4_fsblk_t ext4_blocks_count(struct ext4_super_block *es)
-{
+static ext4_fsblk_t ext4_blocks_count(const struct ext4_super_block* es) {
return ((ext4_fsblk_t)le32_to_cpu(es->s_blocks_count_hi) << 32) |
- le32_to_cpu(es->s_blocks_count_lo);
+ le32_to_cpu(es->s_blocks_count_lo);
}
-static ext4_fsblk_t ext4_r_blocks_count(struct ext4_super_block *es)
-{
+static ext4_fsblk_t ext4_r_blocks_count(const struct ext4_super_block* es) {
return ((ext4_fsblk_t)le32_to_cpu(es->s_r_blocks_count_hi) << 32) |
- le32_to_cpu(es->s_r_blocks_count_lo);
+ le32_to_cpu(es->s_r_blocks_count_lo);
}
-static int do_quota_with_shutdown_check(char *blk_device, char *fs_type,
- struct fstab_rec *rec, int *fs_stat)
-{
- int force_check = 0;
- if (!strcmp(fs_type, "ext4")) {
- /*
- * Some system images do not have tune2fs for licensing reasons
- * Detect these and skip reserve blocks.
- */
- if (access(TUNE2FS_BIN, X_OK)) {
- LERROR << "Not running " << TUNE2FS_BIN << " on "
- << blk_device << " (executable not in system image)";
- } else {
- const char* arg1 = nullptr;
- const char* arg2 = nullptr;
- int status = 0;
- int ret = 0;
- android::base::unique_fd fd(
- TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
- if (fd >= 0) {
- struct ext4_super_block sb;
- ret = read_super_block(fd, &sb);
- if (ret < 0) {
- PERROR << "Can't read '" << blk_device << "' super block";
- return force_check;
- }
- if (sb.s_magic != EXT4_SUPER_MAGIC) {
- LINFO << "Invalid ext4 magic:0x" << std::hex << sb.s_magic << "," << blk_device;
- *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
- return 0; // not a valid fs, tune2fs, fsck, and mount will all fail.
- }
- *fs_stat |= FS_STAT_IS_EXT4;
- LINFO << "superblock s_max_mnt_count:" << sb.s_max_mnt_count << "," << blk_device;
- if (sb.s_max_mnt_count == 0xffff) { // -1 (int16) in ext2, but uint16 in ext4
- *fs_stat |= FS_STAT_NEW_IMAGE_VERSION;
- }
- if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
- (sb.s_state & EXT4_VALID_FS) == 0) {
- LINFO << __FUNCTION__ << "(): was not clealy shutdown, state flag:"
- << std::hex << sb.s_state
- << "incompat flag:" << std::hex << sb.s_feature_incompat;
- force_check = 1;
- *fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN;
- }
- int has_quota = (sb.s_feature_ro_compat
- & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
- int want_quota = fs_mgr_is_quota(rec) != 0;
+// Read the primary superblock from an ext4 filesystem. On failure return
+// false. If it's not an ext4 filesystem, also set FS_STAT_EXT4_INVALID_MAGIC.
+static bool read_ext4_superblock(const char* blk_device, struct ext4_super_block* sb, int* fs_stat) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
- if (has_quota == want_quota) {
- LINFO << "Requested quota status is match on " << blk_device;
- return force_check;
- } else if (want_quota) {
- LINFO << "Enabling quota on " << blk_device;
- arg1 = "-Oquota";
- arg2 = "-Qusrquota,grpquota";
- force_check = 1;
- *fs_stat |= FS_STAT_QUOTA_ENABLED;
- } else {
- LINFO << "Disabling quota on " << blk_device;
- arg1 = "-Q^usrquota,^grpquota";
- arg2 = "-O^quota";
- }
- } else {
- PERROR << "Failed to open '" << blk_device << "'";
- return force_check;
- }
-
- const char *tune2fs_argv[] = {
- TUNE2FS_BIN,
- arg1,
- arg2,
- blk_device,
- };
- ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
- const_cast<char **>(tune2fs_argv),
- &status, true, LOG_KLOG | LOG_FILE,
- true, NULL, NULL, 0);
- if (ret < 0) {
- /* No need to check for error in fork, we can't really handle it now */
- LERROR << "Failed trying to run " << TUNE2FS_BIN;
- *fs_stat |= FS_STAT_TUNE2FS_FAILED;
- }
- }
+ if (fd < 0) {
+ PERROR << "Failed to open '" << blk_device << "'";
+ return false;
}
- return force_check;
+
+ if (pread(fd, sb, sizeof(*sb), 1024) != sizeof(*sb)) {
+ PERROR << "Can't read '" << blk_device << "' superblock";
+ return false;
+ }
+
+ if (sb->s_magic != EXT4_SUPER_MAGIC) {
+ LINFO << "Invalid ext4 magic:0x" << std::hex << sb->s_magic << " "
+ << "on '" << blk_device << "'";
+ // not a valid fs, tune2fs, fsck, and mount will all fail.
+ *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
+ return false;
+ }
+ *fs_stat |= FS_STAT_IS_EXT4;
+ LINFO << "superblock s_max_mnt_count:" << sb->s_max_mnt_count << "," << blk_device;
+ if (sb->s_max_mnt_count == 0xffff) { // -1 (int16) in ext2, but uint16 in ext4
+ *fs_stat |= FS_STAT_NEW_IMAGE_VERSION;
+ }
+ return true;
}
-static void do_reserved_size(char *blk_device, char *fs_type, struct fstab_rec *rec, int *fs_stat)
-{
- /* Check for the types of filesystems we know how to check */
- if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
- /*
- * Some system images do not have tune2fs for licensing reasons
- * Detect these and skip reserve blocks.
- */
- if (access(TUNE2FS_BIN, X_OK)) {
- LERROR << "Not running " << TUNE2FS_BIN << " on "
- << blk_device << " (executable not in system image)";
+// Some system images do not have tune2fs for licensing reasons.
+// Detect these and skip running it.
+static bool tune2fs_available(void) {
+ return access(TUNE2FS_BIN, X_OK) == 0;
+}
+
+static bool run_tune2fs(const char* argv[], int argc) {
+ int ret;
+
+ ret = android_fork_execvp_ext(argc, const_cast<char**>(argv), nullptr, true,
+ LOG_KLOG | LOG_FILE, true, nullptr, nullptr, 0);
+ return ret == 0;
+}
+
+// Enable/disable quota support on the filesystem if needed.
+static void tune_quota(const char* blk_device, const struct fstab_rec* rec,
+ const struct ext4_super_block* sb, int* fs_stat) {
+ bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
+ bool want_quota = fs_mgr_is_quota(rec) != 0;
+
+ if (has_quota == want_quota) {
+ return;
+ }
+
+ if (!tune2fs_available()) {
+ LERROR << "Unable to " << (want_quota ? "enable" : "disable") << " quotas on " << blk_device
+ << " because " TUNE2FS_BIN " is missing";
+ return;
+ }
+
+ const char* argv[] = {TUNE2FS_BIN, nullptr, nullptr, blk_device};
+
+ if (want_quota) {
+ LINFO << "Enabling quotas on " << blk_device;
+ argv[1] = "-Oquota";
+ argv[2] = "-Qusrquota,grpquota";
+ *fs_stat |= FS_STAT_QUOTA_ENABLED;
+ } else {
+ LINFO << "Disabling quotas on " << blk_device;
+ argv[1] = "-O^quota";
+ argv[2] = "-Q^usrquota,^grpquota";
+ }
+
+ if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+ LERROR << "Failed to run " TUNE2FS_BIN " to " << (want_quota ? "enable" : "disable")
+ << " quotas on " << blk_device;
+ *fs_stat |= FS_STAT_TOGGLE_QUOTAS_FAILED;
+ }
+}
+
+// Set the number of reserved filesystem blocks if needed.
+static void tune_reserved_size(const char* blk_device, const struct fstab_rec* rec,
+ const struct ext4_super_block* sb, int* fs_stat) {
+ if (!(rec->fs_mgr_flags & MF_RESERVEDSIZE)) {
+ return;
+ }
+
+ // The size to reserve is given in the fstab, but we won't reserve more
+ // than 2% of the filesystem.
+ const uint64_t max_reserved_blocks = ext4_blocks_count(sb) * 0.02;
+ uint64_t reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(sb);
+
+ if (reserved_blocks > max_reserved_blocks) {
+ LWARNING << "Reserved blocks " << reserved_blocks << " is too large; "
+ << "capping to " << max_reserved_blocks;
+ reserved_blocks = max_reserved_blocks;
+ }
+
+ if (ext4_r_blocks_count(sb) == reserved_blocks) {
+ return;
+ }
+
+ if (!tune2fs_available()) {
+ LERROR << "Unable to set the number of reserved blocks on " << blk_device
+ << " because " TUNE2FS_BIN " is missing";
+ return;
+ }
+
+ char buf[32];
+ const char* argv[] = {TUNE2FS_BIN, "-r", buf, blk_device};
+
+ snprintf(buf, sizeof(buf), "%" PRIu64, reserved_blocks);
+ LINFO << "Setting reserved block count on " << blk_device << " to " << reserved_blocks;
+ if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+ LERROR << "Failed to run " TUNE2FS_BIN " to set the number of reserved blocks on "
+ << blk_device;
+ *fs_stat |= FS_STAT_SET_RESERVED_BLOCKS_FAILED;
+ }
+}
+
+// Enable file-based encryption if needed.
+static void tune_encrypt(const char* blk_device, const struct fstab_rec* rec,
+ const struct ext4_super_block* sb, int* fs_stat) {
+ bool has_encrypt = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) != 0;
+ bool want_encrypt = fs_mgr_is_file_encrypted(rec) != 0;
+
+ if (has_encrypt || !want_encrypt) {
+ return;
+ }
+
+ if (!tune2fs_available()) {
+ LERROR << "Unable to enable ext4 encryption on " << blk_device
+ << " because " TUNE2FS_BIN " is missing";
+ return;
+ }
+
+ const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device};
+
+ LINFO << "Enabling ext4 encryption on " << blk_device;
+ if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+ LERROR << "Failed to run " TUNE2FS_BIN " to enable "
+ << "ext4 encryption on " << blk_device;
+ *fs_stat |= FS_STAT_ENABLE_ENCRYPTION_FAILED;
+ }
+}
+
+//
+// Prepare the filesystem on the given block device to be mounted.
+//
+// If the "check" option was given in the fstab record, or it seems that the
+// filesystem was uncleanly shut down, we'll run fsck on the filesystem.
+//
+// If needed, we'll also enable (or disable) filesystem features as specified by
+// the fstab record.
+//
+static int prepare_fs_for_mount(const char* blk_device, const struct fstab_rec* rec) {
+ int fs_stat = 0;
+
+ if (is_extfs(rec->fs_type)) {
+ struct ext4_super_block sb;
+
+ if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
+ if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
+ (sb.s_state & EXT4_VALID_FS) == 0) {
+ LINFO << "Filesystem on " << blk_device << " was not cleanly shutdown; "
+ << "state flags: 0x" << std::hex << sb.s_state << ", "
+ << "incompat feature flags: 0x" << std::hex << sb.s_feature_incompat;
+ fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN;
+ }
+
+ // Note: quotas should be enabled before running fsck.
+ tune_quota(blk_device, rec, &sb, &fs_stat);
} else {
- LINFO << "Running " << TUNE2FS_BIN << " on " << blk_device;
-
- int status = 0;
- int ret = 0;
- unsigned long reserved_blocks = 0;
- android::base::unique_fd fd(
- TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
- if (fd >= 0) {
- struct ext4_super_block sb;
- ret = read_super_block(fd, &sb);
- if (ret < 0) {
- PERROR << "Can't read '" << blk_device << "' super block";
- return;
- }
- reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(&sb);
- unsigned long reserved_threshold = ext4_blocks_count(&sb) * 0.02;
- if (reserved_threshold < reserved_blocks) {
- LWARNING << "Reserved blocks " << reserved_blocks
- << " is too large";
- reserved_blocks = reserved_threshold;
- }
-
- if (ext4_r_blocks_count(&sb) == reserved_blocks) {
- LINFO << "Have reserved same blocks";
- return;
- }
- } else {
- PERROR << "Failed to open '" << blk_device << "'";
- return;
- }
-
- char buf[16] = {0};
- snprintf(buf, sizeof (buf), "-r %lu", reserved_blocks);
- const char *tune2fs_argv[] = {
- TUNE2FS_BIN,
- buf,
- blk_device,
- };
-
- ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
- const_cast<char **>(tune2fs_argv),
- &status, true, LOG_KLOG | LOG_FILE,
- true, NULL, NULL, 0);
-
- if (ret < 0) {
- /* No need to check for error in fork, we can't really handle it now */
- LERROR << "Failed trying to run " << TUNE2FS_BIN;
- *fs_stat |= FS_STAT_TUNE2FS_FAILED;
- }
+ return fs_stat;
}
}
+
+ if ((rec->fs_mgr_flags & MF_CHECK) ||
+ (fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
+ check_fs(blk_device, rec->fs_type, rec->mount_point, &fs_stat);
+ }
+
+ if (is_extfs(rec->fs_type) && (rec->fs_mgr_flags & (MF_RESERVEDSIZE | MF_FILEENCRYPTION))) {
+ struct ext4_super_block sb;
+
+ if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
+ tune_reserved_size(blk_device, rec, &sb, &fs_stat);
+ tune_encrypt(blk_device, rec, &sb, &fs_stat);
+ }
+ }
+
+ return fs_stat;
}
static void remove_trailing_slashes(char *n)
@@ -457,6 +476,16 @@
return rc;
}
+// Orange state means the device is unlocked, see the following link for details.
+// https://source.android.com/security/verifiedboot/verified-boot#device_state
+bool fs_mgr_is_device_unlocked() {
+ std::string verified_boot_state;
+ if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) {
+ return verified_boot_state == "orange";
+ }
+ return false;
+}
+
/*
* __mount(): wrapper around the mount() system call which also
* sets the underlying block device to read-only if the mount is read-only.
@@ -476,10 +505,11 @@
if ((info.st_mode & S_IFMT) == S_IFLNK)
unlink(target);
mkdir(target, 0755);
+ errno = 0;
ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
save_errno = errno;
- LINFO << __FUNCTION__ << "(source=" << source << ",target="
- << target << ",type=" << rec->fs_type << ")=" << ret;
+ PINFO << __FUNCTION__ << "(source=" << source << ",target=" << target
+ << ",type=" << rec->fs_type << ")=" << ret;
if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
fs_mgr_set_blk_ro(source);
}
@@ -559,10 +589,7 @@
continue;
}
- int fs_stat = 0;
- int force_check = do_quota_with_shutdown_check(fstab->recs[i].blk_device,
- fstab->recs[i].fs_type,
- &fstab->recs[i], &fs_stat);
+ int fs_stat = prepare_fs_for_mount(fstab->recs[i].blk_device, &fstab->recs[i]);
if (fs_stat & FS_STAT_EXT4_INVALID_MAGIC) {
LERROR << __FUNCTION__ << "(): skipping mount, invalid ext4, mountpoint="
<< fstab->recs[i].mount_point << " rec[" << i
@@ -570,15 +597,6 @@
mount_errno = EINVAL; // continue bootup for FDE
continue;
}
- if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
- check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
- fstab->recs[i].mount_point, &fs_stat);
- }
-
- if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
- do_reserved_size(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
- &fstab->recs[i], &fs_stat);
- }
int retry_count = 2;
while (retry_count-- > 0) {
@@ -744,19 +762,6 @@
}
}
-// TODO: add ueventd notifiers if they don't exist.
-// This is just doing a wait_for_device for maximum of 1s
-int fs_mgr_test_access(const char *device) {
- int tries = 25;
- while (tries--) {
- if (!access(device, F_OK) || errno != ENOENT) {
- return 0;
- }
- usleep(40 * 1000);
- }
- return -1;
-}
-
bool is_device_secure() {
int ret = -1;
char value[PROP_VALUE_MAX];
@@ -817,9 +822,7 @@
}
/* Translate LABEL= file system labels into block devices */
- if (!strcmp(fstab->recs[i].fs_type, "ext2") ||
- !strcmp(fstab->recs[i].fs_type, "ext3") ||
- !strcmp(fstab->recs[i].fs_type, "ext4")) {
+ if (is_extfs(fstab->recs[i].fs_type)) {
int tret = translate_ext_labels(&fstab->recs[i]);
if (tret < 0) {
LERROR << "Could not translate label to block device";
@@ -827,8 +830,10 @@
}
}
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
- wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
+ !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
+ LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all";
+ continue;
}
if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
@@ -839,7 +844,8 @@
return FS_MGR_MNTALL_FAIL;
}
}
- if (!avb_handle->SetUpAvb(&fstab->recs[i], true /* wait_for_verity_dev */)) {
+ if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
+ SetUpAvbHashtreeResult::kFail) {
LERROR << "Failed to set up AVB on partition: "
<< fstab->recs[i].mount_point << ", skipping!";
/* Skips mounting the device. */
@@ -847,7 +853,9 @@
}
} else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
- if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
+ if (__android_log_is_debuggable() &&
+ (rc == FS_MGR_SETUP_VERITY_DISABLED ||
+ rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
LINFO << "Verity disabled";
} else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
LERROR << "Could not set up verified partition, skipping!";
@@ -1028,22 +1036,12 @@
}
/* First check the filesystem if requested */
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
- wait_for_file(n_blk_device, WAIT_TIMEOUT);
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
+ LERROR << "Skipping mounting '" << n_blk_device << "'";
+ continue;
}
- int fs_stat = 0;
- int force_check = do_quota_with_shutdown_check(n_blk_device, fstab->recs[i].fs_type,
- &fstab->recs[i], &fs_stat);
-
- if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
- check_fs(n_blk_device, fstab->recs[i].fs_type,
- fstab->recs[i].mount_point, &fs_stat);
- }
-
- if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
- do_reserved_size(n_blk_device, fstab->recs[i].fs_type, &fstab->recs[i], &fs_stat);
- }
+ int fs_stat = prepare_fs_for_mount(n_blk_device, &fstab->recs[i]);
if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
if (!avb_handle) {
@@ -1053,7 +1051,8 @@
return FS_MGR_DOMNT_FAILED;
}
}
- if (!avb_handle->SetUpAvb(&fstab->recs[i], true /* wait_for_verity_dev */)) {
+ if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
+ SetUpAvbHashtreeResult::kFail) {
LERROR << "Failed to set up AVB on partition: "
<< fstab->recs[i].mount_point << ", skipping!";
/* Skips mounting the device. */
@@ -1061,7 +1060,9 @@
}
} else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
- if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
+ if (__android_log_is_debuggable() &&
+ (rc == FS_MGR_SETUP_VERITY_DISABLED ||
+ rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
LINFO << "Verity disabled";
} else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
LERROR << "Could not set up verified partition, skipping!";
@@ -1195,12 +1196,15 @@
ret = -1;
continue;
}
- fprintf(zram_fp, "%d\n", fstab->recs[i].zram_size);
+ fprintf(zram_fp, "%u\n", fstab->recs[i].zram_size);
fclose(zram_fp);
}
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
- wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
+ !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
+ LERROR << "Skipping mkswap for '" << fstab->recs[i].blk_device << "'";
+ ret = -1;
+ continue;
}
/* Initialize the swap area */
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 6618003..7824cfa 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -114,7 +114,6 @@
// Reads the following values from kernel cmdline and provides the
// VerifyVbmetaImages() to verify AvbSlotVerifyData.
-// - androidboot.vbmeta.device_state
// - androidboot.vbmeta.hash_alg
// - androidboot.vbmeta.size
// - androidboot.vbmeta.digest
@@ -123,7 +122,6 @@
// The factory method to return a unique_ptr<FsManagerAvbVerifier>
static std::unique_ptr<FsManagerAvbVerifier> Create();
bool VerifyVbmetaImages(const AvbSlotVerifyData& verify_data);
- bool IsDeviceUnlocked() { return is_device_unlocked_; }
protected:
FsManagerAvbVerifier() = default;
@@ -138,7 +136,6 @@
HashAlgorithm hash_alg_;
uint8_t digest_[SHA512_DIGEST_LENGTH];
size_t vbmeta_size_;
- bool is_device_unlocked_;
};
std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
@@ -161,9 +158,7 @@
const std::string& key = pieces[0];
const std::string& value = pieces[1];
- if (key == "androidboot.vbmeta.device_state") {
- avb_verifier->is_device_unlocked_ = (value == "unlocked");
- } else if (key == "androidboot.vbmeta.hash_alg") {
+ if (key == "androidboot.vbmeta.hash_alg") {
hash_alg = value;
} else if (key == "androidboot.vbmeta.size") {
if (!android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
@@ -402,7 +397,7 @@
fstab_entry->blk_device = strdup(verity_blk_name.c_str());
// Makes sure we've set everything up properly.
- if (wait_for_verity_dev && fs_mgr_test_access(verity_blk_name.c_str()) < 0) {
+ if (wait_for_verity_dev && !fs_mgr_wait_for_file(verity_blk_name, 1s)) {
return false;
}
@@ -493,12 +488,7 @@
}
FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
- // Gets the expected hash value of vbmeta images from kernel cmdline.
- std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
- if (!avb_verifier) {
- LERROR << "Failed to create FsManagerAvbVerifier";
- return nullptr;
- }
+ bool is_device_unlocked = fs_mgr_is_device_unlocked();
FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle());
if (!avb_handle) {
@@ -506,9 +496,8 @@
return nullptr;
}
- AvbSlotVerifyFlags flags = avb_verifier->IsDeviceUnlocked()
- ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
- : AVB_SLOT_VERIFY_FLAGS_NONE;
+ AvbSlotVerifyFlags flags = is_device_unlocked ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
+ : AVB_SLOT_VERIFY_FLAGS_NONE;
AvbSlotVerifyResult verify_result =
avb_ops->AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->avb_slot_data_);
@@ -526,62 +515,81 @@
// for more details.
switch (verify_result) {
case AVB_SLOT_VERIFY_RESULT_OK:
- avb_handle->status_ = kFsManagerAvbHandleSuccess;
+ avb_handle->status_ = kAvbHandleSuccess;
break;
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
- if (!avb_verifier->IsDeviceUnlocked()) {
+ if (!is_device_unlocked) {
LERROR << "ERROR_VERIFICATION isn't allowed when the device is LOCKED";
return nullptr;
}
- avb_handle->status_ = kFsManagerAvbHandleErrorVerification;
+ avb_handle->status_ = kAvbHandleVerificationError;
break;
default:
LERROR << "avb_slot_verify failed, result: " << verify_result;
return nullptr;
}
- // Verifies vbmeta images against the digest passed from bootloader.
- if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
- LERROR << "VerifyVbmetaImages failed";
- return nullptr;
- }
-
// Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
avb_handle->avb_version_ =
android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
- // Checks whether FLAGS_HASHTREE_DISABLED is set.
+ // Checks whether FLAGS_VERIFICATION_DISABLED is set:
+ // - Only the top-level vbmeta struct is read.
+ // - vbmeta struct in other partitions are NOT processed, including AVB HASH descriptor(s)
+ // and AVB HASHTREE descriptor(s).
AvbVBMetaImageHeader vbmeta_header;
avb_vbmeta_image_header_to_host_byte_order(
(AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
&vbmeta_header);
+ bool verification_disabled =
+ ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
- bool hashtree_disabled =
- ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
- if (hashtree_disabled) {
- avb_handle->status_ = kFsManagerAvbHandleHashtreeDisabled;
+ if (verification_disabled) {
+ avb_handle->status_ = kAvbHandleVerificationDisabled;
+ } else {
+ // Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
+ std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
+ if (!avb_verifier) {
+ LERROR << "Failed to create FsManagerAvbVerifier";
+ return nullptr;
+ }
+ if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
+ LERROR << "VerifyVbmetaImages failed";
+ return nullptr;
+ }
+
+ // Checks whether FLAGS_HASHTREE_DISABLED is set.
+ bool hashtree_disabled =
+ ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+ if (hashtree_disabled) {
+ avb_handle->status_ = kAvbHandleHashtreeDisabled;
+ }
}
LINFO << "Returning avb_handle with status: " << avb_handle->status_;
return avb_handle;
}
-bool FsManagerAvbHandle::SetUpAvb(struct fstab_rec* fstab_entry, bool wait_for_verity_dev) {
- if (!fstab_entry) return false;
- if (!avb_slot_data_ || avb_slot_data_->num_vbmeta_images < 1) {
- return false;
+SetUpAvbHashtreeResult FsManagerAvbHandle::SetUpAvbHashtree(struct fstab_rec* fstab_entry,
+ bool wait_for_verity_dev) {
+ if (!fstab_entry || status_ == kAvbHandleUninitialized || !avb_slot_data_ ||
+ avb_slot_data_->num_vbmeta_images < 1) {
+ return SetUpAvbHashtreeResult::kFail;
}
- if (status_ == kFsManagerAvbHandleUninitialized) return false;
- if (status_ == kFsManagerAvbHandleHashtreeDisabled) {
- LINFO << "AVB HASHTREE disabled on:" << fstab_entry->mount_point;
- return true;
+ if (status_ == kAvbHandleHashtreeDisabled || status_ == kAvbHandleVerificationDisabled) {
+ LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point;
+ return SetUpAvbHashtreeResult::kDisabled;
}
- std::string partition_name(basename(fstab_entry->mount_point));
- if (!avb_validate_utf8((const uint8_t*)partition_name.c_str(), partition_name.length())) {
- LERROR << "Partition name: " << partition_name.c_str() << " is not valid UTF-8.";
- return false;
+ // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
+ // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
+ std::string partition_name(basename(fstab_entry->blk_device));
+ if (fstab_entry->fs_mgr_flags & MF_SLOTSELECT) {
+ auto ab_suffix = partition_name.rfind(fs_mgr_get_slot_suffix());
+ if (ab_suffix != std::string::npos) {
+ partition_name.erase(ab_suffix);
+ }
}
AvbHashtreeDescriptor hashtree_descriptor;
@@ -589,13 +597,14 @@
std::string root_digest;
if (!get_hashtree_descriptor(partition_name, *avb_slot_data_, &hashtree_descriptor, &salt,
&root_digest)) {
- return false;
+ return SetUpAvbHashtreeResult::kFail;
}
// Converts HASHTREE descriptor to verity_table_params.
if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest,
wait_for_verity_dev)) {
- return false;
+ return SetUpAvbHashtreeResult::kFail;
}
- return true;
+
+ return SetUpAvbHashtreeResult::kSuccess;
}
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index 512839b..43879fe 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -89,6 +89,15 @@
return AVB_IO_RESULT_OK;
}
+static AvbIOResult dummy_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+ const char* partition ATTRIBUTE_UNUSED,
+ uint64_t* out_size_num_byte) {
+ // The function is for bootloader to load entire content of AVB HASH partitions.
+ // In user-space, returns 0 as we only need to set up AVB HASHTHREE partitions.
+ *out_size_num_byte = 0;
+ return AVB_IO_RESULT_OK;
+}
+
void FsManagerAvbOps::InitializeAvbOps() {
// We only need to provide the implementation of read_from_partition()
// operation since that's all what is being used by the avb_slot_verify().
@@ -101,6 +110,7 @@
avb_ops_.validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
avb_ops_.read_is_device_unlocked = dummy_read_is_device_unlocked;
avb_ops_.get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
+ avb_ops_.get_size_of_partition = dummy_get_size_of_partition;
// Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
avb_ops_.user_data = this;
@@ -132,10 +142,8 @@
}
std::string path = iter->second;
- // Ensures the device path (a symlink created by init) is ready to
- // access. fs_mgr_test_access() will test a few iterations if the
- // path doesn't exist yet.
- if (fs_mgr_test_access(path.c_str()) < 0) {
+ // Ensures the device path (a symlink created by init) is ready to access.
+ if (!fs_mgr_wait_for_file(path, 1s)) {
return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
}
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index ab5beed..9117667 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <string>
+
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -51,9 +53,11 @@
// lastly, check the device tree
if (is_dt_compatible()) {
std::string file_name = kAndroidDtDir + "/" + key;
- // DT entries terminate with '\0' but so do the properties
if (android::base::ReadFileToString(file_name, out_val)) {
- return true;
+ if (!out_val->empty()) {
+ out_val->pop_back(); // Trims the trailing '\0' out.
+ return true;
+ }
}
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index dc73c24..b68875b 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -337,21 +337,22 @@
dirent* dp;
while ((dp = readdir(fstabdir.get())) != NULL) {
- // skip over name and compatible
- if (dp->d_type != DT_DIR) {
- continue;
- }
-
- // skip if its not 'vendor', 'odm' or 'system'
- if (strcmp(dp->d_name, "odm") && strcmp(dp->d_name, "system") &&
- strcmp(dp->d_name, "vendor")) {
- continue;
- }
+ // skip over name, compatible and .
+ if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
// create <dev> <mnt_point> <type> <mnt_flags> <fsmgr_flags>\n
std::vector<std::string> fstab_entry;
std::string file_name;
std::string value;
+ // skip a partition entry if the status property is present and not set to ok
+ file_name = android::base::StringPrintf("%s/%s/status", fstabdir_name.c_str(), dp->d_name);
+ if (read_dt_file(file_name, &value)) {
+ if (value != "okay" && value != "ok") {
+ LINFO << "dt_fstab: Skip disabled entry for partition " << dp->d_name;
+ continue;
+ }
+ }
+
file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
if (!read_dt_file(file_name, &value)) {
LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
@@ -809,32 +810,26 @@
return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
}
-int fs_mgr_is_notrim(struct fstab_rec *fstab)
-{
+int fs_mgr_is_notrim(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & MF_NOTRIM;
}
-int fs_mgr_is_formattable(struct fstab_rec *fstab)
-{
+int fs_mgr_is_formattable(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & (MF_FORMATTABLE);
}
-int fs_mgr_is_slotselect(struct fstab_rec *fstab)
-{
+int fs_mgr_is_slotselect(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & MF_SLOTSELECT;
}
-int fs_mgr_is_nofail(struct fstab_rec *fstab)
-{
+int fs_mgr_is_nofail(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & MF_NOFAIL;
}
-int fs_mgr_is_latemount(struct fstab_rec *fstab)
-{
+int fs_mgr_is_latemount(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & MF_LATEMOUNT;
}
-int fs_mgr_is_quota(struct fstab_rec *fstab)
-{
+int fs_mgr_is_quota(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & MF_QUOTA;
}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index c985462..5ea6e98 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -17,8 +17,12 @@
#ifndef __CORE_FS_MGR_PRIV_H
#define __CORE_FS_MGR_PRIV_H
+#include <chrono>
+#include <string>
+
#include <android-base/logging.h>
-#include <fs_mgr.h>
+
+#include "fs_mgr.h"
#include "fs_mgr_priv_boot_config.h"
/* The CHECK() in logging.h will use program invocation name as the tag.
@@ -43,8 +47,6 @@
#define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000"
-#define WAIT_TIMEOUT 20
-
/* fstab has the following format:
*
* Any line starting with a # is a comment and ignored
@@ -110,9 +112,13 @@
#define DM_BUF_SIZE 4096
+using namespace std::chrono_literals;
+
int fs_mgr_set_blk_ro(const char *blockdev);
-int fs_mgr_test_access(const char *device);
+bool fs_mgr_wait_for_file(const std::string& filename,
+ const std::chrono::milliseconds relative_timeout);
bool fs_mgr_update_for_slotselect(struct fstab *fstab);
+bool fs_mgr_is_device_unlocked();
bool is_dt_compatible();
bool is_device_secure();
int load_verity_state(struct fstab_rec* fstab, int* mode);
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5fa10bc..5de0903 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -348,10 +348,13 @@
static int was_verity_restart()
{
- static const char *files[] = {
+ static const char* files[] = {
+ // clang-format off
+ "/sys/fs/pstore/console-ramoops-0",
"/sys/fs/pstore/console-ramoops",
"/proc/last_kmsg",
NULL
+ // clang-format on
};
int i;
@@ -689,27 +692,55 @@
return read_verity_state(fstab->verity_loc, offset, mode);
}
-static void update_verity_table_blk_device(char *blk_device, char **table)
-{
- std::string result, word;
+// Update the verity table using the actual block device path.
+// Two cases:
+// Case-1: verity table is shared for devices with different by-name prefix.
+// Example:
+// verity table token: /dev/block/bootdevice/by-name/vendor
+// blk_device-1 (non-A/B): /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+// blk_device-2 (A/B): /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor_a
+//
+// Case-2: append A/B suffix in the verity table.
+// Example:
+// verity table token: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+// blk_device: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor_a
+static void update_verity_table_blk_device(const std::string& blk_device, char** table,
+ bool slot_select) {
+ bool updated = false;
+ std::string result, ab_suffix;
auto tokens = android::base::Split(*table, " ");
+ // If slot_select is set, it means blk_device is already updated with ab_suffix.
+ if (slot_select) ab_suffix = fs_mgr_get_slot_suffix();
+
for (const auto& token : tokens) {
- if (android::base::StartsWith(token, "/dev/block/") &&
- android::base::StartsWith(blk_device, token.c_str())) {
- word = blk_device;
+ std::string new_token;
+ if (android::base::StartsWith(token, "/dev/block/")) {
+ if (token == blk_device) return; // no need to update if they're already the same.
+ std::size_t found1 = blk_device.find("by-name");
+ std::size_t found2 = token.find("by-name");
+ if (found1 != std::string::npos && found2 != std::string::npos &&
+ blk_device.substr(found1) == token.substr(found2) + ab_suffix) {
+ new_token = blk_device;
+ }
+ }
+
+ if (!new_token.empty()) {
+ updated = true;
+ LINFO << "Verity table: updated block device from '" << token << "' to '" << new_token
+ << "'";
} else {
- word = token;
+ new_token = token;
}
if (result.empty()) {
- result = word;
+ result = new_token;
} else {
- result += " " + word;
+ result += " " + new_token;
}
}
- if (result.empty()) {
+ if (!updated) {
return;
}
@@ -738,7 +769,7 @@
// setup is needed at all.
if (!is_device_secure()) {
LINFO << "Verity setup skipped for " << mount_point;
- return FS_MGR_SETUP_VERITY_SUCCESS;
+ return FS_MGR_SETUP_VERITY_SKIPPED;
}
if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
@@ -751,8 +782,8 @@
if (fec_verity_get_metadata(f, &verity) < 0) {
PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
// Allow verity disabled when the device is unlocked without metadata
- if ("0" == android::base::GetProperty("ro.boot.flash.locked", "")) {
- retval = FS_MGR_SETUP_VERITY_DISABLED;
+ if (fs_mgr_is_device_unlocked()) {
+ retval = FS_MGR_SETUP_VERITY_SKIPPED;
LWARNING << "Allow invalid metadata when the device is unlocked";
}
goto out;
@@ -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,
@@ -899,7 +929,7 @@
}
// make sure we've set everything up properly
- if (wait_for_verity_dev && fs_mgr_test_access(fstab->blk_device) < 0) {
+ if (wait_for_verity_dev && !fs_mgr_wait_for_file(fstab->blk_device, 1s)) {
goto out;
}
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 9079a43..3d3faf3 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -22,11 +22,7 @@
#include <stdbool.h>
#include <linux/dm-ioctl.h>
-// C++ only headers
-// TODO: move this into separate header files under include/fs_mgr/*.h
-#ifdef __cplusplus
-#include <string>
-#endif
+#include <fstab/fstab.h>
// Magic number at start of verity metadata
#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001
@@ -53,47 +49,10 @@
MOUNT_MODE_LATE = 2
};
-/*
- * The entries must be kept in the same order as they were seen in the fstab.
- * Unless explicitly requested, a lookup on mount point should always
- * return the 1st one.
- */
-struct fstab {
- int num_entries;
- struct fstab_rec *recs;
- char *fstab_filename;
-};
-
-struct fstab_rec {
- char *blk_device;
- char *mount_point;
- char *fs_type;
- unsigned long flags;
- char *fs_options;
- int fs_mgr_flags;
- char *key_loc;
- char *verity_loc;
- long long length;
- char *label;
- int partnum;
- int swap_prio;
- int max_comp_streams;
- unsigned int zram_size;
- uint64_t reserved_size;
- unsigned int file_encryption_mode;
- unsigned int erase_blk_size;
- unsigned int logical_blk_size;
-};
-
// Callback function for verity status
typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab,
const char *mount_point, int mode, int status);
-struct fstab *fs_mgr_read_fstab_default();
-struct fstab *fs_mgr_read_fstab_dt();
-struct fstab *fs_mgr_read_fstab(const char *fstab_path);
-void fs_mgr_free_fstab(struct fstab *fstab);
-
#define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4
#define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 3
@@ -116,30 +75,11 @@
char *real_blk_device, int size);
bool fs_mgr_load_verity_state(int* mode);
bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
-int fs_mgr_add_entry(struct fstab *fstab,
- const char *mount_point, const char *fs_type,
- const char *blk_device);
-struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path);
-int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab);
-int fs_mgr_is_nonremovable(const struct fstab_rec *fstab);
-int fs_mgr_is_verified(const struct fstab_rec *fstab);
-int fs_mgr_is_verifyatboot(const struct fstab_rec *fstab);
-int fs_mgr_is_avb(const struct fstab_rec *fstab);
-int fs_mgr_is_encryptable(const struct fstab_rec *fstab);
-int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab);
-const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec *fstab);
-int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab);
-int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab);
-int fs_mgr_is_notrim(struct fstab_rec *fstab);
-int fs_mgr_is_formattable(struct fstab_rec *fstab);
-int fs_mgr_is_slotselect(struct fstab_rec *fstab);
-int fs_mgr_is_nofail(struct fstab_rec *fstab);
-int fs_mgr_is_latemount(struct fstab_rec *fstab);
-int fs_mgr_is_quota(struct fstab_rec *fstab);
int fs_mgr_swapon_all(struct fstab *fstab);
int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
+#define FS_MGR_SETUP_VERITY_SKIPPED (-3)
#define FS_MGR_SETUP_VERITY_DISABLED (-2)
#define FS_MGR_SETUP_VERITY_FAIL (-1)
#define FS_MGR_SETUP_VERITY_SUCCESS 0
@@ -147,10 +87,4 @@
__END_DECLS
-// C++ only functions
-// TODO: move this into separate header files under include/fs_mgr/*.h
-#ifdef __cplusplus
-std::string fs_mgr_get_slot_suffix();
-#endif
-
#endif /* __CORE_FS_MGR_H */
diff --git a/fs_mgr/include/fs_mgr_avb.h b/fs_mgr/include/fs_mgr_avb.h
index bbafe1a..73a22c8 100644
--- a/fs_mgr/include/fs_mgr_avb.h
+++ b/fs_mgr/include/fs_mgr_avb.h
@@ -25,11 +25,10 @@
#include "fs_mgr.h"
-enum FsManagerAvbHandleStatus {
- kFsManagerAvbHandleUninitialized = -1,
- kFsManagerAvbHandleSuccess = 0,
- kFsManagerAvbHandleHashtreeDisabled = 1,
- kFsManagerAvbHandleErrorVerification = 2,
+enum class SetUpAvbHashtreeResult {
+ kSuccess = 0,
+ kFail,
+ kDisabled,
};
class FsManagerAvbOps;
@@ -40,8 +39,8 @@
using ByNameSymlinkMap = std::map<std::string, std::string>;
// Provides a factory method to return a unique_ptr pointing to itself and the
-// SetUpAvb() function to extract dm-verity parameters from AVB metadata to
-// load verity table into kernel through ioctl.
+// SetUpAvbHashtree() function to extract dm-verity parameters from AVB HASHTREE
+// descriptors to load verity table into kernel through ioctl.
class FsManagerAvbHandle {
public:
// The factory method to return a FsManagerAvbUniquePtr that holds
@@ -65,12 +64,22 @@
// - nullptr: any error when reading and verifying the metadata,
// e.g., I/O error, digest value mismatch, size mismatch, etc.
//
- // - a valid unique_ptr with status kFsMgrAvbHandleHashtreeDisabled:
+ // - a valid unique_ptr with status kAvbHandleHashtreeDisabled:
// to support the existing 'adb disable-verity' feature in Android.
// It's very helpful for developers to make the filesystem writable to
// allow replacing binaries on the device.
//
- // - a valid unique_ptr with status kFsMgrAvbHandleSuccess: the metadata
+ // - a valid unique_ptr with status kAvbHandleVerificationDisabled:
+ // to support 'avbctl disable-verification': only the top-level
+ // vbmeta is read, vbmeta structs in other partitions are not processed.
+ // It's needed to bypass AVB when using the generic system.img to run
+ // VTS for project Treble.
+ //
+ // - a valid unique_ptr with status kAvbHandleVerificationError:
+ // there is verification error when libavb loads vbmeta from each
+ // partition. This is only allowed when the device is unlocked.
+ //
+ // - a valid unique_ptr with status kAvbHandleSuccess: the metadata
// is verified and can be trusted.
//
static FsManagerAvbUniquePtr Open(const fstab& fstab);
@@ -79,14 +88,15 @@
// Sets up dm-verity on the given fstab entry.
// The 'wait_for_verity_dev' parameter makes this function wait for the
// verity device to get created before return.
- // Returns true if the mount point is eligible to mount, it includes:
- // - status_ is kFsMgrAvbHandleHashtreeDisabled or
- // - status_ is kFsMgrAvbHandleSuccess and sending ioctl DM_TABLE_LOAD
- // to load verity table is success.
- // Otherwise, returns false.
- bool SetUpAvb(fstab_rec* fstab_entry, bool wait_for_verity_dev);
+ //
+ // Return value:
+ // - kSuccess: successfully loads dm-verity table into kernel.
+ // - kFailed: failed to setup dm-verity, e.g., vbmeta verification error,
+ // failed to get the HASHTREE descriptor, runtime error when set up
+ // device-mapper, etc.
+ // - kDisabled: hashtree is disabled.
+ SetUpAvbHashtreeResult SetUpAvbHashtree(fstab_rec* fstab_entry, bool wait_for_verity_dev);
- bool hashtree_disabled() const { return status_ == kFsManagerAvbHandleHashtreeDisabled; }
const std::string& avb_version() const { return avb_version_; }
FsManagerAvbHandle(const FsManagerAvbHandle&) = delete; // no copy
@@ -102,11 +112,19 @@
};
private:
- FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kFsManagerAvbHandleUninitialized) {}
+ enum AvbHandleStatus {
+ kAvbHandleSuccess = 0,
+ kAvbHandleUninitialized,
+ kAvbHandleHashtreeDisabled,
+ kAvbHandleVerificationDisabled,
+ kAvbHandleVerificationError,
+ };
+
+ FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kAvbHandleUninitialized) {}
static FsManagerAvbUniquePtr DoOpen(FsManagerAvbOps* avb_ops);
AvbSlotVerifyData* avb_slot_data_;
- FsManagerAvbHandleStatus status_;
+ AvbHandleStatus status_;
std::string avb_version_;
};
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
new file mode 100644
index 0000000..17e1fb1
--- /dev/null
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CORE_FS_TAB_H
+#define __CORE_FS_TAB_H
+
+#include <linux/dm-ioctl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+// C++ only headers
+// TODO: move this into separate header files under include/fs_mgr/*.h
+#ifdef __cplusplus
+#include <string>
+#endif
+
+__BEGIN_DECLS
+
+/*
+ * The entries must be kept in the same order as they were seen in the fstab.
+ * Unless explicitly requested, a lookup on mount point should always
+ * return the 1st one.
+ */
+struct fstab {
+ int num_entries;
+ struct fstab_rec* recs;
+ char* fstab_filename;
+};
+
+struct fstab_rec {
+ char* blk_device;
+ char* mount_point;
+ char* fs_type;
+ unsigned long flags;
+ char* fs_options;
+ int fs_mgr_flags;
+ char* key_loc;
+ char* verity_loc;
+ long long length;
+ char* label;
+ int partnum;
+ int swap_prio;
+ int max_comp_streams;
+ unsigned int zram_size;
+ uint64_t reserved_size;
+ unsigned int file_encryption_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);
+const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec* fstab);
+int fs_mgr_is_convertible_to_fbe(const struct fstab_rec* fstab);
+int fs_mgr_is_noemulatedsd(const struct fstab_rec* fstab);
+int fs_mgr_is_notrim(const struct fstab_rec* fstab);
+int fs_mgr_is_formattable(const struct fstab_rec* fstab);
+int fs_mgr_is_slotselect(const struct fstab_rec* fstab);
+int fs_mgr_is_nofail(const struct fstab_rec* fstab);
+int fs_mgr_is_latemount(const struct fstab_rec* fstab);
+int fs_mgr_is_quota(const struct fstab_rec* fstab);
+
+__END_DECLS
+
+// C++ only functions
+// TODO: move this into separate header files under include/fs_mgr/*.h
+#ifdef __cplusplus
+std::string fs_mgr_get_slot_suffix();
+#endif
+
+#endif /* __CORE_FS_TAB_H */
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 45b6eda..4e3b885 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/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
index 523e1f1..e51a06d 100644
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ b/healthd/BatteryPropertiesRegistrar.cpp
@@ -36,9 +36,19 @@
}
void BatteryPropertiesRegistrar::notifyListeners(const struct BatteryProperties& props) {
- Mutex::Autolock _l(mRegistrationLock);
- for (size_t i = 0; i < mListeners.size(); i++) {
- mListeners[i]->batteryPropertiesChanged(props);
+ Vector<sp<IBatteryPropertiesListener> > listenersCopy;
+
+ // Binder currently may service an incoming oneway transaction whenever an
+ // outbound oneway call is made (if there is already a pending incoming
+ // oneway call waiting). This is considered a bug and may change in the
+ // future. For now, avoid recursive mutex lock while making outbound
+ // calls by making a local copy of the current list of listeners.
+ {
+ Mutex::Autolock _l(mRegistrationLock);
+ listenersCopy = mListeners;
+ }
+ for (size_t i = 0; i < listenersCopy.size(); i++) {
+ listenersCopy[i]->batteryPropertiesChanged(props);
}
}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 91774c6..d7d1454 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/init/Android.bp b/init/Android.bp
index 80d5c42..47578ea 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",
@@ -66,11 +65,14 @@
"capabilities.cpp",
"descriptors.cpp",
"devices.cpp",
+ "firmware_handler.cpp",
"import_parser.cpp",
"init_parser.cpp",
"log.cpp",
"parser.cpp",
"service.cpp",
+ "uevent_listener.cpp",
+ "ueventd_parser.cpp",
"util.cpp",
],
whole_static_libs: ["libcap"],
@@ -151,6 +153,7 @@
"init_test.cpp",
"property_service_test.cpp",
"service_test.cpp",
+ "ueventd_test.cpp",
"util_test.cpp",
],
shared_libs: [
diff --git a/init/Android.mk b/init/Android.mk
index 489d076..6cd47f4 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -40,8 +40,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/README.md b/init/README.md
index 72b6c6b..422fdad 100644
--- a/init/README.md
+++ b/init/README.md
@@ -260,6 +260,14 @@
> Sets the child's /proc/self/oom\_score\_adj to the specified value,
which must range from -1000 to 1000.
+`shutdown <shutdown_behavior>`
+> Set shutdown behavior of the service process. When this is not specified,
+ the service is killed during shutdown process by using SIGTERM and SIGKILL.
+ The service with shutdown_behavior of "critical" is not killed during shutdown
+ until shutdown times out. When shutdown times out, even services tagged with
+ "shutdown critical" will be killed. When the service tagged with "shutdown critical"
+ is not running when shut down starts, it will be started.
+
Triggers
--------
diff --git a/init/action.cpp b/init/action.cpp
index 21abe02..4ec5f17 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -16,15 +16,17 @@
#include "action.h"
+#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include "util.h"
using android::base::Join;
-using android::base::StringPrintf;
+
+namespace android {
+namespace init {
Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
: func_(f), args_(args), line_(line) {}
@@ -89,19 +91,18 @@
}
void Action::ExecuteCommand(const Command& command) const {
- Timer t;
+ android::base::Timer t;
int result = command.InvokeFunc();
- double duration_ms = t.duration_s() * 1000;
+ auto duration = t.duration();
// Any action longer than 50ms will be warned to user as slow operation
- if (duration_ms > 50.0 ||
- android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
+ if (duration > 50ms || android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
- std::string source = StringPrintf(" (%s:%d)", filename_.c_str(), command.line());
- LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
- << " returned " << result << " took " << duration_ms << "ms.";
+ LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
+ << ":" << command.line() << ") returned " << result << " took "
+ << duration.count() << "ms.";
}
}
@@ -325,6 +326,13 @@
}
}
+void ActionManager::ClearQueue() {
+ // We are shutting down so don't claim the oneshot builtin actions back
+ current_executing_actions_ = {};
+ event_queue_ = {};
+ current_command_ = 0;
+}
+
bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line, std::string* err) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
@@ -351,3 +359,6 @@
action_manager_->AddAction(std::move(action_));
}
}
+
+} // namespace init
+} // namespace android
diff --git a/init/action.h b/init/action.h
index d006c50..ad15f3f 100644
--- a/init/action.h
+++ b/init/action.h
@@ -27,6 +27,9 @@
#include "init_parser.h"
#include "keyword_map.h"
+namespace android {
+namespace init {
+
class Command {
public:
Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
@@ -101,6 +104,7 @@
void ExecuteOneCommand();
bool HasMoreCommands() const;
void DumpState() const;
+ void ClearQueue();
private:
ActionManager(ActionManager const&) = delete;
@@ -126,4 +130,7 @@
std::unique_ptr<Action> action_;
};
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 825603a..4727f92 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -40,6 +40,9 @@
using android::base::StringPrintf;
using namespace std::chrono_literals;
+namespace android {
+namespace init {
+
static std::thread* g_bootcharting_thread;
static std::mutex g_bootcharting_finished_mutex;
@@ -192,3 +195,6 @@
if (args[1] == "start") return do_bootchart_start();
return do_bootchart_stop();
}
+
+} // namespace init
+} // namespace android
diff --git a/init/bootchart.h b/init/bootchart.h
index 0e3593d..e4f7b59 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,6 +20,12 @@
#include <string>
#include <vector>
+namespace android {
+namespace init {
+
int do_bootchart(const std::vector<std::string>& args);
+} // namespace init
+} // namespace android
+
#endif /* _BOOTCHART_H */
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 1eacb36..dfd7b73 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -39,11 +39,11 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
@@ -68,6 +68,9 @@
#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
+namespace android {
+namespace init {
+
static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
static int insmod(const char *filename, const char *options, int flags) {
@@ -117,7 +120,7 @@
LOG(ERROR) << "failed to set bootloader message: " << err;
return -1;
}
- DoReboot(ANDROID_RB_RESTART2, "reboot", "recovery", false);
+ property_set("sys.powerctl", "reboot,recovery");
return 0;
}
@@ -535,11 +538,10 @@
}
}
- std::string prop_name = android::base::StringPrintf("ro.boottime.init.mount_all.%s",
- prop_post_fix);
- Timer t;
+ std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
+ android::base::Timer t;
int ret = mount_fstab(fstabfile, mount_mode);
- property_set(prop_name.c_str(), std::to_string(t.duration_ms()).c_str());
+ property_set(prop_name, std::to_string(t.duration().count()));
if (import_rc) {
/* Paths of .rc files are specified at the 2nd argument and beyond */
@@ -567,9 +569,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;
}
@@ -652,8 +652,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) {
@@ -929,3 +928,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 e0351a3..13cf991 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2014 The Android Open Source Project
+ * Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,42 +16,20 @@
#include "devices.h"
-#include <dirent.h>
#include <errno.h>
-#include <fcntl.h>
#include <fnmatch.h>
-#include <grp.h>
-#include <libgen.h>
-#include <linux/netlink.h>
-#include <pwd.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/sendfile.h>
-#include <sys/socket.h>
#include <sys/sysmacros.h>
-#include <sys/time.h>
-#include <sys/un.h>
-#include <sys/wait.h>
#include <unistd.h>
-#include <algorithm>
#include <memory>
-#include <thread>
-#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <cutils/uevent.h>
#include <private/android_filesystem_config.h>
#include <selinux/android.h>
-#include <selinux/label.h>
#include <selinux/selinux.h>
-#include "keyword_map.h"
#include "ueventd.h"
#include "util.h"
@@ -59,305 +37,23 @@
#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
#endif
-static selabel_handle* sehandle;
+using android::base::Basename;
+using android::base::Dirname;
+using android::base::Readlink;
+using android::base::Realpath;
+using android::base::StartsWith;
+using android::base::StringPrintf;
-static android::base::unique_fd device_fd;
-
-Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
- : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
- // If the first * is the last character, then we'll treat name_ as a prefix
- // Otherwise, if a * is present, then we do a full fnmatch().
- auto wildcard_position = name_.find('*');
- if (wildcard_position == name_.length() - 1) {
- prefix_ = true;
- name_.pop_back();
- } else if (wildcard_position != std::string::npos) {
- wildcard_ = true;
- }
-}
-
-bool Permissions::Match(const std::string& path) const {
- if (prefix_) {
- return android::base::StartsWith(path, name_.c_str());
- } else if (wildcard_) {
- return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
- } else {
- return path == name_;
- }
-
- return false;
-}
-
-bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
- const std::string& subsystem) const {
- std::string path_basename = android::base::Basename(path);
- if (name().find(subsystem) != std::string::npos) {
- if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
- if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
- }
- return Match(path);
-}
-
-void SysfsPermissions::SetPermissions(const std::string& path) const {
- std::string attribute_file = path + "/" + attribute_;
- LOG(INFO) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
- << perm();
- chown(attribute_file.c_str(), uid(), gid());
- chmod(attribute_file.c_str(), perm());
-}
-
-// TODO: Move these to be member variables of a future devices class.
-std::vector<Permissions> dev_permissions;
-std::vector<SysfsPermissions> sysfs_permissions;
-
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs) {
- if (is_sysfs && args.size() != 5) {
- *err = "/sys/ lines must have 5 entries";
- return false;
- }
-
- if (!is_sysfs && args.size() != 4) {
- *err = "/dev/ lines must have 4 entries";
- return false;
- }
-
- auto it = args.begin();
- const std::string& name = *it++;
-
- std::string sysfs_attribute;
- if (is_sysfs) sysfs_attribute = *it++;
-
- // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
- std::string& perm_string = *it++;
- char* end_pointer = 0;
- mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
- if (end_pointer == nullptr || *end_pointer != '\0') {
- *err = "invalid mode '" + perm_string + "'";
- return false;
- }
-
- std::string& uid_string = *it++;
- passwd* pwd = getpwnam(uid_string.c_str());
- if (!pwd) {
- *err = "invalid uid '" + uid_string + "'";
- return false;
- }
- uid_t uid = pwd->pw_uid;
-
- std::string& gid_string = *it++;
- struct group* grp = getgrnam(gid_string.c_str());
- if (!grp) {
- *err = "invalid gid '" + gid_string + "'";
- return false;
- }
- gid_t gid = grp->gr_gid;
-
- if (is_sysfs) {
- sysfs_permissions.emplace_back(name, sysfs_attribute, perm, uid, gid);
- } else {
- dev_permissions.emplace_back(name, perm, uid, gid);
- }
- return true;
-}
-
-// TODO: Move this to be a member variable of a future devices class.
-static std::vector<Subsystem> subsystems;
-
-std::string Subsystem::ParseDevPath(uevent* uevent) const {
- std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
- ? uevent->device_name
- : android::base::Basename(uevent->path);
-
- return dir_name_ + "/" + devname;
-}
-
-bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line, std::string* err) {
- if (args.size() != 2) {
- *err = "subsystems must have exactly one name";
- return false;
- }
-
- if (std::find(subsystems.begin(), subsystems.end(), args[1]) != subsystems.end()) {
- *err = "ignoring duplicate subsystem entry";
- return false;
- }
-
- subsystem_.name_ = args[1];
-
- return true;
-}
-
-bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
- if (args[1] == "uevent_devname") {
- subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
- return true;
- }
- if (args[1] == "uevent_devpath") {
- subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
- return true;
- }
-
- *err = "invalid devname '" + args[1] + "'";
- return false;
-}
-
-bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
- if (args[1].front() != '/') {
- *err = "dirname '" + args[1] + " ' does not start with '/'";
- return false;
- }
-
- subsystem_.dir_name_ = args[1];
- return true;
-}
-
-bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
- using OptionParser =
- bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
- static class OptionParserMap : public KeywordMap<OptionParser> {
- private:
- const Map& map() const override {
- // clang-format off
- static const Map option_parsers = {
- {"devname", {1, 1, &SubsystemParser::ParseDevName}},
- {"dirname", {1, 1, &SubsystemParser::ParseDirName}},
- };
- // clang-format on
- return option_parsers;
- }
- } parser_map;
-
- auto parser = parser_map.FindFunction(args, err);
-
- if (!parser) {
- return false;
- }
-
- return (this->*parser)(std::move(args), err);
-}
-
-void SubsystemParser::EndSection() {
- subsystems.emplace_back(std::move(subsystem_));
-}
-
-static void fixup_sys_permissions(const std::string& upath, const std::string& subsystem) {
- // upaths omit the "/sys" that paths in this list
- // contain, so we prepend it...
- std::string path = "/sys" + upath;
-
- for (const auto& s : sysfs_permissions) {
- if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
- }
-
- if (access(path.c_str(), F_OK) == 0) {
- LOG(VERBOSE) << "restorecon_recursive: " << path;
- selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
- }
-}
-
-static std::tuple<mode_t, uid_t, gid_t> get_device_permissions(
- const std::string& path, const std::vector<std::string>& links) {
- // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
- for (auto it = dev_permissions.rbegin(); it != dev_permissions.rend(); ++it) {
- if (it->Match(path) || std::any_of(links.begin(), links.end(),
- [it](const auto& link) { return it->Match(link); })) {
- return {it->perm(), it->uid(), it->gid()};
- }
- }
- /* Default if nothing found. */
- return {0600, 0, 0};
-}
-
-static void make_device(const std::string& path, int block, int major, int minor,
- const std::vector<std::string>& links) {
- dev_t dev;
- char *secontext = NULL;
-
- auto [mode, uid, gid] = get_device_permissions(path, links);
- mode |= (block ? S_IFBLK : S_IFCHR);
-
- if (sehandle) {
- std::vector<const char*> c_links;
- for (const auto& link : links) {
- c_links.emplace_back(link.c_str());
- }
- c_links.emplace_back(nullptr);
- if (selabel_lookup_best_match(sehandle, &secontext, path.c_str(), &c_links[0], mode)) {
- PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
- return;
- }
- setfscreatecon(secontext);
- }
-
- dev = makedev(major, minor);
- /* Temporarily change egid to avoid race condition setting the gid of the
- * device node. Unforunately changing the euid would prevent creation of
- * some device nodes, so the uid has to be set with chown() and is still
- * racy. Fixing the gid race at least fixed the issue with system_server
- * opening dynamic input devices under the AID_INPUT gid. */
- if (setegid(gid)) {
- PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
- goto out;
- }
- /* If the node already exists update its SELinux label to handle cases when
- * it was created with the wrong context during coldboot procedure. */
- if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
- char* fcon = nullptr;
- int rc = lgetfilecon(path.c_str(), &fcon);
- if (rc < 0) {
- PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
- goto out;
- }
-
- bool different = strcmp(fcon, secontext) != 0;
- freecon(fcon);
-
- if (different && lsetfilecon(path.c_str(), secontext)) {
- PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path << "' device";
- }
- }
-
-out:
- chown(path.c_str(), uid, -1);
- if (setegid(AID_ROOT)) {
- PLOG(FATAL) << "setegid(AID_ROOT) failed";
- }
-
- if (secontext) {
- freecon(secontext);
- setfscreatecon(NULL);
- }
-}
-
-// TODO: Move this to be a member variable of a future devices class.
-std::vector<std::string> platform_devices;
-
-// Given a path that may start with a platform device, find the length of the
-// platform device prefix. If it doesn't start with a platform device, return false
-bool find_platform_device(const std::string& path, std::string* out_path) {
- out_path->clear();
- // platform_devices is searched backwards, since parents are added before their children,
- // and we want to match as deep of a child as we can.
- for (auto it = platform_devices.rbegin(); it != platform_devices.rend(); ++it) {
- auto platform_device_path_length = it->length();
- if (platform_device_path_length < path.length() &&
- path[platform_device_path_length] == '/' &&
- android::base::StartsWith(path, it->c_str())) {
- *out_path = *it;
- return true;
- }
- }
- return false;
-}
+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 find_pci_device_prefix(const std::string& path, std::string* result) {
+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;
@@ -385,10 +81,10 @@
* the supplied buffer with the virtual block device ID and return 0.
* If it doesn't start with a virtual block device, or there is some
* error, return -1 */
-static bool find_vbd_device_prefix(const std::string& path, std::string* result) {
+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;
@@ -405,95 +101,187 @@
return true;
}
-void parse_event(const char* msg, uevent* uevent) {
- uevent->partition_num = -1;
- uevent->major = -1;
- uevent->minor = -1;
- // currently ignoring SEQNUM
- while(*msg) {
- if(!strncmp(msg, "ACTION=", 7)) {
- msg += 7;
- uevent->action = msg;
- } else if(!strncmp(msg, "DEVPATH=", 8)) {
- msg += 8;
- uevent->path = msg;
- } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
- msg += 10;
- uevent->subsystem = msg;
- } else if(!strncmp(msg, "FIRMWARE=", 9)) {
- msg += 9;
- uevent->firmware = msg;
- } else if(!strncmp(msg, "MAJOR=", 6)) {
- msg += 6;
- uevent->major = atoi(msg);
- } else if(!strncmp(msg, "MINOR=", 6)) {
- msg += 6;
- uevent->minor = atoi(msg);
- } else if(!strncmp(msg, "PARTN=", 6)) {
- msg += 6;
- uevent->partition_num = atoi(msg);
- } else if(!strncmp(msg, "PARTNAME=", 9)) {
- msg += 9;
- uevent->partition_name = msg;
- } else if(!strncmp(msg, "DEVNAME=", 8)) {
- msg += 8;
- uevent->device_name = msg;
+Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
+ : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
+ // Set 'prefix_' or 'wildcard_' based on the below cases:
+ //
+ // 1) No '*' in 'name' -> Neither are set and Match() checks a given path for strict
+ // equality with 'name'
+ //
+ // 2) '*' only appears as the last character in 'name' -> 'prefix'_ is set to true and
+ // Match() checks if 'name' is a prefix of a given path.
+ //
+ // 3) '*' appears elsewhere -> 'wildcard_' is set to true and Match() uses fnmatch()
+ // with FNM_PATHNAME to compare 'name' to a given path.
+
+ auto wildcard_position = name_.find('*');
+ if (wildcard_position != std::string::npos) {
+ if (wildcard_position == name_.length() - 1) {
+ prefix_ = true;
+ name_.pop_back();
+ } else {
+ wildcard_ = true;
}
-
- // advance to after the next \0
- while(*msg++)
- ;
- }
-
- if (LOG_UEVENTS) {
- LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
- << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
- << ", " << uevent->minor << " }";
}
}
-std::vector<std::string> get_character_device_symlinks(uevent* uevent) {
- std::string parent_device;
- if (!find_platform_device(uevent->path, &parent_device)) return {};
+bool Permissions::Match(const std::string& path) const {
+ if (prefix_) return StartsWith(path, name_.c_str());
+ if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
+ return path == name_;
+}
- // skip path to the parent driver
- std::string path = uevent->path.substr(parent_device.length());
+bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
+ const std::string& subsystem) const {
+ std::string path_basename = Basename(path);
+ if (name().find(subsystem) != std::string::npos) {
+ if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
+ if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
+ }
+ return Match(path);
+}
- if (!android::base::StartsWith(path, "/usb")) return {};
+void SysfsPermissions::SetPermissions(const std::string& path) const {
+ std::string attribute_file = path + "/" + attribute_;
+ LOG(VERBOSE) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
+ << perm();
- // skip root hub name and device. use device interface
- // skip 3 slashes, including the first / by starting the search at the 1st character, not 0th.
- // then extract what comes between the 3rd and 4th slash
- // e.g. "/usb/usb_device/name/tty2-1:1.0" -> "name"
+ if (access(attribute_file.c_str(), F_OK) == 0) {
+ if (chown(attribute_file.c_str(), uid(), gid()) != 0) {
+ PLOG(ERROR) << "chown(" << attribute_file << ", " << uid() << ", " << gid()
+ << ") failed";
+ }
+ if (chmod(attribute_file.c_str(), perm()) != 0) {
+ PLOG(ERROR) << "chmod(" << attribute_file << ", " << perm() << ") failed";
+ }
+ }
+}
- std::string::size_type start = 0;
- start = path.find('/', start + 1);
- if (start == std::string::npos) return {};
+// Given a path that may start with a platform device, find the parent platform device by finding a
+// parent directory with a 'subsystem' symlink that points to the platform bus.
+// If it doesn't start with a platform device, return false
+bool DeviceHandler::FindPlatformDevice(std::string path, std::string* platform_device_path) const {
+ platform_device_path->clear();
- start = path.find('/', start + 1);
- if (start == std::string::npos) return {};
+ // Uevents don't contain the mount point, so we need to add it here.
+ path.insert(0, sysfs_mount_point_);
- auto end = path.find('/', start + 1);
- if (end == std::string::npos) return {};
+ std::string directory = Dirname(path);
- start++; // Skip the first '/'
+ while (directory != "/" && directory != ".") {
+ std::string subsystem_link_path;
+ if (Realpath(directory + "/subsystem", &subsystem_link_path) &&
+ subsystem_link_path == sysfs_mount_point_ + "/bus/platform") {
+ // We need to remove the mount point that we added above before returning.
+ directory.erase(0, sysfs_mount_point_.size());
+ *platform_device_path = directory;
+ return true;
+ }
- auto length = end - start;
- if (length == 0) return {};
+ auto last_slash = path.rfind('/');
+ if (last_slash == std::string::npos) return false;
- auto name_string = path.substr(start, length);
+ path.erase(last_slash);
+ directory = Dirname(path);
+ }
- std::vector<std::string> links;
- links.emplace_back("/dev/usb/" + uevent->subsystem + name_string);
+ return false;
+}
- mkdir("/dev/usb", 0755);
+void DeviceHandler::FixupSysPermissions(const std::string& upath,
+ const std::string& subsystem) const {
+ // upaths omit the "/sys" that paths in this list
+ // contain, so we prepend it...
+ std::string path = "/sys" + upath;
- return links;
+ for (const auto& s : sysfs_permissions_) {
+ if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
+ }
+
+ if (!skip_restorecon_ && access(path.c_str(), F_OK) == 0) {
+ LOG(VERBOSE) << "restorecon_recursive: " << path;
+ if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+ PLOG(ERROR) << "selinux_android_restorecon(" << path << ") failed";
+ }
+ }
+}
+
+std::tuple<mode_t, uid_t, gid_t> DeviceHandler::GetDevicePermissions(
+ const std::string& path, const std::vector<std::string>& links) const {
+ // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
+ for (auto it = dev_permissions_.crbegin(); it != dev_permissions_.crend(); ++it) {
+ if (it->Match(path) || std::any_of(links.cbegin(), links.cend(),
+ [it](const auto& link) { return it->Match(link); })) {
+ return {it->perm(), it->uid(), it->gid()};
+ }
+ }
+ /* Default if nothing found. */
+ return {0600, 0, 0};
+}
+
+void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, int minor,
+ const std::vector<std::string>& links) const {
+ auto[mode, uid, gid] = GetDevicePermissions(path, links);
+ mode |= (block ? S_IFBLK : S_IFCHR);
+
+ char* secontext = nullptr;
+ if (sehandle_) {
+ std::vector<const char*> c_links;
+ for (const auto& link : links) {
+ c_links.emplace_back(link.c_str());
+ }
+ c_links.emplace_back(nullptr);
+ if (selabel_lookup_best_match(sehandle_, &secontext, path.c_str(), &c_links[0], mode)) {
+ PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
+ return;
+ }
+ setfscreatecon(secontext);
+ }
+
+ dev_t dev = makedev(major, minor);
+ /* Temporarily change egid to avoid race condition setting the gid of the
+ * device node. Unforunately changing the euid would prevent creation of
+ * some device nodes, so the uid has to be set with chown() and is still
+ * racy. Fixing the gid race at least fixed the issue with system_server
+ * opening dynamic input devices under the AID_INPUT gid. */
+ if (setegid(gid)) {
+ PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
+ goto out;
+ }
+ /* If the node already exists update its SELinux label to handle cases when
+ * it was created with the wrong context during coldboot procedure. */
+ if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
+ char* fcon = nullptr;
+ int rc = lgetfilecon(path.c_str(), &fcon);
+ if (rc < 0) {
+ PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
+ goto out;
+ }
+
+ bool different = strcmp(fcon, secontext) != 0;
+ freecon(fcon);
+
+ if (different && lsetfilecon(path.c_str(), secontext)) {
+ PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
+ << "' device";
+ }
+ }
+
+out:
+ chown(path.c_str(), uid, -1);
+ if (setegid(AID_ROOT)) {
+ PLOG(FATAL) << "setegid(AID_ROOT) failed";
+ }
+
+ if (secontext) {
+ freecon(secontext);
+ setfscreatecon(nullptr);
+ }
}
// replaces any unacceptable characters with '_', the
// length of the resulting string is equal to the input string
-void sanitize_partition_name(std::string* string) {
+void SanitizePartitionName(std::string* string) {
const char* accept =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -508,25 +296,25 @@
}
}
-std::vector<std::string> get_block_device_symlinks(uevent* uevent) {
+std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
std::string device;
std::string type;
- if (find_platform_device(uevent->path, &device)) {
+ if (FindPlatformDevice(uevent.path, &device)) {
// Skip /devices/platform or /devices/ if present
static const std::string devices_platform_prefix = "/devices/platform/";
static const std::string devices_prefix = "/devices/";
- 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());
}
type = "platform";
- } else if (find_pci_device_prefix(uevent->path, &device)) {
+ } else if (FindPciDevicePrefix(uevent.path, &device)) {
type = "pci";
- } else if (find_vbd_device_prefix(uevent->path, &device)) {
+ } else if (FindVbdDevicePrefix(uevent.path, &device)) {
type = "vbd";
} else {
return {};
@@ -538,379 +326,113 @@
auto link_path = "/dev/block/" + type + "/" + device;
- if (!uevent->partition_name.empty()) {
- std::string partition_name_sanitized(uevent->partition_name);
- sanitize_partition_name(&partition_name_sanitized);
- if (partition_name_sanitized != uevent->partition_name) {
- LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '"
+ if (!uevent.partition_name.empty()) {
+ std::string partition_name_sanitized(uevent.partition_name);
+ SanitizePartitionName(&partition_name_sanitized);
+ if (partition_name_sanitized != uevent.partition_name) {
+ LOG(VERBOSE) << "Linking partition '" << uevent.partition_name << "' as '"
<< partition_name_sanitized << "'";
}
links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
}
- if (uevent->partition_num >= 0) {
- links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent->partition_num));
+ if (uevent.partition_num >= 0) {
+ links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent.partition_num));
}
- auto last_slash = uevent->path.rfind('/');
- links.emplace_back(link_path + "/" + uevent->path.substr(last_slash + 1));
+ auto last_slash = uevent.path.rfind('/');
+ links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1));
return links;
}
-static void make_link_init(const std::string& oldpath, const std::string& newpath) {
- if (mkdir_recursive(dirname(newpath.c_str()), 0755, sehandle)) {
- PLOG(ERROR) << "Failed to create directory " << dirname(newpath.c_str());
- }
-
- if (symlink(oldpath.c_str(), newpath.c_str()) && errno != EEXIST) {
- PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
- }
-}
-
-static void remove_link(const std::string& oldpath, const std::string& newpath) {
- std::string path;
- if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath.c_str());
-}
-
-static void handle_device(const std::string& action, const std::string& devpath, int block,
- int major, int minor, const std::vector<std::string>& links) {
+void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,
+ int major, int minor, const std::vector<std::string>& links) const {
if (action == "add") {
- make_device(devpath, block, major, minor, links);
+ MakeDevice(devpath, block, major, minor, links);
for (const auto& link : links) {
- make_link_init(devpath, 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) {
+ PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+ }
}
}
if (action == "remove") {
for (const auto& link : links) {
- remove_link(devpath, link);
+ std::string link_path;
+ if (Readlink(link, &link_path) && link_path == devpath) {
+ unlink(link.c_str());
+ }
}
unlink(devpath.c_str());
}
}
-void handle_platform_device_event(uevent* uevent) {
- if (uevent->action == "add") {
- platform_devices.emplace_back(uevent->path);
- } else if (uevent->action == "remove") {
- auto it = std::find(platform_devices.begin(), platform_devices.end(), uevent->path);
- if (it != platform_devices.end()) platform_devices.erase(it);
- }
-}
-
-static void handle_block_device_event(uevent* uevent) {
- // if it's not a /dev device, nothing to do
- if (uevent->major < 0 || uevent->minor < 0) return;
-
- const char* base = "/dev/block/";
- make_dir(base, 0755, sehandle);
-
- std::string name = android::base::Basename(uevent->path);
- std::string devpath = base + name;
-
- std::vector<std::string> links;
- if (android::base::StartsWith(uevent->path, "/devices")) {
- links = get_block_device_symlinks(uevent);
+void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
+ if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
+ FixupSysPermissions(uevent.path, uevent.subsystem);
}
- handle_device(uevent->action, devpath, 1, uevent->major, uevent->minor, links);
-}
-
-static void handle_generic_device_event(uevent* uevent) {
// if it's not a /dev device, nothing to do
- if (uevent->major < 0 || uevent->minor < 0) return;
+ if (uevent.major < 0 || uevent.minor < 0) return;
std::string devpath;
+ std::vector<std::string> links;
+ bool block = false;
- if (android::base::StartsWith(uevent->subsystem, "usb")) {
- if (uevent->subsystem == "usb") {
- if (!uevent->device_name.empty()) {
- devpath = "/dev/" + uevent->device_name;
+ if (uevent.subsystem == "block") {
+ block = true;
+ devpath = "/dev/block/" + Basename(uevent.path);
+
+ if (StartsWith(uevent.path, "/devices")) {
+ links = GetBlockDeviceSymlinks(uevent);
+ }
+ } else if (StartsWith(uevent.subsystem, "usb")) {
+ if (uevent.subsystem == "usb") {
+ if (!uevent.device_name.empty()) {
+ devpath = "/dev/" + uevent.device_name;
} else {
// This imitates the file system that would be created
// if we were using devfs instead.
// Minors are broken up into groups of 128, starting at "001"
- int bus_id = uevent->minor / 128 + 1;
- int device_id = uevent->minor % 128 + 1;
- devpath = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
+ int bus_id = uevent.minor / 128 + 1;
+ int device_id = uevent.minor % 128 + 1;
+ devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
}
} else {
// ignore other USB events
return;
}
- } else if (auto subsystem = std::find(subsystems.begin(), subsystems.end(), uevent->subsystem);
- subsystem != subsystems.end()) {
+ } else if (const auto subsystem =
+ std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
+ 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 = get_character_device_symlinks(uevent);
-
- handle_device(uevent->action, devpath, 0, uevent->major, uevent->minor, links);
+ HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
}
-static void handle_device_event(struct uevent *uevent)
-{
- if (uevent->action == "add" || uevent->action == "change" || uevent->action == "online") {
- fixup_sys_permissions(uevent->path, uevent->subsystem);
- }
+DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
+ std::vector<SysfsPermissions> sysfs_permissions,
+ std::vector<Subsystem> subsystems, bool skip_restorecon)
+ : dev_permissions_(std::move(dev_permissions)),
+ sysfs_permissions_(std::move(sysfs_permissions)),
+ subsystems_(std::move(subsystems)),
+ sehandle_(selinux_android_file_context_handle()),
+ skip_restorecon_(skip_restorecon),
+ sysfs_mount_point_("/sys") {}
- if (uevent->subsystem == "block") {
- handle_block_device_event(uevent);
- } else if (uevent->subsystem == "platform") {
- handle_platform_device_event(uevent);
- } else {
- handle_generic_device_event(uevent);
- }
-}
+DeviceHandler::DeviceHandler()
+ : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
+ std::vector<Subsystem>{}, false) {}
-static void load_firmware(uevent* uevent, const std::string& root,
- int fw_fd, size_t fw_size,
- int loading_fd, int data_fd) {
- // Start transfer.
- android::base::WriteFully(loading_fd, "1", 1);
-
- // Copy the firmware.
- int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
- if (rc == -1) {
- PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent->firmware << "' }";
- }
-
- // Tell the firmware whether to abort or commit.
- const char* response = (rc != -1) ? "0" : "-1";
- android::base::WriteFully(loading_fd, response, strlen(response));
-}
-
-static int is_booting() {
- return access("/dev/.booting", F_OK) == 0;
-}
-
-static void process_firmware_event(uevent* uevent) {
- int booting = is_booting();
-
- LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
-
- std::string root = "/sys" + uevent->path;
- std::string loading = root + "/loading";
- std::string data = root + "/data";
-
- android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY|O_CLOEXEC));
- if (loading_fd == -1) {
- PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent->firmware;
- return;
- }
-
- android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY|O_CLOEXEC));
- if (data_fd == -1) {
- PLOG(ERROR) << "couldn't open firmware data fd for " << uevent->firmware;
- return;
- }
-
- static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
- "/firmware/image/"};
-
-try_loading_again:
- for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
- std::string file = firmware_dirs[i] + uevent->firmware;
- android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY|O_CLOEXEC));
- struct stat sb;
- if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
- load_firmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
- return;
- }
- }
-
- if (booting) {
- // If we're not fully booted, we may be missing
- // filesystems needed for firmware, wait and retry.
- std::this_thread::sleep_for(100ms);
- booting = is_booting();
- goto try_loading_again;
- }
-
- LOG(ERROR) << "firmware: could not find firmware for " << uevent->firmware;
-
- // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
- write(loading_fd, "-1", 2);
-}
-
-static void handle_firmware_event(uevent* uevent) {
- if (uevent->subsystem != "firmware" || uevent->action != "add") return;
-
- // Loading the firmware in a child means we can do that in parallel...
- // (We ignore SIGCHLD rather than wait for our children.)
- pid_t pid = fork();
- if (pid == 0) {
- Timer t;
- process_firmware_event(uevent);
- LOG(INFO) << "loading " << uevent->path << " took " << t;
- _exit(EXIT_SUCCESS);
- } else if (pid == -1) {
- PLOG(ERROR) << "could not fork to process firmware event for " << uevent->firmware;
- }
-}
-
-static bool inline should_stop_coldboot(coldboot_action_t act)
-{
- return (act == COLDBOOT_STOP || act == COLDBOOT_FINISH);
-}
-
-#define UEVENT_MSG_LEN 2048
-
-static inline coldboot_action_t handle_device_fd_with(
- std::function<coldboot_action_t(uevent* uevent)> handle_uevent)
-{
- char msg[UEVENT_MSG_LEN+2];
- int n;
- while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
- if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
- continue;
-
- msg[n] = '\0';
- msg[n+1] = '\0';
-
- uevent uevent;
- parse_event(msg, &uevent);
- coldboot_action_t act = handle_uevent(&uevent);
- if (should_stop_coldboot(act))
- return act;
- }
-
- return COLDBOOT_CONTINUE;
-}
-
-coldboot_action_t handle_device_fd(coldboot_callback fn)
-{
- coldboot_action_t ret = handle_device_fd_with(
- [&](uevent* uevent) -> coldboot_action_t {
- // default is to always create the devices
- coldboot_action_t act = COLDBOOT_CREATE;
- if (fn) {
- act = fn(uevent);
- }
-
- if (act == COLDBOOT_CREATE || act == COLDBOOT_STOP) {
- handle_device_event(uevent);
- handle_firmware_event(uevent);
- }
-
- return act;
- });
-
- return ret;
-}
-
-/* Coldboot walks parts of the /sys tree and pokes the uevent files
-** to cause the kernel to regenerate device add events that happened
-** before init's device manager was started
-**
-** We drain any pending events from the netlink socket every time
-** we poke another uevent file to make sure we don't overrun the
-** socket's buffer.
-*/
-
-static coldboot_action_t do_coldboot(DIR *d, coldboot_callback fn)
-{
- struct dirent *de;
- int dfd, fd;
- coldboot_action_t act = COLDBOOT_CONTINUE;
-
- dfd = dirfd(d);
-
- fd = openat(dfd, "uevent", O_WRONLY);
- if (fd >= 0) {
- write(fd, "add\n", 4);
- close(fd);
- act = handle_device_fd(fn);
- if (should_stop_coldboot(act))
- return act;
- }
-
- while (!should_stop_coldboot(act) && (de = readdir(d))) {
- DIR *d2;
-
- if(de->d_type != DT_DIR || de->d_name[0] == '.')
- continue;
-
- fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
- if(fd < 0)
- continue;
-
- d2 = fdopendir(fd);
- if(d2 == 0)
- close(fd);
- else {
- act = do_coldboot(d2, fn);
- closedir(d2);
- }
- }
-
- // default is always to continue looking for uevents
- return act;
-}
-
-static coldboot_action_t coldboot(const char *path, coldboot_callback fn)
-{
- std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
- if (d) {
- return do_coldboot(d.get(), fn);
- }
-
- return COLDBOOT_CONTINUE;
-}
-
-void device_init(const char* path, coldboot_callback fn) {
- if (!sehandle) {
- sehandle = selinux_android_file_context_handle();
- }
- // open uevent socket and selinux status only if it hasn't been
- // done before
- if (device_fd == -1) {
- /* is 256K enough? udev uses 16MB! */
- device_fd.reset(uevent_open_socket(256 * 1024, true));
- if (device_fd == -1) {
- return;
- }
- fcntl(device_fd, F_SETFL, O_NONBLOCK);
- }
-
- if (access(COLDBOOT_DONE, F_OK) == 0) {
- LOG(VERBOSE) << "Skipping coldboot, already done!";
- return;
- }
-
- Timer t;
- coldboot_action_t act;
- if (!path) {
- act = coldboot("/sys/class", fn);
- if (!should_stop_coldboot(act)) {
- act = coldboot("/sys/block", fn);
- if (!should_stop_coldboot(act)) {
- act = coldboot("/sys/devices", fn);
- }
- }
- } else {
- act = coldboot(path, fn);
- }
-
- // If we have a callback, then do as it says. If no, then the default is
- // to always create COLDBOOT_DONE file.
- if (!fn || (act == COLDBOOT_FINISH)) {
- close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
- }
-
- LOG(INFO) << "Coldboot took " << t;
-}
-
-void device_close() {
- platform_devices.clear();
- device_fd.reset();
-}
-
-int get_device_fd() {
- return device_fd;
-}
+} // namespace init
+} // namespace android
diff --git a/init/devices.h b/init/devices.h
index 647b4c4..c64f5fb 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -20,35 +20,17 @@
#include <sys/stat.h>
#include <sys/types.h>
-#include <functional>
+#include <algorithm>
#include <string>
#include <vector>
-#include "init_parser.h"
+#include <android-base/file.h>
+#include <selinux/label.h>
-enum coldboot_action_t {
- // coldboot continues without creating the device for the uevent
- COLDBOOT_CONTINUE = 0,
- // coldboot continues after creating the device for the uevent
- COLDBOOT_CREATE,
- // coldboot stops after creating the device for uevent but doesn't
- // create the COLDBOOT_DONE file
- COLDBOOT_STOP,
- // same as COLDBOOT_STOP, but creates the COLDBOOT_DONE file
- COLDBOOT_FINISH
-};
+#include "uevent.h"
-struct uevent {
- std::string action;
- std::string path;
- std::string subsystem;
- std::string firmware;
- std::string partition_name;
- std::string device_name;
- int partition_num;
- int major;
- int minor;
-};
+namespace android {
+namespace init {
class Permissions {
public:
@@ -93,9 +75,15 @@
// Returns the full path for a uevent of a device that is a member of this subsystem,
// according to the rules parsed from ueventd.rc
- std::string ParseDevPath(uevent* uevent) const;
+ std::string ParseDevPath(const Uevent& uevent) const {
+ std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
+ ? uevent.device_name
+ : android::base::Basename(uevent.path);
- bool operator==(const std::string& string_name) { return name_ == string_name; }
+ return dir_name_ + "/" + devname;
+ }
+
+ bool operator==(const std::string& string_name) const { return name_ == string_name; }
private:
enum class DevnameSource {
@@ -108,34 +96,43 @@
DevnameSource devname_source_;
};
-class SubsystemParser : public SectionParser {
+class DeviceHandler {
public:
- SubsystemParser() {}
- bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
- std::string* err) override;
- bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
- void EndSection() override;
+ friend class DeviceHandlerTester;
+
+ DeviceHandler();
+ DeviceHandler(std::vector<Permissions> dev_permissions,
+ std::vector<SysfsPermissions> sysfs_permissions,
+ std::vector<Subsystem> subsystems, bool skip_restorecon);
+ ~DeviceHandler(){};
+
+ void HandleDeviceEvent(const Uevent& uevent);
+
+ std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
+ void set_skip_restorecon(bool value) { skip_restorecon_ = value; }
private:
- bool ParseDevName(std::vector<std::string>&& args, std::string* err);
- bool ParseDirName(std::vector<std::string>&& args, std::string* err);
+ bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
+ std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
+ const std::string& path, const std::vector<std::string>& links) const;
+ void MakeDevice(const std::string& path, bool block, int major, int minor,
+ const std::vector<std::string>& links) const;
+ void HandleDevice(const std::string& action, const std::string& devpath, bool block, int major,
+ int minor, const std::vector<std::string>& links) const;
+ void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
- Subsystem subsystem_;
+ std::vector<Permissions> dev_permissions_;
+ std::vector<SysfsPermissions> sysfs_permissions_;
+ std::vector<Subsystem> subsystems_;
+ selabel_handle* sehandle_;
+ bool skip_restorecon_;
+ std::string sysfs_mount_point_;
};
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs);
-typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback;
-extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr);
-extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr);
-extern void device_close();
-int get_device_fd();
-
// Exposed for testing
-extern std::vector<std::string> platform_devices;
-bool find_platform_device(const std::string& path, std::string* out_path);
-std::vector<std::string> get_character_device_symlinks(uevent* uevent);
-std::vector<std::string> get_block_device_symlinks(uevent* uevent);
-void sanitize_partition_name(std::string* string);
-void handle_platform_device_event(uevent* uevent);
+void SanitizePartitionName(std::string* string);
-#endif /* _INIT_DEVICES_H */
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index 66521db..ac4ab9b 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -16,173 +16,68 @@
#include "devices.h"
-#include <string>
-#include <vector>
-
#include <android-base/scopeguard.h>
+#include <android-base/test_utils.h>
#include <gtest/gtest.h>
-void add_platform_device(const std::string& path) {
- uevent uevent = {
- .action = "add", .subsystem = "platform", .path = path,
- };
- handle_platform_device_event(&uevent);
-}
+#include "util.h"
-void remove_platform_device(const std::string& path) {
- uevent uevent = {
- .action = "remove", .subsystem = "platform", .path = path,
- };
- handle_platform_device_event(&uevent);
-}
+using namespace std::string_literals;
-template <std::vector<std::string> (*Function)(uevent*)>
-void test_get_symlinks(const std::string& platform_device_name, uevent* uevent,
- const std::vector<std::string> expected_links) {
- add_platform_device(platform_device_name);
- auto platform_device_remover = android::base::make_scope_guard(
- [&platform_device_name]() { remove_platform_device(platform_device_name); });
+namespace android {
+namespace init {
- std::vector<std::string> result = Function(uevent);
+class DeviceHandlerTester {
+ public:
+ void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent,
+ const std::vector<std::string> expected_links) {
+ TemporaryDir fake_sys_root;
+ device_handler_.sysfs_mount_point_ = fake_sys_root.path;
- auto expected_size = expected_links.size();
- ASSERT_EQ(expected_size, result.size());
- if (expected_size == 0) return;
+ std::string platform_device_dir = fake_sys_root.path + platform_device;
+ mkdir_recursive(platform_device_dir, 0777, nullptr);
- // Explicitly iterate so the results are visible if a failure occurs
- for (unsigned int i = 0; i < expected_size; ++i) {
- EXPECT_EQ(expected_links[i], result[i]);
+ std::string platform_bus = fake_sys_root.path + "/bus/platform"s;
+ mkdir_recursive(platform_bus, 0777, nullptr);
+ symlink(platform_bus.c_str(), (platform_device_dir + "/subsystem").c_str());
+
+ mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777, nullptr);
+
+ std::vector<std::string> result;
+ result = device_handler_.GetBlockDeviceSymlinks(uevent);
+
+ auto expected_size = expected_links.size();
+ ASSERT_EQ(expected_size, result.size());
+ if (expected_size == 0) return;
+
+ // Explicitly iterate so the results are visible if a failure occurs
+ for (unsigned int i = 0; i < expected_size; ++i) {
+ EXPECT_EQ(expected_links[i], result[i]);
+ }
}
-}
-TEST(devices, handle_platform_device_event) {
- platform_devices.clear();
- add_platform_device("/devices/platform/some_device_name");
- ASSERT_EQ(1U, platform_devices.size());
- remove_platform_device("/devices/platform/some_device_name");
- ASSERT_EQ(0U, platform_devices.size());
-}
+ private:
+ DeviceHandler device_handler_;
+};
-TEST(devices, find_platform_device) {
- platform_devices.clear();
- add_platform_device("/devices/platform/some_device_name");
- add_platform_device("/devices/platform/some_device_name/longer");
- add_platform_device("/devices/platform/other_device_name");
- EXPECT_EQ(3U, platform_devices.size());
-
- std::string out_path;
- EXPECT_FALSE(find_platform_device("/devices/platform/not_found", &out_path));
- EXPECT_EQ("", out_path);
-
- EXPECT_FALSE(
- find_platform_device("/devices/platform/some_device_name_with_same_prefix", &out_path));
-
- EXPECT_TRUE(
- find_platform_device("/devices/platform/some_device_name/longer/longer_child", &out_path));
- EXPECT_EQ("/devices/platform/some_device_name/longer", out_path);
-
- EXPECT_TRUE(find_platform_device("/devices/platform/some_device_name/other_child", &out_path));
- EXPECT_EQ("/devices/platform/some_device_name", out_path);
-}
-
-TEST(devices, get_character_device_symlinks_success) {
- const char* platform_device = "/devices/platform/some_device_name";
- uevent uevent = {
- .path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
- .subsystem = "tty",
- };
- std::vector<std::string> expected_result{"/dev/usb/ttyname"};
-
- test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_no_pdev_match) {
- const char* platform_device = "/devices/platform/some_device_name";
- uevent uevent = {
- .path = "/device/name/tty2-1:1.0", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_nothing_after_platform_device) {
- const char* platform_device = "/devices/platform/some_device_name";
- uevent uevent = {
- .path = "/devices/platform/some_device_name", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_no_usb_found) {
- const char* platform_device = "/devices/platform/some_device_name";
- uevent uevent = {
- .path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_no_roothub) {
- const char* platform_device = "/devices/platform/some_device_name";
- uevent uevent = {
- .path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_no_usb_device) {
- const char* platform_device = "/devices/platform/some_device_name";
- uevent uevent = {
- .path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_no_final_slash) {
- const char* platform_device = "/devices/platform/some_device_name";
- uevent uevent = {
- .path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_character_device_symlinks_no_final_name) {
- const char* platform_device = "/devices/platform/some_device_name";
- uevent uevent = {
- .path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
-}
-
-TEST(devices, get_block_device_symlinks_success_platform) {
+TEST(device_handler, get_block_device_symlinks_success_platform) {
// These are actual paths from bullhead
const char* platform_device = "/devices/soc.0/f9824900.sdhci";
- uevent uevent = {
+ Uevent uevent = {
.path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0",
.partition_name = "",
.partition_num = -1,
};
std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
- test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
-TEST(devices, get_block_device_symlinks_success_platform_with_partition) {
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition) {
// These are actual paths from bullhead
const char* platform_device = "/devices/soc.0/f9824900.sdhci";
- uevent uevent = {
+ Uevent uevent = {
.path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
.partition_name = "modem",
.partition_num = 1,
@@ -193,12 +88,13 @@
"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
};
- test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
-TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_num) {
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_num) {
const char* platform_device = "/devices/soc.0/f9824900.sdhci";
- uevent uevent = {
+ Uevent uevent = {
.path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
.partition_name = "",
.partition_num = 1,
@@ -208,12 +104,13 @@
"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
};
- test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
-TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_name) {
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_name) {
const char* platform_device = "/devices/soc.0/f9824900.sdhci";
- uevent uevent = {
+ Uevent uevent = {
.path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
.partition_name = "modem",
.partition_num = -1,
@@ -223,101 +120,107 @@
"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
};
- test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
-TEST(devices, get_block_device_symlinks_success_pci) {
+TEST(device_handler, get_block_device_symlinks_success_pci) {
const char* platform_device = "/devices/do/not/match";
- uevent uevent = {
+ Uevent uevent = {
.path = "/devices/pci0000:00/0000:00:1f.2/mmcblk0", .partition_name = "", .partition_num = -1,
};
std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
- test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
-TEST(devices, get_block_device_symlinks_pci_bad_format) {
+TEST(device_handler, get_block_device_symlinks_pci_bad_format) {
const char* platform_device = "/devices/do/not/match";
- uevent uevent = {
+ Uevent uevent = {
.path = "/devices/pci//mmcblk0", .partition_name = "", .partition_num = -1,
};
std::vector<std::string> expected_result{};
- test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
-TEST(devices, get_block_device_symlinks_success_vbd) {
+TEST(device_handler, get_block_device_symlinks_success_vbd) {
const char* platform_device = "/devices/do/not/match";
- uevent uevent = {
+ Uevent uevent = {
.path = "/devices/vbd-1234/mmcblk0", .partition_name = "", .partition_num = -1,
};
std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
- test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
-TEST(devices, get_block_device_symlinks_vbd_bad_format) {
+TEST(device_handler, get_block_device_symlinks_vbd_bad_format) {
const char* platform_device = "/devices/do/not/match";
- uevent uevent = {
+ Uevent uevent = {
.path = "/devices/vbd-/mmcblk0", .partition_name = "", .partition_num = -1,
};
std::vector<std::string> expected_result{};
- test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
-TEST(devices, get_block_device_symlinks_no_matches) {
+TEST(device_handler, get_block_device_symlinks_no_matches) {
const char* platform_device = "/devices/soc.0/f9824900.sdhci";
- uevent uevent = {
+ Uevent uevent = {
.path = "/devices/soc.0/not_the_device/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
.partition_name = "",
.partition_num = -1,
};
std::vector<std::string> expected_result;
- test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+ DeviceHandlerTester device_handler_tester_;
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
-TEST(devices, sanitize_null) {
- sanitize_partition_name(nullptr);
+TEST(device_handler, sanitize_null) {
+ SanitizePartitionName(nullptr);
}
-TEST(devices, sanitize_empty) {
+TEST(device_handler, sanitize_empty) {
std::string empty;
- sanitize_partition_name(&empty);
+ SanitizePartitionName(&empty);
EXPECT_EQ(0u, empty.size());
}
-TEST(devices, sanitize_allgood) {
+TEST(device_handler, sanitize_allgood) {
std::string good =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789"
"_-.";
std::string good_copy = good;
- sanitize_partition_name(&good);
+ SanitizePartitionName(&good);
EXPECT_EQ(good_copy, good);
}
-TEST(devices, sanitize_somebad) {
+TEST(device_handler, sanitize_somebad) {
std::string string = "abc!@#$%^&*()";
- sanitize_partition_name(&string);
+ SanitizePartitionName(&string);
EXPECT_EQ("abc__________", string);
}
-TEST(devices, sanitize_allbad) {
+TEST(device_handler, sanitize_allbad) {
std::string string = "!@#$%^&*()";
- sanitize_partition_name(&string);
+ SanitizePartitionName(&string);
EXPECT_EQ("__________", string);
}
-TEST(devices, sanitize_onebad) {
+TEST(device_handler, sanitize_onebad) {
std::string string = ")";
- sanitize_partition_name(&string);
+ SanitizePartitionName(&string);
EXPECT_EQ("_", string);
}
-TEST(devices, DevPermissionsMatchNormal) {
+TEST(device_handler, DevPermissionsMatchNormal) {
// Basic from ueventd.rc
// /dev/null 0666 root root
Permissions permissions("/dev/null", 0666, 0, 0);
@@ -329,7 +232,7 @@
EXPECT_EQ(0U, permissions.gid());
}
-TEST(devices, DevPermissionsMatchPrefix) {
+TEST(device_handler, DevPermissionsMatchPrefix) {
// Prefix from ueventd.rc
// /dev/dri/* 0666 root graphics
Permissions permissions("/dev/dri/*", 0666, 0, 1000);
@@ -342,7 +245,7 @@
EXPECT_EQ(1000U, permissions.gid());
}
-TEST(devices, DevPermissionsMatchWildcard) {
+TEST(device_handler, DevPermissionsMatchWildcard) {
// Wildcard example
// /dev/device*name 0666 root graphics
Permissions permissions("/dev/device*name", 0666, 0, 1000);
@@ -356,7 +259,7 @@
EXPECT_EQ(1000U, permissions.gid());
}
-TEST(devices, DevPermissionsMatchWildcardPrefix) {
+TEST(device_handler, DevPermissionsMatchWildcardPrefix) {
// Wildcard+Prefix example
// /dev/device*name* 0666 root graphics
Permissions permissions("/dev/device*name*", 0666, 0, 1000);
@@ -372,7 +275,7 @@
EXPECT_EQ(1000U, permissions.gid());
}
-TEST(devices, SysfsPermissionsMatchWithSubsystemNormal) {
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemNormal) {
// /sys/devices/virtual/input/input* enable 0660 root input
SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001);
EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/input0", "input"));
@@ -382,7 +285,7 @@
EXPECT_EQ(1001U, permissions.gid());
}
-TEST(devices, SysfsPermissionsMatchWithSubsystemClass) {
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemClass) {
// /sys/class/input/event* enable 0660 root input
SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001);
EXPECT_TRUE(permissions.MatchWithSubsystem(
@@ -396,7 +299,7 @@
EXPECT_EQ(1001U, permissions.gid());
}
-TEST(devices, SysfsPermissionsMatchWithSubsystemBus) {
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemBus) {
// /sys/bus/i2c/devices/i2c-* enable 0660 root input
SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001);
EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "i2c"));
@@ -407,3 +310,6 @@
EXPECT_EQ(0U, permissions.uid());
EXPECT_EQ(1001U, permissions.gid());
}
+
+} // namespace init
+} // namespace android
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
new file mode 100644
index 0000000..b686885
--- /dev/null
+++ b/init/firmware_handler.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "firmware_handler.h"
+
+#include <fcntl.h>
+#include <sys/sendfile.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+#include <thread>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+using android::base::Timer;
+using android::base::unique_fd;
+using android::base::WriteFully;
+
+namespace android {
+namespace init {
+
+static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
+ int loading_fd, int data_fd) {
+ // Start transfer.
+ WriteFully(loading_fd, "1", 1);
+
+ // Copy the firmware.
+ int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
+ if (rc == -1) {
+ PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent.firmware
+ << "' }";
+ }
+
+ // Tell the firmware whether to abort or commit.
+ const char* response = (rc != -1) ? "0" : "-1";
+ WriteFully(loading_fd, response, strlen(response));
+}
+
+static bool IsBooting() {
+ return access("/dev/.booting", F_OK) == 0;
+}
+
+static void ProcessFirmwareEvent(const Uevent& uevent) {
+ int booting = IsBooting();
+
+ LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
+
+ std::string root = "/sys" + uevent.path;
+ std::string loading = root + "/loading";
+ std::string data = root + "/data";
+
+ unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
+ if (loading_fd == -1) {
+ PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware;
+ return;
+ }
+
+ unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
+ if (data_fd == -1) {
+ PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware;
+ return;
+ }
+
+ static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
+ "/firmware/image/"};
+
+try_loading_again:
+ for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
+ std::string file = firmware_dirs[i] + uevent.firmware;
+ unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
+ struct stat sb;
+ if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
+ LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+ return;
+ }
+ }
+
+ if (booting) {
+ // If we're not fully booted, we may be missing
+ // filesystems needed for firmware, wait and retry.
+ std::this_thread::sleep_for(100ms);
+ booting = IsBooting();
+ goto try_loading_again;
+ }
+
+ LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
+
+ // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
+ write(loading_fd, "-1", 2);
+}
+
+void HandleFirmwareEvent(const Uevent& uevent) {
+ if (uevent.subsystem != "firmware" || uevent.action != "add") return;
+
+ // Loading the firmware in a child means we can do that in parallel...
+ auto pid = fork();
+ if (pid == -1) {
+ PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
+ }
+ if (pid == 0) {
+ Timer t;
+ ProcessFirmwareEvent(uevent);
+ LOG(INFO) << "loading " << uevent.path << " took " << t;
+ _exit(EXIT_SUCCESS);
+ }
+}
+
+} // namespace init
+} // namespace android
diff --git a/libunwindstack/Log.h b/init/firmware_handler.h
similarity index 66%
copy from libunwindstack/Log.h
copy to init/firmware_handler.h
index 2d01aa8..e456ac4 100644
--- a/libunwindstack/Log.h
+++ b/init/firmware_handler.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,12 +14,17 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
+#ifndef _INIT_FIRMWARE_HANDLER_H
+#define _INIT_FIRMWARE_HANDLER_H
-#include <stdint.h>
+#include "uevent.h"
-void log_to_stdout(bool enable);
-void log(uint8_t indent, const char* format, ...);
+namespace android {
+namespace init {
-#endif // _LIBUNWINDSTACK_LOG_H
+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 8b5d15e..bf251ff 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>
@@ -58,7 +56,6 @@
#include "action.h"
#include "bootchart.h"
-#include "devices.h"
#include "import_parser.h"
#include "init_first_stage.h"
#include "init_parser.h"
@@ -72,9 +69,14 @@
#include "util.h"
#include "watchdogd.h"
+using namespace std::string_literals;
+
using android::base::boot_clock;
using android::base::GetProperty;
-using android::base::StringPrintf;
+using android::base::Timer;
+
+namespace android {
+namespace init {
struct selabel_handle *sehandle;
struct selabel_handle *sehandle_prop;
@@ -93,6 +95,7 @@
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
static std::string wait_prop_name;
static std::string wait_prop_value;
+static bool shutting_down;
void DumpState() {
ServiceManager::GetInstance().DumpState();
@@ -157,21 +160,31 @@
return true;
}
+void ResetWaitForProp() {
+ wait_prop_name.clear();
+ wait_prop_value.clear();
+ waiting_for_prop.reset();
+}
+
void property_changed(const std::string& name, const std::string& value) {
// If the property is sys.powerctl, we bypass the event queue and immediately handle it.
// This is to ensure that init will always and immediately shutdown/reboot, regardless of
// if there are other pending events to process or if init is waiting on an exec service or
// waiting on a property.
- if (name == "sys.powerctl") HandlePowerctlMessage(value);
+ // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
+ // commands to be executed.
+ if (name == "sys.powerctl") {
+ if (HandlePowerctlMessage(value)) {
+ shutting_down = true;
+ }
+ }
if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
if (waiting_for_prop) {
if (wait_prop_name == name && wait_prop_value == value) {
- wait_prop_name.clear();
- wait_prop_value.clear();
LOG(INFO) << "Wait for property took " << *waiting_for_prop;
- waiting_for_prop.reset();
+ ResetWaitForProp();
}
}
}
@@ -220,7 +233,7 @@
panic();
}
- property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration_ms()).c_str());
+ property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
return 0;
}
@@ -450,14 +463,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);
}
}
@@ -488,7 +501,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);
}
}
@@ -512,8 +525,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);
}
}
@@ -859,7 +871,7 @@
}
// init's first stage can't set properties, so pass the time to the second stage.
- setenv("INIT_SELINUX_TOOK", std::to_string(t.duration_ms()).c_str(), 1);
+ setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
} else {
selinux_init_all_handles();
}
@@ -881,7 +893,6 @@
selinux_android_restorecon("/dev/__properties__", 0);
selinux_android_restorecon("/plat_property_contexts", 0);
selinux_android_restorecon("/nonplat_property_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);
@@ -1004,7 +1015,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 };
@@ -1136,7 +1147,7 @@
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || sm.IsWaitingForExec())) {
- restart_processes();
+ if (!shutting_down) restart_processes();
// If there's a process that needs restarting, wake up in time for that.
if (process_needs_restart_at != 0) {
@@ -1159,3 +1170,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..aaab523 100644
--- a/init/init.h
+++ b/init/init.h
@@ -19,6 +19,11 @@
#include <string>
+#include <selinux/label.h>
+
+namespace android {
+namespace init {
+
// Note: These globals are *only* valid in init, so they should not be used in ueventd,
// watchdogd, or any files that may be included in those, such as devices.cpp and util.cpp.
// TODO: Have an Init class and remove all globals.
@@ -39,4 +44,9 @@
void DumpState();
+void ResetWaitForProp();
+
+} // namespace init
+} // namespace android
+
#endif /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 9d2a0d1..0f7e38f 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -19,11 +19,13 @@
#include <stdlib.h>
#include <unistd.h>
+#include <chrono>
#include <memory>
#include <set>
#include <string>
#include <vector>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
@@ -31,8 +33,15 @@
#include "devices.h"
#include "fs_mgr.h"
#include "fs_mgr_avb.h"
+#include "uevent.h"
+#include "uevent_listener.h"
#include "util.h"
+using android::base::Timer;
+
+namespace android {
+namespace init {
+
// Class Declarations
// ------------------
class FirstStageMount {
@@ -47,22 +56,23 @@
bool InitDevices();
protected:
- void InitRequiredDevices();
- void InitVerityDevice(const std::string& verity_device);
+ bool InitRequiredDevices();
+ bool InitVerityDevice(const std::string& verity_device);
bool MountPartitions();
- virtual coldboot_action_t ColdbootCallback(uevent* uevent);
+ virtual ListenerAction UeventCallback(const Uevent& uevent);
// Pure virtual functions.
virtual bool GetRequiredDevices() = 0;
virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
bool need_dm_verity_;
- // Device tree fstab entries.
+
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
- // Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
std::vector<fstab_rec*> mount_fstab_recs_;
std::set<std::string> required_devices_partition_names_;
+ DeviceHandler device_handler_;
+ UeventListener uevent_listener_;
};
class FirstStageMountVBootV1 : public FirstStageMount {
@@ -83,7 +93,7 @@
~FirstStageMountVBootV2() override = default;
protected:
- coldboot_action_t ColdbootCallback(uevent* uevent) override;
+ ListenerAction UeventCallback(const Uevent& uevent) override;
bool GetRequiredDevices() override;
bool SetUpDmVerity(fstab_rec* fstab_rec) override;
bool InitAvbHandle();
@@ -111,12 +121,10 @@
LOG(ERROR) << "Failed to read fstab from device tree";
return;
}
- for (auto mount_point : {"/system", "/vendor", "/odm"}) {
- fstab_rec* fstab_rec =
- fs_mgr_get_entry_for_mount_point(device_tree_fstab_.get(), mount_point);
- if (fstab_rec != nullptr) {
- mount_fstab_recs_.push_back(fstab_rec);
- }
+ // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
+ // for easier manipulation later, e.g., range-base for loop.
+ for (int i = 0; i < device_tree_fstab_->num_entries; i++) {
+ mount_fstab_recs_.push_back(&device_tree_fstab_->recs[i]);
}
}
@@ -140,86 +148,119 @@
}
bool FirstStageMount::InitDevices() {
- if (!GetRequiredDevices()) return false;
-
- InitRequiredDevices();
-
- // InitRequiredDevices() will remove found partitions from required_devices_partition_names_.
- // So if it isn't empty here, it means some partitions are not found.
- if (!required_devices_partition_names_.empty()) {
- LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
- << android::base::Join(required_devices_partition_names_, ", ");
- return false;
- } else {
- return true;
- }
+ return GetRequiredDevices() && InitRequiredDevices();
}
// Creates devices with uevent->partition_name matching one in the member variable
// required_devices_partition_names_. Found partitions will then be removed from it
// for the subsequent member function to check which devices are NOT created.
-void FirstStageMount::InitRequiredDevices() {
+bool FirstStageMount::InitRequiredDevices() {
if (required_devices_partition_names_.empty()) {
- return;
+ return true;
}
if (need_dm_verity_) {
const std::string dm_path = "/devices/virtual/misc/device-mapper";
- device_init(("/sys" + dm_path).c_str(), [&dm_path](uevent* uevent) -> coldboot_action_t {
- if (uevent->path == dm_path) return COLDBOOT_STOP;
- return COLDBOOT_CONTINUE; // dm_path not found, continue to find it.
- });
+ bool found = false;
+ auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
+ if (uevent.path == dm_path) {
+ device_handler_.HandleDeviceEvent(uevent);
+ found = true;
+ return ListenerAction::kStop;
+ }
+ return ListenerAction::kContinue;
+ };
+ uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
+ if (!found) {
+ LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
+ Timer t;
+ uevent_listener_.Poll(dm_callback, 10s);
+ LOG(INFO) << "Wait for device-mapper returned after " << t;
+ }
+ if (!found) {
+ LOG(ERROR) << "device-mapper device not found after polling timeout";
+ return false;
+ }
}
- device_init(nullptr,
- [this](uevent* uevent) -> coldboot_action_t { return ColdbootCallback(uevent); });
+ auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+ uevent_listener_.RegenerateUevents(uevent_callback);
- device_close();
+ // UeventCallback() will remove found partitions from required_devices_partition_names_.
+ // So if it isn't empty here, it means some partitions are not found.
+ if (!required_devices_partition_names_.empty()) {
+ LOG(INFO) << __PRETTY_FUNCTION__
+ << ": partition(s) not found in /sys, waiting for their uevent(s): "
+ << android::base::Join(required_devices_partition_names_, ", ");
+ Timer t;
+ uevent_listener_.Poll(uevent_callback, 10s);
+ LOG(INFO) << "Wait for partitions returned after " << t;
+ }
+
+ if (!required_devices_partition_names_.empty()) {
+ LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
+ << android::base::Join(required_devices_partition_names_, ", ");
+ return false;
+ }
+
+ return true;
}
-coldboot_action_t FirstStageMount::ColdbootCallback(uevent* uevent) {
- // We need platform devices to create symlinks.
- if (uevent->subsystem == "platform") {
- return COLDBOOT_CREATE;
- }
-
+ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent) {
// Ignores everything that is not a block device.
- if (uevent->subsystem != "block") {
- return COLDBOOT_CONTINUE;
+ if (uevent.subsystem != "block") {
+ return ListenerAction::kContinue;
}
- if (!uevent->partition_name.empty()) {
+ if (!uevent.partition_name.empty()) {
// Matches partition name to create device nodes.
// Both required_devices_partition_names_ and uevent->partition_name have A/B
// suffix when A/B is used.
- auto iter = required_devices_partition_names_.find(uevent->partition_name);
+ auto iter = required_devices_partition_names_.find(uevent.partition_name);
if (iter != required_devices_partition_names_.end()) {
- LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
+ LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
required_devices_partition_names_.erase(iter);
+ device_handler_.HandleDeviceEvent(uevent);
if (required_devices_partition_names_.empty()) {
- return COLDBOOT_STOP; // Found all partitions, stop coldboot.
+ return ListenerAction::kStop;
} else {
- return COLDBOOT_CREATE; // Creates this device and continue to find others.
+ return ListenerAction::kContinue;
}
}
}
// Not found a partition or find an unneeded partition, continue to find others.
- return COLDBOOT_CONTINUE;
+ return ListenerAction::kContinue;
}
// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-void FirstStageMount::InitVerityDevice(const std::string& verity_device) {
+bool FirstStageMount::InitVerityDevice(const std::string& verity_device) {
const std::string device_name(basename(verity_device.c_str()));
const std::string syspath = "/sys/block/" + device_name;
+ bool found = false;
- device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
- if (uevent->device_name == device_name) {
+ auto verity_callback = [&device_name, &verity_device, this, &found](const Uevent& uevent) {
+ if (uevent.device_name == device_name) {
LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
- return COLDBOOT_STOP;
+ device_handler_.HandleDeviceEvent(uevent);
+ found = true;
+ return ListenerAction::kStop;
}
- return COLDBOOT_CONTINUE;
- });
- device_close();
+ return ListenerAction::kContinue;
+ };
+
+ uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
+ if (!found) {
+ LOG(INFO) << "dm-verity device not found in /sys, waiting for its uevent";
+ Timer t;
+ uevent_listener_.Poll(verity_callback, 10s);
+ LOG(INFO) << "wait for dm-verity device returned after " << t;
+ }
+ if (!found) {
+ LOG(ERROR) << "dm-verity device not found after polling timeout";
+ return false;
+ }
+
+ return true;
}
bool FirstStageMount::MountPartitions() {
@@ -280,14 +321,18 @@
bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
if (fs_mgr_is_verified(fstab_rec)) {
int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
- if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
- LOG(INFO) << "Verity disabled for '" << fstab_rec->mount_point << "'";
- } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
- // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
- // Needs to create it because ueventd isn't started in init first stage.
- InitVerityDevice(fstab_rec->blk_device);
- } else {
- return false;
+ switch (ret) {
+ case FS_MGR_SETUP_VERITY_SKIPPED:
+ case FS_MGR_SETUP_VERITY_DISABLED:
+ LOG(INFO) << "Verity disabled/skipped for '" << fstab_rec->mount_point << "'";
+ return true;
+ case FS_MGR_SETUP_VERITY_SUCCESS:
+ // The exact block device name (fstab_rec->blk_device) is changed to
+ // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
+ // first stage.
+ return InitVerityDevice(fstab_rec->blk_device);
+ default:
+ return false;
}
}
return true; // Returns true to mount the partition.
@@ -345,46 +390,49 @@
return true;
}
-coldboot_action_t FirstStageMountVBootV2::ColdbootCallback(uevent* uevent) {
- // Invokes the parent function to see if any desired partition has been found.
- // If yes, record the by-name symlink for creating FsManagerAvbHandle later.
- coldboot_action_t parent_callback_ret = FirstStageMount::ColdbootCallback(uevent);
-
- // Skips the uevent if the parent function returns COLDBOOT_CONTINUE (meaning
- // that the uevent was skipped) or there is no uevent->partition_name to
- // create the by-name symlink.
- if (parent_callback_ret != COLDBOOT_CONTINUE && !uevent->partition_name.empty()) {
- // get_block_device_symlinks() will return three symlinks at most, depending on
+ListenerAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
+ // Check if this uevent corresponds to one of the required partitions and store its symlinks if
+ // so, in order to create FsManagerAvbHandle later.
+ // Note that the parent callback removes partitions from the list of required partitions
+ // as it finds them, so this must happen first.
+ if (!uevent.partition_name.empty() &&
+ required_devices_partition_names_.find(uevent.partition_name) !=
+ required_devices_partition_names_.end()) {
+ // GetBlockDeviceSymlinks() will return three symlinks at most, depending on
// the content of uevent. by-name symlink will be at [0] if uevent->partition_name
// is not empty. e.g.,
// - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
// - /dev/block/platform/soc.0/f9824900.sdhci/by-num/p1
// - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
- std::vector<std::string> links = get_block_device_symlinks(uevent);
+ std::vector<std::string> links = device_handler_.GetBlockDeviceSymlinks(uevent);
if (!links.empty()) {
- auto[it, inserted] = by_name_symlink_map_.emplace(uevent->partition_name, links[0]);
+ auto[it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
if (!inserted) {
- LOG(ERROR) << "Partition '" << uevent->partition_name
+ LOG(ERROR) << "Partition '" << uevent.partition_name
<< "' already existed in the by-name symlink map with a value of '"
<< it->second << "', new value '" << links[0] << "' will be ignored.";
}
}
}
- return parent_callback_ret;
+ return FirstStageMount::UeventCallback(uevent);
}
bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
if (fs_mgr_is_avb(fstab_rec)) {
if (!InitAvbHandle()) return false;
- if (avb_handle_->hashtree_disabled()) {
- LOG(INFO) << "avb hashtree disabled for '" << fstab_rec->mount_point << "'";
- } else if (avb_handle_->SetUpAvb(fstab_rec, false /* wait_for_verity_dev */)) {
- // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
- // Needs to create it because ueventd isn't started in init first stage.
- InitVerityDevice(fstab_rec->blk_device);
- } else {
- return false;
+ SetUpAvbHashtreeResult hashtree_result =
+ avb_handle_->SetUpAvbHashtree(fstab_rec, false /* wait_for_verity_dev */);
+ switch (hashtree_result) {
+ case SetUpAvbHashtreeResult::kDisabled:
+ return true; // Returns true to mount the partition.
+ case SetUpAvbHashtreeResult::kSuccess:
+ // The exact block device name (fstab_rec->blk_device) is changed to
+ // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
+ // first stage.
+ return InitVerityDevice(fstab_rec->blk_device);
+ default:
+ return false;
}
}
return true; // Returns true to mount the partition.
@@ -412,7 +460,7 @@
// Public functions
// ----------------
-// Mounts /system, /vendor, and/or /odm if they are present in the fstab provided by device tree.
+// Mounts partitions specified by fstab in device tree.
bool DoFirstStageMount() {
// Skips first stage mount if we're in recovery mode.
if (IsRecoveryMode()) {
@@ -464,3 +512,6 @@
}
setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
}
+
+} // namespace init
+} // namespace android
diff --git a/init/init_first_stage.h b/init/init_first_stage.h
index 170a24c..c7a3867 100644
--- a/init/init_first_stage.h
+++ b/init/init_first_stage.h
@@ -17,7 +17,13 @@
#ifndef _INIT_FIRST_STAGE_H
#define _INIT_FIRST_STAGE_H
+namespace android {
+namespace init {
+
bool DoFirstStageMount();
void SetInitAvbVersionInRecovery();
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 1b31cf2..9f7089b 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -18,6 +18,7 @@
#include <dirent.h>
+#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -25,6 +26,9 @@
#include "parser.h"
#include "util.h"
+namespace android {
+namespace init {
+
Parser::Parser() {
}
@@ -108,7 +112,7 @@
bool Parser::ParseConfigFile(const std::string& path) {
LOG(INFO) << "Parsing file " << path << "...";
- Timer t;
+ android::base::Timer t;
std::string data;
std::string err;
if (!ReadFile(path, &data, &err)) {
@@ -159,3 +163,6 @@
}
return ParseConfigFile(path);
}
+
+} // namespace init
+} // namespace android
diff --git a/init/init_parser.h b/init/init_parser.h
index bd8a178..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() {}
@@ -66,9 +69,9 @@
// be written.
using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
+ // TODO: init is the only user of this as a singleton; remove it.
static Parser& GetInstance();
- // Exposed for testing
Parser();
bool ParseConfig(const std::string& path);
@@ -93,4 +96,7 @@
bool is_odm_etc_init_loaded_ = false;
};
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
index 86d60d0..95f269a 100644
--- a/init/init_parser_test.cpp
+++ b/init/init_parser_test.cpp
@@ -16,13 +16,17 @@
#include "init_parser.h"
-#include "init.h"
-#include "service.h"
+#include <string>
+#include <vector>
#include <gtest/gtest.h>
-#include <string>
-#include <vector>
+#include "init.h"
+#include "service.h"
+#include "util.h"
+
+namespace android {
+namespace init {
TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
ServiceManager& sm = ServiceManager::GetInstance();
@@ -141,3 +145,6 @@
TEST(init_parser, make_exec_oneshot_service_with_just_command_no_dash) {
Test_make_exec_oneshot_service(false, false, false, false, false);
}
+
+} // namespace init
+} // namespace android
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 7093ba9..0a4071b 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -27,6 +27,9 @@
#include "keyword_map.h"
#include "util.h"
+namespace android {
+namespace init {
+
class TestFunctionMap : public KeywordMap<BuiltinFunction> {
public:
// Helper for argument-less functions
@@ -185,3 +188,6 @@
EXPECT_EQ(6, num_executed);
}
+
+} // namespace init
+} // namespace android
diff --git a/init/keychords.cpp b/init/keychords.cpp
index c572cee..a0d7cc5 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "keychords.h"
+
#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
@@ -25,7 +27,9 @@
#include <android-base/properties.h>
#include "init.h"
-#include "service.h"
+
+namespace android {
+namespace init {
static struct input_keychord *keychords = 0;
static int keychords_count = 0;
@@ -112,3 +116,6 @@
register_epoll_handler(keychord_fd, handle_keychord);
}
+
+} // namespace init
+} // namespace android
diff --git a/init/keychords.h b/init/keychords.h
index d2723b7..1c34098 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -17,9 +17,15 @@
#ifndef _INIT_KEYCHORDS_H_
#define _INIT_KEYCHORDS_H_
-struct service;
+#include "service.h"
-void add_service_keycodes(service*);
+namespace android {
+namespace init {
+
+void add_service_keycodes(Service* svc);
void keychord_init();
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 88bad01..481d637 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -22,6 +22,9 @@
#include <android-base/stringprintf.h>
+namespace android {
+namespace init {
+
template <typename Function>
class KeywordMap {
public:
@@ -79,4 +82,7 @@
virtual const Map& map() const = 0;
};
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/log.cpp b/init/log.cpp
index 0615730..1830077 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -23,6 +23,9 @@
#include <android-base/logging.h>
#include <selinux/selinux.h>
+namespace android {
+namespace init {
+
void InitKernelLogging(char* argv[]) {
// Make stdin/stdout/stderr all point to /dev/null.
int fd = open("/sys/fs/selinux/null", O_RDWR);
@@ -55,3 +58,6 @@
android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
return 0;
}
+
+} // namespace init
+} // namespace android
diff --git a/init/log.h b/init/log.h
index 29a27af..5a4eba6 100644
--- a/init/log.h
+++ b/init/log.h
@@ -19,8 +19,14 @@
#include <sys/cdefs.h>
+namespace android {
+namespace init {
+
void InitKernelLogging(char* argv[]);
int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
+} // namespace init
+} // namespace android
+
#endif
diff --git a/init/parser.cpp b/init/parser.cpp
index 0d13cfe..c0fa6d9 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -1,5 +1,8 @@
#include "parser.h"
+namespace android {
+namespace init {
+
int next_token(struct parse_state *state)
{
char *x = state->ptr;
@@ -116,3 +119,6 @@
}
return T_EOF;
}
+
+} // namespace init
+} // namespace android
diff --git a/init/parser.h b/init/parser.h
index 3dcc566..86e4c57 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -21,6 +21,9 @@
#define T_TEXT 1
#define T_NEWLINE 2
+namespace android {
+namespace init {
+
struct parse_state
{
char *ptr;
@@ -31,4 +34,7 @@
int next_token(struct parse_state *state);
+} // namespace init
+} // namespace android
+
#endif /* PARSER_H_ */
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 18e47e3..925cc9b 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,12 +39,13 @@
#include <sys/_system_properties.h>
#include <memory>
+#include <queue>
#include <vector>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <bootimg.h>
#include <fs_mgr.h>
@@ -55,11 +56,14 @@
#include "init.h"
#include "util.h"
-using android::base::StringPrintf;
+using android::base::Timer;
#define PERSISTENT_PROPERTY_DIR "/data/property"
#define RECOVERY_MOUNT_POINT "/recovery"
+namespace android {
+namespace init {
+
static int persistent_properties_loaded = 0;
static int property_set_fd = -1;
@@ -144,7 +148,7 @@
if (name[0] == '.') return false;
if (name[namelen - 1] == '.') return false;
- /* Only allow alphanumeric, plus '.', '-', '@', or '_' */
+ /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
/* Don't allow ".." to appear in a property name */
for (size_t i = 0; i < namelen; i++) {
if (name[i] == '.') {
@@ -152,7 +156,7 @@
if (name[i-1] == '.') return false;
continue;
}
- if (name[i] == '_' || name[i] == '-' || name[i] == '@') continue;
+ if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
if (name[i] >= 'a' && name[i] <= 'z') continue;
if (name[i] >= 'A' && name[i] <= 'Z') continue;
if (name[i] >= '0' && name[i] <= '9') continue;
@@ -162,7 +166,7 @@
return true;
}
-uint32_t property_set(const std::string& name, const std::string& value) {
+static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
size_t valuelen = value.size();
if (!is_legal_property_name(name)) {
@@ -176,12 +180,6 @@
return PROP_ERROR_INVALID_VALUE;
}
- if (name == "selinux.restorecon_recursive" && valuelen > 0) {
- if (selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
- LOG(ERROR) << "Failed to restorecon_recursive " << value;
- }
- }
-
prop_info* pi = (prop_info*) __system_property_find(name.c_str());
if (pi != nullptr) {
// ro.* properties are actually "write-once".
@@ -210,6 +208,85 @@
return PROP_SUCCESS;
}
+typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&);
+
+struct PropertyChildInfo {
+ pid_t pid;
+ PropertyAsyncFunc func;
+ std::string name;
+ std::string value;
+};
+
+static std::queue<PropertyChildInfo> property_children;
+
+static void PropertyChildLaunch() {
+ auto& info = property_children.front();
+ pid_t pid = fork();
+ if (pid < 0) {
+ LOG(ERROR) << "Failed to fork for property_set_async";
+ while (!property_children.empty()) {
+ property_children.pop();
+ }
+ return;
+ }
+ if (pid != 0) {
+ info.pid = pid;
+ } else {
+ if (info.func(info.name, info.value) != 0) {
+ LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
+ << "\") failed";
+ }
+ exit(0);
+ }
+}
+
+bool PropertyChildReap(pid_t pid) {
+ if (property_children.empty()) {
+ return false;
+ }
+ auto& info = property_children.front();
+ if (info.pid != pid) {
+ return false;
+ }
+ if (PropertySetImpl(info.name, info.value) != PROP_SUCCESS) {
+ LOG(ERROR) << "Failed to set async property " << info.name;
+ }
+ property_children.pop();
+ if (!property_children.empty()) {
+ PropertyChildLaunch();
+ }
+ return true;
+}
+
+static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
+ PropertyAsyncFunc func) {
+ if (value.empty()) {
+ return PropertySetImpl(name, value);
+ }
+
+ PropertyChildInfo info;
+ info.func = func;
+ info.name = name;
+ info.value = value;
+ property_children.push(info);
+ if (property_children.size() == 1) {
+ PropertyChildLaunch();
+ }
+ return PROP_SUCCESS;
+}
+
+static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) {
+ return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+}
+
+uint32_t property_set(const std::string& name, const std::string& value) {
+ if (name == "selinux.restorecon_recursive") {
+ return PropertySetAsync(name, value, RestoreconRecursiveAsync);
+ }
+
+ return PropertySetImpl(name, value);
+}
+
class SocketConnection {
public:
SocketConnection(int socket, const struct ucred& cred)
@@ -276,7 +353,7 @@
while (*timeout_ms > 0) {
Timer timer;
int nr = poll(ufds, 1, *timeout_ms);
- uint64_t millis = timer.duration_ms();
+ uint64_t millis = timer.duration().count();
*timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis;
if (nr > 0) {
@@ -640,7 +717,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";
}
@@ -670,3 +747,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..969caec 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -20,7 +20,7 @@
#include <fcntl.h>
#include <linux/fs.h>
#include <mntent.h>
-#include <selinux/selinux.h>
+#include <sys/capability.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
@@ -35,6 +35,7 @@
#include <thread>
#include <vector>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
@@ -48,10 +49,16 @@
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
+#include "capabilities.h"
+#include "init.h"
#include "property_service.h"
#include "service.h"
using android::base::StringPrintf;
+using android::base::Timer;
+
+namespace android {
+namespace init {
// represents umount status during reboot / shutdown.
enum UmountStat {
@@ -156,12 +163,42 @@
}
static void LogShutdownTime(UmountStat stat, Timer* t) {
- LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration_ms()) << ":" << stat;
+ LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration().count()) << ":"
+ << stat;
+}
+
+// Determines whether the system is capable of rebooting. This is conservative,
+// so if any of the attempts to determine this fail, it will still return true.
+static bool IsRebootCapable() {
+ if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
+ PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
+ return true;
+ }
+
+ ScopedCaps caps(cap_get_proc());
+ if (!caps) {
+ PLOG(WARNING) << "cap_get_proc() failed";
+ return true;
+ }
+
+ cap_flag_value_t value = CAP_SET;
+ if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
+ PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
+ return true;
+ }
+ return value == CAP_SET;
}
static void __attribute__((noreturn))
RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
LOG(INFO) << "Reboot ending, jumping to kernel";
+
+ if (!IsRebootCapable()) {
+ // On systems where init does not have the capability of rebooting the
+ // device, just exit cleanly.
+ exit(0);
+ }
+
switch (cmd) {
case ANDROID_RB_POWEROFF:
reboot(RB_POWER_OFF);
@@ -220,7 +257,7 @@
}
}
-static UmountStat UmountPartitions(int timeoutMs) {
+static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
Timer t;
UmountStat stat = UMOUNT_STAT_TIMEOUT;
int retry = 0;
@@ -238,7 +275,7 @@
stat = UMOUNT_STAT_SUCCESS;
break;
}
- if ((timeoutMs < t.duration_ms()) && retry > 0) { // try umount at least once
+ if ((timeout < t.duration()) && retry > 0) { // try umount at least once
stat = UMOUNT_STAT_TIMEOUT;
break;
}
@@ -267,7 +304,7 @@
*
* return true when umount was successful. false when timed out.
*/
-static UmountStat TryUmountAndFsck(bool runFsck, int timeoutMs) {
+static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeout) {
Timer t;
std::vector<MountEntry> block_devices;
std::vector<MountEntry> emulated_devices;
@@ -278,13 +315,13 @@
return UMOUNT_STAT_ERROR;
}
- UmountStat stat = UmountPartitions(timeoutMs - t.duration_ms());
+ UmountStat stat = UmountPartitions(timeout - t.duration());
if (stat != UMOUNT_STAT_SUCCESS) {
LOG(INFO) << "umount timeout, last resort, kill all and try";
if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
KillAllProcesses();
// even if it succeeds, still it is timeout and do not run fsck with all processes killed
- UmountPartitions(0);
+ UmountPartitions(0ms);
if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
}
@@ -318,26 +355,27 @@
abort();
}
- constexpr unsigned int shutdownTimeoutDefault = 6;
- unsigned int shutdownTimeout = shutdownTimeoutDefault;
- if (SHUTDOWN_ZERO_TIMEOUT) { // eng build
- shutdownTimeout = 0;
- } else {
- shutdownTimeout =
- android::base::GetUintProperty("ro.build.shutdown_timeout", shutdownTimeoutDefault);
+ auto shutdown_timeout = 0s;
+ if (!SHUTDOWN_ZERO_TIMEOUT) {
+ constexpr unsigned int shutdown_timeout_default = 6;
+ auto shutdown_timeout_property =
+ android::base::GetUintProperty("ro.build.shutdown_timeout", shutdown_timeout_default);
+ shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
}
- LOG(INFO) << "Shutdown timeout: " << shutdownTimeout;
+ LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " seconds";
// keep debugging tools until non critical ones are all gone.
const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
- const std::set<std::string> to_starts{"watchdogd", "vold", "ueventd"};
+ const std::set<std::string> to_starts{"watchdogd"};
ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
if (kill_after_apps.count(s->name())) {
s->SetShutdownCritical();
} else if (to_starts.count(s->name())) {
s->Start();
s->SetShutdownCritical();
+ } else if (s->IsShutdownCritical()) {
+ s->Start(); // start shutdown critical service if not started
}
});
@@ -351,7 +389,7 @@
// optional shutdown step
// 1. terminate all services except shutdown critical ones. wait for delay to finish
- if (shutdownTimeout > 0) {
+ if (shutdown_timeout > 0s) {
LOG(INFO) << "terminating init services";
// Ask all services to terminate except shutdown critical ones.
@@ -360,9 +398,9 @@
});
int service_count = 0;
- // Up to half as long as shutdownTimeout or 3 seconds, whichever is lower.
- unsigned int terminationWaitTimeout = std::min<unsigned int>((shutdownTimeout + 1) / 2, 3);
- while (t.duration_s() < terminationWaitTimeout) {
+ // Up to half as long as shutdown_timeout or 3 seconds, whichever is lower.
+ auto termination_wait_timeout = std::min((shutdown_timeout + 1s) / 2, 3s);
+ while (t.duration() < termination_wait_timeout) {
ServiceManager::GetInstance().ReapAnyOutstandingChildren();
service_count = 0;
@@ -410,7 +448,7 @@
});
// 4. sync, try umount, and optionally run fsck for user shutdown
sync();
- UmountStat stat = TryUmountAndFsck(runFsck, shutdownTimeout * 1000 - t.duration_ms());
+ UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
// Follow what linux shutdown is doing: one more sync with little bit delay
sync();
std::this_thread::sleep_for(100ms);
@@ -457,6 +495,9 @@
}
} else if (command == "thermal-shutdown") { // no additional parameter allowed
cmd = ANDROID_RB_THERMOFF;
+ // Do not queue "shutdown" trigger since we want to shutdown immediately
+ DoReboot(cmd, command, reboot_target, run_fsck);
+ return true;
} else {
command_invalid = true;
}
@@ -465,6 +506,28 @@
return false;
}
- DoReboot(cmd, command, reboot_target, run_fsck);
+ LOG(INFO) << "Clear action queue and start shutdown trigger";
+ ActionManager::GetInstance().ClearQueue();
+ // Queue shutdown trigger first
+ ActionManager::GetInstance().QueueEventTrigger("shutdown");
+ // Queue built-in shutdown_done
+ auto shutdown_handler = [cmd, command, reboot_target,
+ run_fsck](const std::vector<std::string>&) {
+ DoReboot(cmd, command, reboot_target, run_fsck);
+ return 0;
+ };
+ ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
+
+ // Skip wait for prop if it is in progress
+ ResetWaitForProp();
+
+ // Skip wait for exec if it is in progress
+ if (ServiceManager::GetInstance().IsWaitingForExec()) {
+ ServiceManager::GetInstance().ClearExecWait();
+ }
+
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 7c931da..7a657c8 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -34,6 +34,7 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <processgroup/processgroup.h>
@@ -45,10 +46,17 @@
#include "util.h"
using android::base::boot_clock;
+using android::base::GetProperty;
+using android::base::Join;
+using android::base::make_scope_guard;
using android::base::ParseInt;
+using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
+namespace android {
+namespace init {
+
static std::string ComputeContextFromExecutable(std::string& service_name,
const std::string& service_path) {
std::string computed_context;
@@ -198,28 +206,16 @@
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));
}
}
void Service::KillProcessGroup(int signal) {
- // We ignore reporting errors of ESRCH as this commonly happens in the below case,
- // 1) Terminate() is called, which sends SIGTERM to the process
- // 2) The process successfully exits
- // 3) ReapOneProcess() is called, which calls waitpid(-1, ...) which removes the pid entry.
- // 4) Reap() is called, which sends SIGKILL, but the pid no longer exists.
- // TODO: sigaction for SIGCHLD reports the pid of the exiting process,
- // we should do this kill with that pid first before calling waitpid().
- if (kill(-pid_, signal) == -1 && errno != ESRCH) {
- PLOG(ERROR) << "kill(" << pid_ << ", " << signal << ") failed";
- }
-
// If we've already seen a successful result from killProcessGroup*(), then we have removed
// the cgroup already and calling these functions a second time will simply result in an error.
// This is true regardless of which signal was sent.
@@ -241,8 +237,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_;
}
}
@@ -333,8 +336,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; });
}
@@ -498,6 +501,14 @@
return true;
}
+bool Service::ParseShutdown(const std::vector<std::string>& args, std::string* err) {
+ if (args[1] == "critical") {
+ flags_ |= SVC_SHUTDOWN_CRITICAL;
+ return true;
+ }
+ return false;
+}
+
template <typename T>
bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
@@ -537,9 +548,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;
}
@@ -602,6 +612,7 @@
{"namespace", {1, 2, &Service::ParseNamespace}},
{"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}},
+ {"shutdown", {1, 1, &Service::ParseShutdown}},
{"socket", {3, 6, &Service::ParseSocket}},
{"file", {2, 2, &Service::ParseFile}},
{"user", {1, 1, &Service::ParseUser}},
@@ -622,10 +633,10 @@
return (this->*parser)(args, err);
}
-bool Service::ExecStart(std::unique_ptr<Timer>* exec_waiter) {
+bool Service::ExecStart(std::unique_ptr<android::base::Timer>* exec_waiter) {
flags_ |= SVC_EXEC | SVC_ONESHOT;
- exec_waiter->reset(new Timer);
+ exec_waiter->reset(new android::base::Timer);
if (!Start()) {
exec_waiter->reset();
@@ -707,13 +718,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.
@@ -727,7 +738,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;
@@ -768,7 +779,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);
@@ -787,9 +798,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");
@@ -966,8 +977,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;
@@ -1089,13 +1099,25 @@
}
bool ServiceManager::ReapOneProcess() {
- int status;
- pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
- if (pid == 0) {
+ siginfo_t siginfo = {};
+ // This returns a zombie pid or informs us that there are no zombies left to be reaped.
+ // It does NOT reap the pid; that is done below.
+ if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
+ PLOG(ERROR) << "waitid failed";
return false;
- } else if (pid == -1) {
- PLOG(ERROR) << "waitpid failed";
- return false;
+ }
+
+ auto pid = siginfo.si_pid;
+ if (pid == 0) return false;
+
+ // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
+ // whenever the function returns from this point forward.
+ // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
+ // want the pid to remain valid throughout that (and potentially future) usages.
+ auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
+
+ if (PropertyChildReap(pid)) {
+ return true;
}
Service* svc = FindServiceByPid(pid);
@@ -1103,24 +1125,20 @@
std::string name;
std::string wait_string;
if (svc) {
- name = android::base::StringPrintf("Service '%s' (pid %d)",
- svc->name().c_str(), pid);
+ name = StringPrintf("Service '%s' (pid %d)", svc->name().c_str(), pid);
if (svc->flags() & SVC_EXEC) {
- wait_string =
- android::base::StringPrintf(" waiting took %f seconds", exec_waiter_->duration_s());
+ wait_string = StringPrintf(" waiting took %f seconds",
+ exec_waiter_->duration().count() / 1000.0f);
}
} else {
- name = android::base::StringPrintf("Untracked pid %d", pid);
+ name = StringPrintf("Untracked pid %d", pid);
}
+ auto status = siginfo.si_status;
if (WIFEXITED(status)) {
LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
} else if (WIFSIGNALED(status)) {
LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
- } else if (WIFSTOPPED(status)) {
- LOG(INFO) << name << " stopped by signal " << WSTOPSIG(status) << wait_string;
- } else {
- LOG(INFO) << name << " state changed" << wait_string;
}
if (!svc) {
@@ -1144,6 +1162,15 @@
}
}
+void ServiceManager::ClearExecWait() {
+ // Clear EXEC flag if there is one pending
+ // And clear the wait flag
+ for (const auto& s : services_) {
+ s->UnSetExec();
+ }
+ exec_waiter_.reset();
+}
+
bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line, std::string* err) {
if (args.size() < 3) {
@@ -1185,3 +1212,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..f682abd 100644
--- a/init/service.h
+++ b/init/service.h
@@ -32,7 +32,6 @@
#include "descriptors.h"
#include "init_parser.h"
#include "keyword_map.h"
-#include "util.h"
#define SVC_DISABLED 0x001 // do not autostart with class
#define SVC_ONESHOT 0x002 // do not restart on exit
@@ -55,8 +54,8 @@
#define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups
-class Action;
-class ServiceManager;
+namespace android {
+namespace init {
struct ServiceEnvironmentInfo {
ServiceEnvironmentInfo();
@@ -76,7 +75,7 @@
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
bool ParseLine(const std::vector<std::string>& args, std::string* err);
- bool ExecStart(std::unique_ptr<Timer>* exec_waiter);
+ bool ExecStart(std::unique_ptr<android::base::Timer>* exec_waiter);
bool Start();
bool StartIfNotDisabled();
bool Enable();
@@ -89,6 +88,7 @@
void DumpState() const;
void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
+ void UnSetExec() { flags_ &= ~SVC_EXEC; }
const std::string& name() const { return name_; }
const std::set<std::string>& classnames() const { return classnames_; }
@@ -137,6 +137,7 @@
bool ParseNamespace(const std::vector<std::string>& args, std::string* err);
bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
+ bool ParseShutdown(const std::vector<std::string>& args, std::string* err);
bool ParseSocket(const std::vector<std::string>& args, std::string* err);
bool ParseFile(const std::vector<std::string>& args, std::string* err);
bool ParseUser(const std::vector<std::string>& args, std::string* err);
@@ -186,7 +187,7 @@
};
class ServiceManager {
-public:
+ public:
static ServiceManager& GetInstance();
// Exposed for testing
@@ -208,14 +209,15 @@
void ReapAnyOutstandingChildren();
void RemoveService(const Service& svc);
void DumpState() const;
+ void ClearExecWait();
-private:
+ private:
// Cleans up a child process that exited.
// Returns true iff a children was cleaned up.
bool ReapOneProcess();
static int exec_count_; // Every service needs a unique name.
- std::unique_ptr<Timer> exec_waiter_;
+ std::unique_ptr<android::base::Timer> exec_waiter_;
std::vector<std::unique_ptr<Service>> services_;
};
@@ -236,4 +238,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
new file mode 100644
index 0000000..c4fd945
--- /dev/null
+++ b/init/uevent.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENT_H
+#define _INIT_UEVENT_H
+
+#include <string>
+
+namespace android {
+namespace init {
+
+struct Uevent {
+ std::string action;
+ std::string path;
+ std::string subsystem;
+ std::string firmware;
+ std::string partition_name;
+ std::string device_name;
+ int partition_num;
+ int major;
+ int minor;
+};
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
new file mode 100644
index 0000000..ac1d7c7
--- /dev/null
+++ b/init/uevent_listener.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uevent_listener.h"
+
+#include <fcntl.h>
+#include <poll.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+#include <cutils/uevent.h>
+
+namespace android {
+namespace init {
+
+static void ParseEvent(const char* msg, Uevent* uevent) {
+ uevent->partition_num = -1;
+ uevent->major = -1;
+ uevent->minor = -1;
+ uevent->action.clear();
+ uevent->path.clear();
+ uevent->subsystem.clear();
+ uevent->firmware.clear();
+ uevent->partition_name.clear();
+ uevent->device_name.clear();
+ // currently ignoring SEQNUM
+ while (*msg) {
+ if (!strncmp(msg, "ACTION=", 7)) {
+ msg += 7;
+ uevent->action = msg;
+ } else if (!strncmp(msg, "DEVPATH=", 8)) {
+ msg += 8;
+ uevent->path = msg;
+ } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
+ msg += 10;
+ uevent->subsystem = msg;
+ } else if (!strncmp(msg, "FIRMWARE=", 9)) {
+ msg += 9;
+ uevent->firmware = msg;
+ } else if (!strncmp(msg, "MAJOR=", 6)) {
+ msg += 6;
+ uevent->major = atoi(msg);
+ } else if (!strncmp(msg, "MINOR=", 6)) {
+ msg += 6;
+ uevent->minor = atoi(msg);
+ } else if (!strncmp(msg, "PARTN=", 6)) {
+ msg += 6;
+ uevent->partition_num = atoi(msg);
+ } else if (!strncmp(msg, "PARTNAME=", 9)) {
+ msg += 9;
+ uevent->partition_name = msg;
+ } else if (!strncmp(msg, "DEVNAME=", 8)) {
+ msg += 8;
+ uevent->device_name = msg;
+ }
+
+ // advance to after the next \0
+ while (*msg++)
+ ;
+ }
+
+ if (LOG_UEVENTS) {
+ LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
+ << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
+ << ", " << uevent->minor << " }";
+ }
+}
+
+UeventListener::UeventListener() {
+ // is 256K enough? udev uses 16MB!
+ device_fd_.reset(uevent_open_socket(256 * 1024, true));
+ if (device_fd_ == -1) {
+ LOG(FATAL) << "Could not open uevent socket";
+ }
+
+ fcntl(device_fd_, F_SETFL, O_NONBLOCK);
+}
+
+bool UeventListener::ReadUevent(Uevent* uevent) const {
+ char msg[UEVENT_MSG_LEN + 2];
+ int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);
+ if (n <= 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ LOG(ERROR) << "Error reading from Uevent Fd";
+ }
+ return false;
+ }
+ if (n >= UEVENT_MSG_LEN) {
+ LOG(ERROR) << "Uevent overflowed buffer, discarding";
+ // Return true here even if we discard as we may have more uevents pending and we
+ // want to keep processing them.
+ return true;
+ }
+
+ msg[n] = '\0';
+ msg[n + 1] = '\0';
+
+ ParseEvent(msg, uevent);
+
+ return true;
+}
+
+// RegenerateUevents*() walks parts of the /sys tree and pokes the uevent files to cause the kernel
+// to regenerate device add uevents that have already happened. This is particularly useful when
+// starting ueventd, to regenerate all of the uevents that it had previously missed.
+//
+// We drain any pending events from the netlink socket every time we poke another uevent file to
+// make sure we don't overrun the socket's buffer.
+//
+
+ListenerAction UeventListener::RegenerateUeventsForDir(DIR* d,
+ const ListenerCallback& callback) const {
+ int dfd = dirfd(d);
+
+ int fd = openat(dfd, "uevent", O_WRONLY);
+ if (fd >= 0) {
+ write(fd, "add\n", 4);
+ close(fd);
+
+ Uevent uevent;
+ while (ReadUevent(&uevent)) {
+ if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;
+ }
+ }
+
+ dirent* de;
+ while ((de = readdir(d)) != nullptr) {
+ if (de->d_type != DT_DIR || de->d_name[0] == '.') continue;
+
+ fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+ if (fd < 0) continue;
+
+ std::unique_ptr<DIR, decltype(&closedir)> d2(fdopendir(fd), closedir);
+ if (d2 == 0) {
+ close(fd);
+ } else {
+ if (RegenerateUeventsForDir(d2.get(), callback) == ListenerAction::kStop) {
+ return ListenerAction::kStop;
+ }
+ }
+ }
+
+ // default is always to continue looking for uevents
+ return ListenerAction::kContinue;
+}
+
+ListenerAction UeventListener::RegenerateUeventsForPath(const std::string& path,
+ const ListenerCallback& callback) const {
+ std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
+ if (!d) return ListenerAction::kContinue;
+
+ return RegenerateUeventsForDir(d.get(), callback);
+}
+
+static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
+
+void UeventListener::RegenerateUevents(const ListenerCallback& callback) const {
+ for (const auto path : kRegenerationPaths) {
+ if (RegenerateUeventsForPath(path, callback) == ListenerAction::kStop) return;
+ }
+}
+
+void UeventListener::Poll(const ListenerCallback& callback,
+ const std::optional<std::chrono::milliseconds> relative_timeout) const {
+ using namespace std::chrono;
+
+ pollfd ufd;
+ ufd.events = POLLIN;
+ ufd.fd = device_fd_;
+
+ auto start_time = steady_clock::now();
+
+ while (true) {
+ ufd.revents = 0;
+
+ int timeout_ms = -1;
+ if (relative_timeout) {
+ auto now = steady_clock::now();
+ auto time_elapsed = duration_cast<milliseconds>(now - start_time);
+ if (time_elapsed > *relative_timeout) return;
+
+ auto remaining_timeout = *relative_timeout - time_elapsed;
+ timeout_ms = remaining_timeout.count();
+ }
+
+ int nr = poll(&ufd, 1, timeout_ms);
+ if (nr == 0) return;
+ if (nr < 0) {
+ PLOG(ERROR) << "poll() of uevent socket failed, continuing";
+ continue;
+ }
+ if (ufd.revents & POLLIN) {
+ // We're non-blocking, so if we receive a poll event keep processing until
+ // we have exhausted all uevent messages.
+ Uevent uevent;
+ while (ReadUevent(&uevent)) {
+ if (callback(uevent) == ListenerAction::kStop) return;
+ }
+ }
+ }
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
new file mode 100644
index 0000000..5b453fe
--- /dev/null
+++ b/init/uevent_listener.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENT_LISTENER_H
+#define _INIT_UEVENT_LISTENER_H
+
+#include <dirent.h>
+
+#include <chrono>
+#include <functional>
+#include <optional>
+
+#include <android-base/unique_fd.h>
+
+#include "uevent.h"
+
+#define UEVENT_MSG_LEN 2048
+
+namespace android {
+namespace init {
+
+enum class ListenerAction {
+ kStop = 0, // Stop regenerating uevents as we've handled the one(s) we're interested in.
+ kContinue, // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
+};
+
+using ListenerCallback = std::function<ListenerAction(const Uevent&)>;
+
+class UeventListener {
+ public:
+ UeventListener();
+
+ void RegenerateUevents(const ListenerCallback& callback) const;
+ ListenerAction RegenerateUeventsForPath(const std::string& path,
+ const ListenerCallback& callback) const;
+ void Poll(const ListenerCallback& callback,
+ const std::optional<std::chrono::milliseconds> relative_timeout = {}) const;
+
+ private:
+ bool ReadUevent(Uevent* uevent) const;
+ ListenerAction RegenerateUeventsForDir(DIR* d, const ListenerCallback& callback) const;
+
+ android::base::unique_fd device_fd_;
+};
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 8c0c574..c0eae1e 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -18,50 +18,215 @@
#include <ctype.h>
#include <fcntl.h>
-#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/wait.h>
+#include <set>
+#include <thread>
+
+#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
+#include <selinux/android.h>
#include <selinux/selinux.h>
#include "devices.h"
+#include "firmware_handler.h"
#include "log.h"
+#include "uevent_listener.h"
+#include "ueventd_parser.h"
#include "util.h"
-int ueventd_main(int argc, char **argv)
-{
- /*
- * init sets the umask to 077 for forked processes. We need to
- * create files with exact permissions, without modification by
- * the umask.
- */
- umask(000);
+// At a high level, ueventd listens for uevent messages generated by the kernel through a netlink
+// socket. When ueventd receives such a message it handles it by taking appropriate actions,
+// which can typically be creating a device node in /dev, setting file permissions, setting selinux
+// labels, etc.
+// Ueventd also handles loading of firmware that the kernel requests, and creates symlinks for block
+// and character devices.
- /* Prevent fire-and-forget children from becoming zombies.
- * If we should need to wait() for some children in the future
- * (as opposed to none right now), double-forking here instead
- * of ignoring SIGCHLD may be the better solution.
- */
- signal(SIGCHLD, SIG_IGN);
+// When ueventd starts, it regenerates uevents for all currently registered devices by traversing
+// /sys and writing 'add' to each 'uevent' file that it finds. This causes the kernel to generate
+// and resend uevent messages for all of the currently registered devices. This is done, because
+// ueventd would not have been running when these devices were registered and therefore was unable
+// to receive their uevent messages and handle them appropriately. This process is known as
+// 'cold boot'.
- InitKernelLogging(argv);
+// 'init' currently waits synchronously on the cold boot process of ueventd before it continues
+// its boot process. For this reason, cold boot should be as quick as possible. One way to achieve
+// a speed up here is to parallelize the handling of ueventd messages, which consume the bulk of the
+// time during cold boot.
- LOG(INFO) << "ueventd started!";
+// Handling of uevent messages has two unique properties:
+// 1) It can be done in isolation; it doesn't need to read or write any status once it is started.
+// 2) It uses setegid() and setfscreatecon() so either care (aka locking) must be taken to ensure
+// that no file system operations are done while the uevent process has an abnormal egid or
+// fscreatecon or this handling must happen in a separate process.
+// Given the above two properties, it is best to fork() subprocesses to handle the uevents. This
+// reduces the overhead and complexity that would be required in a solution with threads and locks.
+// In testing, a racy multithreaded solution has the same performance as the fork() solution, so
+// there is no reason to deal with the complexity of the former.
- selinux_callback cb;
- cb.func_log = selinux_klog_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
+// One other important caveat during the boot process is the handling of SELinux restorecon.
+// Since many devices have child devices, calling selinux_android_restorecon() recursively for each
+// device when its uevent is handled, results in multiple restorecon operations being done on a
+// given file. It is more efficient to simply do restorecon recursively on /sys during cold boot,
+// than to do restorecon on each device as its uevent is handled. This only applies to cold boot;
+// once that has completed, restorecon is done for each device as its uevent is handled.
- Parser& parser = Parser::GetInstance();
- parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>());
+// With all of the above considered, the cold boot process has the below steps:
+// 1) ueventd regenerates uevents by doing the /sys traversal and listens to the netlink socket for
+// the generated uevents. It writes these uevents into a queue represented by a vector.
+//
+// 2) ueventd forks 'n' separate uevent handler subprocesses and has each of them to handle the
+// uevents in the queue based on a starting offset (their process number) and a stride (the total
+// number of processes). Note that no IPC happens at this point and only const functions from
+// DeviceHandler should be called from this context.
+//
+// 3) In parallel to the subprocesses handling the uevents, the main thread of ueventd calls
+// selinux_android_restorecon() recursively on /sys/class, /sys/block, and /sys/devices.
+//
+// 4) Once the restorecon operation finishes, the main thread calls waitpid() to wait for all
+// subprocess handlers to complete and exit. Once this happens, it marks coldboot as having
+// completed.
+//
+// At this point, ueventd is single threaded, poll()'s and then handles any future uevents.
+
+// Lastly, it should be noted that uevents that occur during the coldboot process are handled
+// without issue after the coldboot process completes. This is because the uevent listener is
+// paused while the uevent handler and restorecon actions take place. Once coldboot completes,
+// the uevent listener resumes in polling mode and will handle the uevents that occurred during
+// coldboot.
+
+namespace android {
+namespace init {
+
+class ColdBoot {
+ public:
+ ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler)
+ : uevent_listener_(uevent_listener),
+ device_handler_(device_handler),
+ num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
+
+ void Run();
+
+ private:
+ void UeventHandlerMain(unsigned int process_num, unsigned int total_processes);
+ void RegenerateUevents();
+ void ForkSubProcesses();
+ void DoRestoreCon();
+ void WaitForSubProcesses();
+
+ UeventListener& uevent_listener_;
+ DeviceHandler& device_handler_;
+
+ unsigned int num_handler_subprocesses_;
+ std::vector<Uevent> uevent_queue_;
+
+ std::set<pid_t> subprocess_pids_;
+};
+
+void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
+ for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {
+ auto& uevent = uevent_queue_[i];
+ device_handler_.HandleDeviceEvent(uevent);
+ }
+ _exit(EXIT_SUCCESS);
+}
+
+void ColdBoot::RegenerateUevents() {
+ uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
+ HandleFirmwareEvent(uevent);
+
+ uevent_queue_.emplace_back(std::move(uevent));
+ return ListenerAction::kContinue;
+ });
+}
+
+void ColdBoot::ForkSubProcesses() {
+ for (unsigned int i = 0; i < num_handler_subprocesses_; ++i) {
+ auto pid = fork();
+ if (pid < 0) {
+ PLOG(FATAL) << "fork() failed!";
+ }
+
+ if (pid == 0) {
+ UeventHandlerMain(i, num_handler_subprocesses_);
+ }
+
+ subprocess_pids_.emplace(pid);
+ }
+}
+
+void ColdBoot::DoRestoreCon() {
+ selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
+ device_handler_.set_skip_restorecon(false);
+}
+
+void ColdBoot::WaitForSubProcesses() {
+ // Treat subprocesses that crash or get stuck the same as if ueventd itself has crashed or gets
+ // stuck.
+ //
+ // When a subprocess crashes, we fatally abort from ueventd. init will restart ueventd when
+ // init reaps it, and the cold boot process will start again. If this continues to fail, then
+ // since ueventd is marked as a critical service, init will reboot to recovery.
+ //
+ // When a subprocess gets stuck, keep ueventd spinning waiting for it. init has a timeout for
+ // cold boot and will reboot to the bootloader if ueventd does not complete in time.
+ while (!subprocess_pids_.empty()) {
+ int status;
+ pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, 0));
+ if (pid == -1) {
+ PLOG(ERROR) << "waitpid() failed";
+ continue;
+ }
+
+ auto it = std::find(subprocess_pids_.begin(), subprocess_pids_.end(), pid);
+ if (it == subprocess_pids_.end()) continue;
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) == EXIT_SUCCESS) {
+ subprocess_pids_.erase(it);
+ } else {
+ LOG(FATAL) << "subprocess exited with status " << WEXITSTATUS(status);
+ }
+ } else if (WIFSIGNALED(status)) {
+ LOG(FATAL) << "subprocess killed by signal " << WTERMSIG(status);
+ }
+ }
+}
+
+void ColdBoot::Run() {
+ android::base::Timer cold_boot_timer;
+
+ RegenerateUevents();
+
+ ForkSubProcesses();
+
+ DoRestoreCon();
+
+ WaitForSubProcesses();
+
+ close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+ LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
+}
+
+DeviceHandler CreateDeviceHandler() {
+ Parser parser;
+
+ std::vector<Subsystem> subsystems;
+ parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>(&subsystems));
+
using namespace std::placeholders;
- parser.AddSingleLineParser("/sys/", std::bind(ParsePermissionsLine, _1, _2, true));
- parser.AddSingleLineParser("/dev/", std::bind(ParsePermissionsLine, _1, _2, false));
+ std::vector<SysfsPermissions> sysfs_permissions;
+ std::vector<Permissions> dev_permissions;
+ parser.AddSingleLineParser(
+ "/sys/", std::bind(ParsePermissionsLine, _1, _2, &sysfs_permissions, nullptr));
+ parser.AddSingleLineParser("/dev/",
+ std::bind(ParsePermissionsLine, _1, _2, nullptr, &dev_permissions));
+
parser.ParseConfig("/ueventd.rc");
parser.ParseConfig("/vendor/ueventd.rc");
parser.ParseConfig("/odm/ueventd.rc");
@@ -76,22 +241,49 @@
std::string hardware = android::base::GetProperty("ro.hardware", "");
parser.ParseConfig("/ueventd." + hardware + ".rc");
- device_init();
+ return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
+ std::move(subsystems), true);
+}
- pollfd ufd;
- ufd.events = POLLIN;
- ufd.fd = get_device_fd();
+int ueventd_main(int argc, char** argv) {
+ /*
+ * init sets the umask to 077 for forked processes. We need to
+ * create files with exact permissions, without modification by
+ * the umask.
+ */
+ umask(000);
- while (true) {
- ufd.revents = 0;
- int nr = poll(&ufd, 1, -1);
- if (nr <= 0) {
- continue;
- }
- if (ufd.revents & POLLIN) {
- handle_device_fd();
- }
+ InitKernelLogging(argv);
+
+ LOG(INFO) << "ueventd started!";
+
+ selinux_callback cb;
+ cb.func_log = selinux_klog_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+
+ DeviceHandler device_handler = CreateDeviceHandler();
+ UeventListener uevent_listener;
+
+ if (access(COLDBOOT_DONE, F_OK) != 0) {
+ ColdBoot cold_boot(uevent_listener, device_handler);
+ cold_boot.Run();
}
+ // We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now.
+ signal(SIGCHLD, SIG_IGN);
+ // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN
+ // for SIGCHLD above.
+ while (waitpid(-1, nullptr, WNOHANG) > 0) {
+ }
+
+ uevent_listener.Poll([&device_handler](const Uevent& uevent) {
+ HandleFirmwareEvent(uevent);
+ device_handler.HandleDeviceEvent(uevent);
+ return ListenerAction::kContinue;
+ });
+
return 0;
}
+
+} // 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
new file mode 100644
index 0000000..02e0d42
--- /dev/null
+++ b/init/ueventd_parser.cpp
@@ -0,0 +1,151 @@
+/*
+ * 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 "ueventd_parser.h"
+
+#include <grp.h>
+#include <pwd.h>
+
+#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) {
+ bool is_sysfs = out_sysfs_permissions != nullptr;
+ if (is_sysfs && args.size() != 5) {
+ *err = "/sys/ lines must have 5 entries";
+ return false;
+ }
+
+ if (!is_sysfs && args.size() != 4) {
+ *err = "/dev/ lines must have 4 entries";
+ return false;
+ }
+
+ auto it = args.begin();
+ const std::string& name = *it++;
+
+ std::string sysfs_attribute;
+ if (is_sysfs) sysfs_attribute = *it++;
+
+ // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
+ std::string& perm_string = *it++;
+ char* end_pointer = 0;
+ mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
+ if (end_pointer == nullptr || *end_pointer != '\0') {
+ *err = "invalid mode '" + perm_string + "'";
+ return false;
+ }
+
+ std::string& uid_string = *it++;
+ passwd* pwd = getpwnam(uid_string.c_str());
+ if (!pwd) {
+ *err = "invalid uid '" + uid_string + "'";
+ return false;
+ }
+ uid_t uid = pwd->pw_uid;
+
+ std::string& gid_string = *it++;
+ struct group* grp = getgrnam(gid_string.c_str());
+ if (!grp) {
+ *err = "invalid gid '" + gid_string + "'";
+ return false;
+ }
+ gid_t gid = grp->gr_gid;
+
+ if (is_sysfs) {
+ out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid);
+ } else {
+ out_dev_permissions->emplace_back(name, perm, uid, gid);
+ }
+ return true;
+}
+
+bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) {
+ if (args.size() != 2) {
+ *err = "subsystems must have exactly one name";
+ return false;
+ }
+
+ if (std::find(subsystems_->begin(), subsystems_->end(), args[1]) != subsystems_->end()) {
+ *err = "ignoring duplicate subsystem entry";
+ return false;
+ }
+
+ subsystem_.name_ = args[1];
+
+ return true;
+}
+
+bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
+ if (args[1] == "uevent_devname") {
+ subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
+ return true;
+ }
+ if (args[1] == "uevent_devpath") {
+ subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
+ return true;
+ }
+
+ *err = "invalid devname '" + args[1] + "'";
+ return false;
+}
+
+bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
+ if (args[1].front() != '/') {
+ *err = "dirname '" + args[1] + " ' does not start with '/'";
+ return false;
+ }
+
+ subsystem_.dir_name_ = args[1];
+ return true;
+}
+
+bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+ using OptionParser =
+ bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
+ static class OptionParserMap : public KeywordMap<OptionParser> {
+ private:
+ const Map& map() const override {
+ // clang-format off
+ static const Map option_parsers = {
+ {"devname", {1, 1, &SubsystemParser::ParseDevName}},
+ {"dirname", {1, 1, &SubsystemParser::ParseDirName}},
+ };
+ // clang-format on
+ return option_parsers;
+ }
+ } parser_map;
+
+ auto parser = parser_map.FindFunction(args, err);
+
+ if (!parser) {
+ return false;
+ }
+
+ return (this->*parser)(std::move(args), err);
+}
+
+void SubsystemParser::EndSection() {
+ subsystems_->emplace_back(std::move(subsystem_));
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
new file mode 100644
index 0000000..592df63
--- /dev/null
+++ b/init/ueventd_parser.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENTD_PARSER_H
+#define _INIT_UEVENTD_PARSER_H
+
+#include <string>
+#include <vector>
+
+#include "devices.h"
+#include "init_parser.h"
+
+namespace android {
+namespace init {
+
+class SubsystemParser : public SectionParser {
+ public:
+ SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
+ bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
+ std::string* err) override;
+ bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+ void EndSection() override;
+
+ private:
+ bool ParseDevName(std::vector<std::string>&& args, std::string* err);
+ bool ParseDirName(std::vector<std::string>&& args, std::string* err);
+
+ Subsystem subsystem_;
+ std::vector<Subsystem>* subsystems_;
+};
+
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
+ std::vector<SysfsPermissions>* out_sysfs_permissions,
+ std::vector<Permissions>* out_dev_permissions);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
new file mode 100644
index 0000000..86d7055
--- /dev/null
+++ b/init/ueventd_test.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <linux/futex.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/scopeguard.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <selinux/selinux.h>
+
+using namespace std::string_literals;
+
+template <typename T, typename F>
+void WriteFromMultipleThreads(std::vector<std::pair<std::string, T>>& files_and_parameters,
+ F function) {
+ auto num_threads = files_and_parameters.size();
+ pthread_barrier_t barrier;
+ pthread_barrier_init(&barrier, nullptr, num_threads);
+ auto barrier_destroy =
+ android::base::make_scope_guard([&barrier]() { pthread_barrier_destroy(&barrier); });
+
+ auto make_thread_function = [&function, &barrier](const auto& file, const auto& parameter) {
+ return [&]() {
+ function(parameter);
+ pthread_barrier_wait(&barrier);
+ android::base::WriteStringToFile("<empty>", file);
+ };
+ };
+
+ std::vector<std::thread> threads;
+ // TODO(b/63712782): Structured bindings + templated containers are broken in clang :(
+ // for (const auto& [file, parameter] : files_and_parameters) {
+ for (const auto& pair : files_and_parameters) {
+ const auto& file = pair.first;
+ const auto& parameter = pair.second;
+ threads.emplace_back(std::thread(make_thread_function(file, parameter)));
+ }
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+}
+
+TEST(ueventd, setegid_IsPerThread) {
+ if (getuid() != 0) return;
+
+ TemporaryDir dir;
+
+ gid_t gid = 0;
+ std::vector<std::pair<std::string, gid_t>> files_and_gids;
+ std::generate_n(std::back_inserter(files_and_gids), 100, [&gid, &dir]() {
+ gid++;
+ return std::pair(dir.path + "/gid_"s + std::to_string(gid), gid);
+ });
+
+ WriteFromMultipleThreads(files_and_gids, [](gid_t gid) { EXPECT_EQ(0, setegid(gid)); });
+
+ for (const auto& [file, expected_gid] : files_and_gids) {
+ struct stat info;
+ EXPECT_EQ(0, stat(file.c_str(), &info));
+ EXPECT_EQ(expected_gid, info.st_gid);
+ }
+}
+
+TEST(ueventd, setfscreatecon_IsPerThread) {
+ if (getuid() != 0) return;
+
+ const char* const contexts[] = {
+ "u:object_r:audio_device:s0",
+ "u:object_r:sensors_device:s0",
+ "u:object_r:video_device:s0"
+ "u:object_r:zero_device:s0",
+ };
+
+ TemporaryDir dir;
+ std::vector<std::pair<std::string, std::string>> files_and_contexts;
+ for (const char* context : contexts) {
+ files_and_contexts.emplace_back(dir.path + "/context_"s + context, context);
+ }
+
+ WriteFromMultipleThreads(files_and_contexts, [](const std::string& context) {
+ EXPECT_EQ(0, setfscreatecon(context.c_str()));
+ });
+
+ for (const auto& [file, expected_context] : files_and_contexts) {
+ char* file_context;
+ EXPECT_GT(getfilecon(file.c_str(), &file_context), 0);
+ EXPECT_EQ(expected_context, file_context);
+ freecon(file_context);
+ }
+}
diff --git a/init/util.cpp b/init/util.cpp
index 75f81b9..2792794 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.
@@ -367,14 +370,10 @@
void panic() {
LOG(ERROR) << "panic: rebooting to bootloader";
+ // Do not queue "shutdown" trigger since we want to shutdown immediately
DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
}
-std::ostream& operator<<(std::ostream& os, const Timer& t) {
- os << t.duration_s() << " seconds";
- return os;
-}
-
// Reads the content of device tree file under kAndroidDtDir directory.
// Returns true if the read is success, false otherwise.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
@@ -397,3 +396,6 @@
}
return false;
}
+
+} // namespace init
+} // namespace android
diff --git a/init/util.h b/init/util.h
index 1ad6b77..452df2d 100644
--- a/init/util.h
+++ b/init/util.h
@@ -35,32 +35,15 @@
using android::base::boot_clock;
using namespace std::chrono_literals;
+namespace android {
+namespace init {
+
int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
const char* socketcon, selabel_handle* sehandle);
bool ReadFile(const std::string& path, std::string* content, std::string* err);
bool WriteFile(const std::string& path, const std::string& content, std::string* err);
-class Timer {
- public:
- Timer() : start_(boot_clock::now()) {}
-
- double duration_s() const {
- typedef std::chrono::duration<double> double_duration;
- return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
- }
-
- int64_t duration_ms() const {
- return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_)
- .count();
- }
-
- private:
- android::base::boot_clock::time_point start_;
-};
-
-std::ostream& operator<<(std::ostream& os, const Timer& t);
-
bool DecodeUid(const std::string& name, uid_t* uid, std::string* err);
int mkdir_recursive(const std::string& pathname, mode_t mode, selabel_handle* sehandle);
@@ -78,4 +61,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/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 7dd9227..e02aaf2 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -113,6 +113,7 @@
static_libs: ["libasync_safe", "libcutils"],
},
},
+ whole_static_libs: ["libdemangle"],
}
cc_library_shared {
@@ -160,6 +161,7 @@
shared_libs = [
"libbase",
"libunwind",
+ "libziparchive",
],
}
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 3545661..e46d353 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -27,6 +27,8 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
+#include <demangle.h>
+
#include "BacktraceLog.h"
#include "thread_utils.h"
#include "UnwindCurrent.h"
@@ -62,8 +64,7 @@
if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
return "";
}
- std::string func_name = GetFunctionNameRaw(pc, offset);
- return func_name;
+ return demangle(GetFunctionNameRaw(pc, offset).c_str());
}
bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, word_t* out_value) {
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 919b65b..13c4abf 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -266,14 +266,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))) {
@@ -282,25 +295,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 15391d9..cf8f5c3 100644
--- a/libcutils/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -34,7 +34,7 @@
* Check if Linux kernel enables SCHEDTUNE feature (only available in Android
* common kernel or Linaro LSK, not in mainline Linux as of v4.9)
*
- * Return value: 1 if Linux kernel CONFIG_SCHEDTUNE=y; 0 otherwise.
+ * Return value: 1 if Linux kernel CONFIG_CGROUP_SCHEDTUNE=y; 0 otherwise.
*/
extern bool schedboost_enabled();
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index e29a844..f733e90 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -28,13 +28,6 @@
#define UNUSED __attribute__((__unused__))
-#ifndef SLOGE
-#define SLOGE ALOGE
-#endif
-#ifndef SLOGW
-#define SLOGW ALOGW
-#endif
-
/* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
* Call this any place a SchedPolicy is used as an input parameter.
* Returns the possibly re-mapped policy.
@@ -123,11 +116,8 @@
on where init.rc mounts cpuset. That's why we'd better require this
configuration be set if CONFIG_CPUSETS is set.
- With runtime check using the following function, build time
- variables like ENABLE_CPUSETS (used in Android.mk) or cpusets (used
- in Android.bp) are not needed.
+ In older releases, this was controlled by build-time configuration.
*/
-
bool cpusets_enabled() {
static bool enabled = (access("/dev/cpuset/tasks", F_OK) == 0);
@@ -136,15 +126,11 @@
/*
Similar to CONFIG_CPUSETS above, but with a different configuration
- CONFIG_SCHEDTUNE that's in Android common Linux kernel and Linaro
+ CONFIG_CGROUP_SCHEDTUNE that's in Android common Linux kernel and Linaro
Stable Kernel (LSK), but not in mainline Linux as of v4.9.
- With runtime check using the following function, build time
- variables like ENABLE_SCHEDBOOST (used in Android.mk) or schedboost
- (used in Android.bp) are not needed.
-
+ In older releases, this was controlled by build-time configuration.
*/
-
bool schedboost_enabled() {
static bool enabled = (access("/dev/stune/tasks", F_OK) == 0);
@@ -340,7 +326,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) {
@@ -348,7 +334,7 @@
snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
int fd = open(buf, O_WRONLY | O_CLOEXEC);
if (fd != -1) {
- int len = snprintf(buf, sizeof(buf), "%llu", slack);
+ int len = snprintf(buf, sizeof(buf), "%lu", slack);
if (write(fd, buf, len) != len) {
SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
}
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
index a62cd51..391adb6 100644
--- a/libcutils/tests/fs_config.cpp
+++ b/libcutils/tests/fs_config.cpp
@@ -29,12 +29,39 @@
extern const fs_path_config* __for_testing_only__android_dirs;
extern const fs_path_config* __for_testing_only__android_files;
+extern bool (*__for_testing_only__fs_config_cmp)(bool, const char*, size_t, const char*, size_t);
// Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we
// hit a nullptr termination, before we declare the list is just too big or
// could be missing the nullptr.
static constexpr size_t max_idx = 4096;
+static const struct fs_config_cmp_test {
+ bool dir;
+ const char* prefix;
+ const char* path;
+ bool match;
+} fs_config_cmp_tests[] = {
+ // clang-format off
+ { true, "system/lib", "system/lib/hw", true },
+ { true, "vendor/lib", "system/vendor/lib/hw", true },
+ { true, "system/vendor/lib", "vendor/lib/hw", true },
+ { true, "system/vendor/lib", "system/vendor/lib/hw", true },
+ { false, "vendor/bin/wifi", "system/vendor/bin/w", false },
+ { false, "vendor/bin/wifi", "system/vendor/bin/wifi", true },
+ { false, "vendor/bin/wifi", "system/vendor/bin/wifi2", false },
+ { false, "system/vendor/bin/wifi", "system/vendor/bin/wifi", true, },
+ { false, "odm/bin/wifi", "system/odm/bin/wifi", true },
+ { false, "oem/bin/wifi", "system/oem/bin/wifi", true },
+ { false, "data/bin/wifi", "system/data/bin/wifi", false },
+ { false, "system/bin/*", "system/bin/wifi", true },
+ { false, "vendor/bin/*", "system/vendor/bin/wifi", true },
+ { false, "system/bin/*", "system/bin", false },
+ { false, "system/vendor/bin/*", "vendor/bin/wifi", true },
+ { false, NULL, NULL, false },
+ // clang-format on
+};
+
static bool check_unique(std::vector<const char*>& paths, const std::string& config_name,
const std::string& prefix) {
bool retval = false;
@@ -106,6 +133,22 @@
return check_unique(paths_tmp, config, prefix) || retval;
}
+static bool check_fs_config_cmp(const fs_config_cmp_test* tests) {
+ bool match, retval = false;
+ for (size_t idx = 0; tests[idx].prefix; ++idx) {
+ match = __for_testing_only__fs_config_cmp(tests[idx].dir, tests[idx].prefix,
+ strlen(tests[idx].prefix), tests[idx].path,
+ strlen(tests[idx].path));
+ if (match != tests[idx].match) {
+ GTEST_LOG_(ERROR) << tests[idx].path << (match ? " matched " : " didn't match ")
+ << tests[idx].prefix;
+ retval = true;
+ break;
+ }
+ }
+ return retval;
+}
+
#define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field))
static bool check_unique(const std::string& config, const std::string& prefix) {
@@ -199,3 +242,7 @@
TEST(fs_config, odm_files_alias) {
check_two(__for_testing_only__android_files, "files", "odm/");
}
+
+TEST(fs_config, system_alias) {
+ EXPECT_FALSE(check_fs_config_cmp(fs_config_cmp_tests));
+}
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index 041fd63..088981a 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -1,5 +1,6 @@
cc_library {
name: "libdiskconfig",
+ vendor_available: true,
srcs: [
"diskconfig.c",
"diskutils.c",
diff --git a/libion/Android.bp b/libion/Android.bp
index da98111..6f267e4 100644
--- a/libion/Android.bp
+++ b/libion/Android.bp
@@ -1,6 +1,7 @@
cc_library {
name: "libion",
+ vendor_available: true,
srcs: ["ion.c"],
shared_libs: ["liblog"],
local_include_dirs: [
diff --git a/liblog/Android.bp b/liblog/Android.bp
index e74aa82..b98d18f 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,6 +42,24 @@
"logd_writer.c",
]
+cc_library_headers {
+ name: "liblog_headers",
+ host_supported: true,
+ vendor_available: true,
+ export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ vendor: {
+ export_include_dirs: ["include_vndk"],
+ },
+ },
+}
+
// Shared and static library for host and device
// ========================================================
cc_library {
@@ -81,7 +99,8 @@
},
},
- export_include_dirs: ["include"],
+ header_libs: ["liblog_headers"],
+ export_header_lib_headers: ["liblog_headers"],
cflags: [
"-Werror",
@@ -100,7 +119,7 @@
}
ndk_headers {
- name: "liblog_headers",
+ name: "liblog_ndk_headers",
from: "include/android",
to: "android",
srcs: ["include/android/log.h"],
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 3a215e9..d01708d 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -161,7 +161,7 @@
#endif
#if __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
-clockid_t android_log_clockid();
+clockid_t android_log_clockid(void);
#endif
#endif /* __linux__ */
@@ -185,7 +185,7 @@
* May be used to clean up File descriptors after a Fork, the resources are
* all O_CLOEXEC so wil self clean on exec().
*/
-void __android_log_close();
+void __android_log_close(void);
#endif
#ifndef __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index 057be5d..bb1ce34 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -177,6 +177,12 @@
return *this;
}
+ android_log_event_list& operator<<(bool value) {
+ int retval = android_log_write_int32(ctx, value ? 1 : 0);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
android_log_event_list& operator<<(int64_t value) {
int retval = android_log_write_int64(ctx, value);
if (retval < 0) ret = retval;
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
index 01623df..a79beec 100644
--- a/liblog/include_vndk/log/log.h
+++ b/liblog/include_vndk/log/log.h
@@ -9,6 +9,7 @@
#include <log/log_radio.h>
#include <log/log_read.h>
#include <log/log_safetynet.h>
+#include <log/log_system.h>
#include <log/log_time.h>
/*
diff --git a/liblog/include_vndk/log/log_system.h b/liblog/include_vndk/log/log_system.h
new file mode 120000
index 0000000..d0d3904
--- /dev/null
+++ b/liblog/include_vndk/log/log_system.h
@@ -0,0 +1 @@
+../../include/log/log_system.h
\ No newline at end of file
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 71f74ab..46ec5ef 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1720,6 +1720,7 @@
// Kills logd and toss all collected data, equivalent to logcat -b all -c,
// except we also return errors to the logging callers.
#ifdef USING_LOGGER_DEFAULT
+#ifdef __ANDROID__
#ifdef TEST_PREFIX
// helper to liblog.enoent to count end-to-end matching logging messages.
static int count_matching_ts(log_time ts) {
@@ -1832,7 +1833,8 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
-#endif // USING_LOCAL_LOGD
+#endif // __ANDROID__
+#endif // USING_LOGGER_DEFAULT
// Below this point we run risks of setuid(AID_SYSTEM) which may affect others.
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index a63ab8e..6ed568a 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -23,6 +23,7 @@
#include <android-base/stringprintf.h>
#include <android/log.h> // minimal logging API
#include <gtest/gtest.h>
+#include <log/log_properties.h>
// Test the APIs in this standalone include file
#include <log/log_read.h>
// Do not use anything in log/log_time.h despite side effects of the above.
@@ -97,9 +98,12 @@
/* security buffer is allowed to be denied */
if (strcmp("security", name)) {
EXPECT_LT(0, get_log_size);
- /* crash buffer is allowed to be empty, that is actually healthy! */
- EXPECT_LE((strcmp("crash", name)) != 0,
- android_logger_get_log_readable_size(logger));
+ // crash buffer is allowed to be empty, that is actually healthy!
+ // kernel buffer is allowed to be empty on "user" builds
+ EXPECT_LE( // boolean 1 or 0 depending on expected content or empty
+ !!((strcmp("crash", name) != 0) &&
+ ((strcmp("kernel", name) != 0) || __android_log_is_debuggable())),
+ android_logger_get_log_readable_size(logger));
} else {
EXPECT_NE(0, get_log_size);
if (get_log_size < 0) {
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
index 6fe67a4..213be17 100644
--- a/libmemunreachable/Allocator.cpp
+++ b/libmemunreachable/Allocator.cpp
@@ -33,9 +33,11 @@
#include "android-base/macros.h"
-#include "anon_vma_naming.h"
#include "Allocator.h"
#include "LinkedList.h"
+#include "anon_vma_naming.h"
+
+namespace android {
// runtime interfaces used:
// abort
@@ -57,10 +59,9 @@
static constexpr size_t kUsableChunkSize = kChunkSize - kPageSize;
static constexpr size_t kMaxBucketAllocationSize = kChunkSize / 4;
static constexpr size_t kMinBucketAllocationSize = 8;
-static constexpr unsigned int kNumBuckets = const_log2(kMaxBucketAllocationSize)
- - const_log2(kMinBucketAllocationSize) + 1;
-static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize
- / kPageSize;
+static constexpr unsigned int kNumBuckets =
+ const_log2(kMaxBucketAllocationSize) - const_log2(kMinBucketAllocationSize) + 1;
+static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize / kPageSize;
std::atomic<int> heap_count;
@@ -93,7 +94,7 @@
void FreeLocked(void* ptr);
struct MapAllocation {
- void *ptr;
+ void* ptr;
size_t size;
MapAllocation* next;
};
@@ -107,8 +108,7 @@
}
static inline unsigned int size_to_bucket(size_t size) {
- if (size < kMinBucketAllocationSize)
- return kMinBucketAllocationSize;
+ if (size < kMinBucketAllocationSize) return kMinBucketAllocationSize;
return log2(size - 1) + 1 - const_log2(kMinBucketAllocationSize);
}
@@ -140,8 +140,7 @@
// Trim beginning
if (aligned_ptr != ptr) {
- ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr)
- - reinterpret_cast<uintptr_t>(ptr);
+ ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr) - reinterpret_cast<uintptr_t>(ptr);
munmap(ptr, extra);
map_size -= extra;
ptr = aligned_ptr;
@@ -151,14 +150,13 @@
if (map_size != size) {
assert(map_size > size);
assert(ptr != NULL);
- munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size),
- map_size - size);
+ munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size), map_size - size);
}
-#define PR_SET_VMA 0x53564d41
-#define PR_SET_VMA_ANON_NAME 0
- prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
- reinterpret_cast<uintptr_t>(ptr), size, "leak_detector_malloc");
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(ptr), size,
+ "leak_detector_malloc");
return ptr;
}
@@ -170,36 +168,31 @@
Chunk(HeapImpl* heap, int bucket);
~Chunk() {}
- void *Alloc();
+ void* Alloc();
void Free(void* ptr);
void Purge();
bool Empty();
static Chunk* ptr_to_chunk(void* ptr) {
- return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr)
- & ~(kChunkSize - 1));
+ return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr) & ~(kChunkSize - 1));
}
static bool is_chunk(void* ptr) {
return (reinterpret_cast<uintptr_t>(ptr) & (kChunkSize - 1)) != 0;
}
- unsigned int free_count() {
- return free_count_;
- }
- HeapImpl* heap() {
- return heap_;
- }
- LinkedList<Chunk*> node_; // linked list sorted by minimum free count
+ unsigned int free_count() { return free_count_; }
+ HeapImpl* heap() { return heap_; }
+ LinkedList<Chunk*> node_; // linked list sorted by minimum free count
private:
DISALLOW_COPY_AND_ASSIGN(Chunk);
HeapImpl* heap_;
unsigned int bucket_;
- unsigned int allocation_size_; // size of allocations in chunk, min 8 bytes
- unsigned int max_allocations_; // maximum number of allocations in the chunk
- unsigned int first_free_bitmap_; // index into bitmap for first non-full entry
- unsigned int free_count_; // number of available allocations
- unsigned int frees_since_purge_; // number of calls to Free since last Purge
+ unsigned int allocation_size_; // size of allocations in chunk, min 8 bytes
+ unsigned int max_allocations_; // maximum number of allocations in the chunk
+ unsigned int first_free_bitmap_; // index into bitmap for first non-full entry
+ unsigned int free_count_; // number of available allocations
+ unsigned int frees_since_purge_; // number of calls to Free since last Purge
// bitmap of pages that have been dirtied
uint32_t dirty_pages_[div_round_up(kUsablePagesPerChunk, 32)];
@@ -210,13 +203,10 @@
char data_[0];
unsigned int ptr_to_n(void* ptr) {
- ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr)
- - reinterpret_cast<uintptr_t>(data_);
+ ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(data_);
return offset / allocation_size_;
}
- void* n_to_ptr(unsigned int n) {
- return data_ + n * allocation_size_;
- }
+ void* n_to_ptr(unsigned int n) { return data_ + n * allocation_size_; }
};
static_assert(sizeof(Chunk) <= kPageSize, "header must fit in page");
@@ -225,23 +215,27 @@
assert(count == sizeof(Chunk));
void* mem = MapAligned(kChunkSize, kChunkSize);
if (!mem) {
- abort(); //throw std::bad_alloc;
+ abort(); // throw std::bad_alloc;
}
return mem;
}
// Override new operator on chunk to use mmap to allocate kChunkSize
-void Chunk::operator delete(void *ptr) {
+void Chunk::operator delete(void* ptr) {
assert(reinterpret_cast<Chunk*>(ptr) == ptr_to_chunk(ptr));
munmap(ptr, kChunkSize);
}
-Chunk::Chunk(HeapImpl* heap, int bucket) :
- node_(this), heap_(heap), bucket_(bucket), allocation_size_(
- bucket_to_size(bucket)), max_allocations_(
- kUsableChunkSize / allocation_size_), first_free_bitmap_(0), free_count_(
- max_allocations_), frees_since_purge_(0) {
+Chunk::Chunk(HeapImpl* heap, int bucket)
+ : node_(this),
+ heap_(heap),
+ bucket_(bucket),
+ allocation_size_(bucket_to_size(bucket)),
+ max_allocations_(kUsableChunkSize / allocation_size_),
+ first_free_bitmap_(0),
+ free_count_(max_allocations_),
+ frees_since_purge_(0) {
memset(dirty_pages_, 0, sizeof(dirty_pages_));
memset(free_bitmap_, 0xff, sizeof(free_bitmap_));
}
@@ -254,8 +248,7 @@
assert(free_count_ > 0);
unsigned int i = first_free_bitmap_;
- while (free_bitmap_[i] == 0)
- i++;
+ while (free_bitmap_[i] == 0) i++;
assert(i < arraysize(free_bitmap_));
unsigned int bit = __builtin_ffs(free_bitmap_[i]) - 1;
assert(free_bitmap_[i] & (1U << bit));
@@ -306,38 +299,35 @@
void Chunk::Purge() {
frees_since_purge_ = 0;
- //unsigned int allocsPerPage = kPageSize / allocation_size_;
+ // unsigned int allocsPerPage = kPageSize / allocation_size_;
}
// Override new operator on HeapImpl to use mmap to allocate a page
-void* HeapImpl::operator new(std::size_t count __attribute__((unused)))
- noexcept {
+void* HeapImpl::operator new(std::size_t count __attribute__((unused))) noexcept {
assert(count == sizeof(HeapImpl));
void* mem = MapAligned(kPageSize, kPageSize);
if (!mem) {
- abort(); //throw std::bad_alloc;
+ abort(); // throw std::bad_alloc;
}
heap_count++;
return mem;
}
-void HeapImpl::operator delete(void *ptr) {
+void HeapImpl::operator delete(void* ptr) {
munmap(ptr, kPageSize);
}
-HeapImpl::HeapImpl() :
- free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {
-}
+HeapImpl::HeapImpl() : free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {}
bool HeapImpl::Empty() {
for (unsigned int i = 0; i < kNumBuckets; i++) {
- for (LinkedList<Chunk*> *it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+ for (LinkedList<Chunk*>* it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) {
if (!it->data()->Empty()) {
return false;
}
}
- for (LinkedList<Chunk*> *it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+ for (LinkedList<Chunk*>* it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) {
if (!it->data()->Empty()) {
return false;
}
@@ -350,12 +340,12 @@
HeapImpl::~HeapImpl() {
for (unsigned int i = 0; i < kNumBuckets; i++) {
while (!free_chunks_[i].empty()) {
- Chunk *chunk = free_chunks_[i].next()->data();
+ Chunk* chunk = free_chunks_[i].next()->data();
chunk->node_.remove();
delete chunk;
}
while (!full_chunks_[i].empty()) {
- Chunk *chunk = full_chunks_[i].next()->data();
+ Chunk* chunk = full_chunks_[i].next()->data();
chunk->node_.remove();
delete chunk;
}
@@ -373,18 +363,18 @@
}
int bucket = size_to_bucket(size);
if (free_chunks_[bucket].empty()) {
- Chunk *chunk = new Chunk(this, bucket);
+ Chunk* chunk = new Chunk(this, bucket);
free_chunks_[bucket].insert(chunk->node_);
}
return free_chunks_[bucket].next()->data()->Alloc();
}
-void HeapImpl::Free(void *ptr) {
+void HeapImpl::Free(void* ptr) {
std::lock_guard<std::mutex> lk(m_);
FreeLocked(ptr);
}
-void HeapImpl::FreeLocked(void *ptr) {
+void HeapImpl::FreeLocked(void* ptr) {
if (!Chunk::is_chunk(ptr)) {
HeapImpl::MapFree(ptr);
} else {
@@ -397,12 +387,11 @@
void* HeapImpl::MapAlloc(size_t size) {
size = (size + kPageSize - 1) & ~(kPageSize - 1);
- MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(
- sizeof(MapAllocation)));
+ MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(sizeof(MapAllocation)));
void* ptr = MapAligned(size, kChunkSize);
if (!ptr) {
FreeLocked(allocation);
- abort(); //throw std::bad_alloc;
+ abort(); // throw std::bad_alloc;
}
allocation->ptr = ptr;
allocation->size = size;
@@ -412,10 +401,9 @@
return ptr;
}
-void HeapImpl::MapFree(void *ptr) {
- MapAllocation **allocation = &map_allocation_list_;
- while (*allocation && (*allocation)->ptr != ptr)
- allocation = &(*allocation)->next;
+void HeapImpl::MapFree(void* ptr) {
+ MapAllocation** allocation = &map_allocation_list_;
+ while (*allocation && (*allocation)->ptr != ptr) allocation = &(*allocation)->next;
assert(*allocation != nullptr);
@@ -425,22 +413,22 @@
*allocation = (*allocation)->next;
}
-void HeapImpl::MoveToFreeList(Chunk *chunk, int bucket) {
+void HeapImpl::MoveToFreeList(Chunk* chunk, int bucket) {
MoveToList(chunk, &free_chunks_[bucket]);
}
-void HeapImpl::MoveToFullList(Chunk *chunk, int bucket) {
+void HeapImpl::MoveToFullList(Chunk* chunk, int bucket) {
MoveToList(chunk, &full_chunks_[bucket]);
}
-void HeapImpl::MoveToList(Chunk *chunk, LinkedList<Chunk*>* head) {
+void HeapImpl::MoveToList(Chunk* chunk, LinkedList<Chunk*>* head) {
// Remove from old list
chunk->node_.remove();
- LinkedList<Chunk*> *node = head;
+ LinkedList<Chunk*>* node = head;
// Insert into new list, sorted by lowest free count
- while (node->next() != head && node->data() != nullptr
- && node->data()->free_count() < chunk->free_count())
+ while (node->next() != head && node->data() != nullptr &&
+ node->data()->free_count() < chunk->free_count())
node = node->next();
node->insert(chunk->node_);
@@ -469,10 +457,12 @@
impl_->Free(ptr);
}
-void Heap::deallocate(HeapImpl*impl, void* ptr) {
+void Heap::deallocate(HeapImpl* impl, void* ptr) {
impl->Free(ptr);
}
bool Heap::empty() {
return impl_->Empty();
}
+
+} // namespace android
diff --git a/libmemunreachable/Allocator.h b/libmemunreachable/Allocator.h
index 5390739..837a12b 100644
--- a/libmemunreachable/Allocator.h
+++ b/libmemunreachable/Allocator.h
@@ -27,18 +27,20 @@
#include <unordered_map>
#include <unordered_set>
#include <vector>
+
+namespace android {
+
extern std::atomic<int> heap_count;
class HeapImpl;
-template<typename T>
+template <typename T>
class Allocator;
-
// Non-templated class that implements wraps HeapImpl to keep
// implementation out of the header file
class Heap {
-public:
+ public:
Heap();
~Heap();
@@ -59,110 +61,99 @@
static void deallocate(HeapImpl* impl, void* ptr);
// Allocate a class of type T
- template<class T>
+ template <class T>
T* allocate() {
return reinterpret_cast<T*>(allocate(sizeof(T)));
}
// Comparators, copied objects will be equal
- bool operator ==(const Heap& other) const {
- return impl_ == other.impl_;
- }
- bool operator !=(const Heap& other) const {
- return !(*this == other);
- }
+ bool operator==(const Heap& other) const { return impl_ == other.impl_; }
+ bool operator!=(const Heap& other) const { return !(*this == other); }
// std::unique_ptr wrapper that allocates using allocate and deletes using
// deallocate
- template<class T>
+ template <class T>
using unique_ptr = std::unique_ptr<T, std::function<void(void*)>>;
- template<class T, class... Args>
+ template <class T, class... Args>
unique_ptr<T> make_unique(Args&&... args) {
HeapImpl* impl = impl_;
- return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...),
- [impl](void* ptr) {
- reinterpret_cast<T*>(ptr)->~T();
- deallocate(impl, ptr);
- });
+ return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...), [impl](void* ptr) {
+ reinterpret_cast<T*>(ptr)->~T();
+ deallocate(impl, ptr);
+ });
}
// std::unique_ptr wrapper that allocates using allocate and deletes using
// deallocate
- template<class T>
+ template <class T>
using shared_ptr = std::shared_ptr<T>;
- template<class T, class... Args>
+ template <class T, class... Args>
shared_ptr<T> make_shared(Args&&... args);
-protected:
+ protected:
HeapImpl* impl_;
bool owns_impl_;
};
// STLAllocator implements the std allocator interface on top of a Heap
-template<typename T>
+template <typename T>
class STLAllocator {
-public:
+ public:
using value_type = T;
- ~STLAllocator() {
- }
+ ~STLAllocator() {}
// Construct an STLAllocator on top of a Heap
- STLAllocator(const Heap& heap) : // NOLINT, implicit
- heap_(heap) {
- }
+ STLAllocator(const Heap& heap)
+ : // NOLINT, implicit
+ heap_(heap) {}
// Rebind an STLAllocator from an another STLAllocator
- template<typename U>
- STLAllocator(const STLAllocator<U>& other) : // NOLINT, implicit
- heap_(other.heap_) {
- }
+ template <typename U>
+ STLAllocator(const STLAllocator<U>& other)
+ : // NOLINT, implicit
+ heap_(other.heap_) {}
STLAllocator(const STLAllocator&) = default;
STLAllocator<T>& operator=(const STLAllocator<T>&) = default;
- T* allocate(std::size_t n) {
- return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T)));
- }
+ T* allocate(std::size_t n) { return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T))); }
- void deallocate(T* ptr, std::size_t) {
- heap_.deallocate(ptr);
- }
+ void deallocate(T* ptr, std::size_t) { heap_.deallocate(ptr); }
- template<typename U>
- bool operator ==(const STLAllocator<U>& other) const {
+ template <typename U>
+ bool operator==(const STLAllocator<U>& other) const {
return heap_ == other.heap_;
}
- template<typename U>
- inline bool operator !=(const STLAllocator<U>& other) const {
+ template <typename U>
+ inline bool operator!=(const STLAllocator<U>& other) const {
return !(this == other);
}
- template<typename U>
+ template <typename U>
friend class STLAllocator;
-protected:
+ protected:
Heap heap_;
};
-
// Allocator extends STLAllocator with some convenience methods for allocating
// a single object and for constructing unique_ptr and shared_ptr objects with
// appropriate deleters.
-template<class T>
+template <class T>
class Allocator : public STLAllocator<T> {
public:
~Allocator() {}
- Allocator(const Heap& other) : // NOLINT, implicit
- STLAllocator<T>(other) {
- }
+ Allocator(const Heap& other)
+ : // NOLINT, implicit
+ STLAllocator<T>(other) {}
- template<typename U>
- Allocator(const STLAllocator<U>& other) : // NOLINT, implicit
- STLAllocator<T>(other) {
- }
+ template <typename U>
+ Allocator(const STLAllocator<U>& other)
+ : // NOLINT, implicit
+ STLAllocator<T>(other) {}
Allocator(const Allocator&) = default;
Allocator<T>& operator=(const Allocator<T>&) = default;
@@ -171,24 +162,20 @@
using STLAllocator<T>::deallocate;
using STLAllocator<T>::heap_;
- T* allocate() {
- return STLAllocator<T>::allocate(1);
- }
- void deallocate(void* ptr) {
- heap_.deallocate(ptr);
- }
+ T* allocate() { return STLAllocator<T>::allocate(1); }
+ void deallocate(void* ptr) { heap_.deallocate(ptr); }
using shared_ptr = Heap::shared_ptr<T>;
- template<class... Args>
- shared_ptr make_shared(Args&& ...args) {
+ template <class... Args>
+ shared_ptr make_shared(Args&&... args) {
return heap_.template make_shared<T>(std::forward<Args>(args)...);
}
using unique_ptr = Heap::unique_ptr<T>;
- template<class... Args>
- unique_ptr make_unique(Args&& ...args) {
+ template <class... Args>
+ unique_ptr make_unique(Args&&... args) {
return heap_.template make_unique<T>(std::forward<Args>(args)...);
}
};
@@ -196,33 +183,36 @@
// std::unique_ptr wrapper that allocates using allocate and deletes using
// deallocate. Implemented outside class definition in order to pass
// Allocator<T> to shared_ptr.
-template<class T, class... Args>
+template <class T, class... Args>
inline Heap::shared_ptr<T> Heap::make_shared(Args&&... args) {
return std::allocate_shared<T, Allocator<T>, Args...>(Allocator<T>(*this),
- std::forward<Args>(args)...);
+ std::forward<Args>(args)...);
}
namespace allocator {
-template<class T>
+template <class T>
using vector = std::vector<T, Allocator<T>>;
-template<class T>
+template <class T>
using list = std::list<T, Allocator<T>>;
-template<class Key, class T, class Compare = std::less<Key>>
+template <class Key, class T, class Compare = std::less<Key>>
using map = std::map<Key, T, Compare, Allocator<std::pair<const Key, T>>>;
-template<class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
-using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>;
+template <class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+using unordered_map =
+ std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>;
-template<class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+template <class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
using unordered_set = std::unordered_set<Key, Hash, KeyEqual, Allocator<Key>>;
-template<class Key, class Compare = std::less<Key>>
+template <class Key, class Compare = std::less<Key>>
using set = std::set<Key, Compare, Allocator<Key>>;
using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
}
+} // namespace android
+
#endif
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index cdac76b..826a576 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -26,6 +26,7 @@
defaults: ["libmemunreachable_defaults"],
srcs: [
"Allocator.cpp",
+ "Binder.cpp",
"HeapWalker.cpp",
"LeakFolding.cpp",
"LeakPipe.cpp",
@@ -84,3 +85,18 @@
},
},
}
+
+cc_test {
+ name: "memunreachable_binder_test",
+ defaults: ["libmemunreachable_defaults"],
+ srcs: [
+ "tests/Binder_test.cpp",
+ "tests/MemUnreachable_test.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libhwbinder",
+ "libmemunreachable",
+ "libutils",
+ ],
+}
diff --git a/libmemunreachable/Binder.cpp b/libmemunreachable/Binder.cpp
new file mode 100644
index 0000000..60512a3
--- /dev/null
+++ b/libmemunreachable/Binder.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/cdefs.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include "Binder.h"
+#include "log.h"
+
+__BEGIN_DECLS
+
+// Weak undefined references to the symbols in libbinder and libhwbinder
+// so that libmemunreachable can call them in processes that have them
+// loaded without requiring libmemunreachable to have dependencies on them.
+ssize_t __attribute__((weak)) getBinderKernelReferences(size_t, uintptr_t*);
+ssize_t __attribute__((weak)) getHWBinderKernelReferences(size_t, uintptr_t*);
+
+__END_DECLS
+
+namespace android {
+
+static bool BinderReferencesToVector(allocator::vector<uintptr_t>& refs,
+ std::function<ssize_t(size_t, uintptr_t*)> fn) {
+ if (fn == nullptr) {
+ return true;
+ }
+
+ size_t size = refs.size();
+
+ do {
+ refs.resize(size);
+
+ ssize_t ret = fn(refs.size(), refs.data());
+ if (ret < 0) {
+ return false;
+ }
+
+ size = ret;
+ } while (size > refs.size());
+
+ refs.resize(size);
+ return true;
+}
+
+bool BinderReferences(allocator::vector<uintptr_t>& refs) {
+ refs.clear();
+
+ allocator::vector<uintptr_t> binder_refs{refs.get_allocator()};
+ if (BinderReferencesToVector(refs, getBinderKernelReferences)) {
+ refs.insert(refs.end(), binder_refs.begin(), binder_refs.end());
+ } else {
+ MEM_ALOGE("getBinderKernelReferences failed");
+ }
+
+ allocator::vector<uintptr_t> hwbinder_refs{refs.get_allocator()};
+ if (BinderReferencesToVector(hwbinder_refs, getHWBinderKernelReferences)) {
+ refs.insert(refs.end(), hwbinder_refs.begin(), hwbinder_refs.end());
+ } else {
+ MEM_ALOGE("getHWBinderKernelReferences failed");
+ }
+
+ return true;
+}
+
+} // namespace android
diff --git a/libunwindstack/Log.h b/libmemunreachable/Binder.h
similarity index 65%
copy from libunwindstack/Log.h
copy to libmemunreachable/Binder.h
index 2d01aa8..bf4fd3e 100644
--- a/libunwindstack/Log.h
+++ b/libmemunreachable/Binder.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,12 +14,15 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
+#ifndef LIBMEMUNREACHABLE_BINDER_H_
+#define LIBMEMUNREACHABLE_BINDER_H_
-#include <stdint.h>
+#include "Allocator.h"
-void log_to_stdout(bool enable);
-void log(uint8_t indent, const char* format, ...);
+namespace android {
-#endif // _LIBUNWINDSTACK_LOG_H
+bool BinderReferences(allocator::vector<uintptr_t>& refs);
+
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_BINDER_H_
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index c365ae5..2403ad0 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -28,6 +28,8 @@
#include "ScopedSignalHandler.h"
#include "log.h"
+namespace android {
+
bool HeapWalker::Allocation(uintptr_t begin, uintptr_t end) {
if (end == begin) {
end = begin + 1;
@@ -114,8 +116,8 @@
return true;
}
-bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit,
- size_t* num_leaks_out, size_t* leak_bytes_out) {
+bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit, size_t* num_leaks_out,
+ size_t* leak_bytes_out) {
leaked.clear();
size_t num_leaks = 0;
@@ -148,9 +150,9 @@
static bool MapOverPage(void* addr) {
const size_t page_size = sysconf(_SC_PAGE_SIZE);
- void *page = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & ~(page_size-1));
+ void* page = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & ~(page_size - 1));
- void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
+ void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
if (ret == MAP_FAILED) {
MEM_ALOGE("failed to map page at %p: %s", page, strerror(errno));
return false;
@@ -159,7 +161,8 @@
return true;
}
-void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si, void* /*uctx*/) {
+void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si,
+ void* /*uctx*/) {
uintptr_t addr = reinterpret_cast<uintptr_t>(si->si_addr);
if (addr != walking_ptr_) {
handler.reset();
@@ -172,3 +175,5 @@
}
ScopedSignalHandler::SignalFn ScopedSignalHandler::handler_;
+
+} // namespace android
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index b25696f..5c7ec13 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -25,6 +25,8 @@
#include "ScopedSignalHandler.h"
#include "Tarjan.h"
+namespace android {
+
// A range [begin, end)
struct Range {
uintptr_t begin;
@@ -34,31 +36,31 @@
bool operator==(const Range& other) const {
return this->begin == other.begin && this->end == other.end;
}
- bool operator!=(const Range& other) const {
- return !(*this == other);
- }
+ bool operator!=(const Range& other) const { return !(*this == other); }
};
// Comparator for Ranges that returns equivalence for overlapping ranges
struct compare_range {
- bool operator()(const Range& a, const Range& b) const {
- return a.end <= b.begin;
- }
+ bool operator()(const Range& a, const Range& b) const { return a.end <= b.begin; }
};
class HeapWalker {
public:
- explicit HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator),
- allocations_(allocator), allocation_bytes_(0),
- roots_(allocator), root_vals_(allocator),
- segv_handler_(allocator), walking_ptr_(0) {
+ explicit HeapWalker(Allocator<HeapWalker> allocator)
+ : allocator_(allocator),
+ allocations_(allocator),
+ allocation_bytes_(0),
+ roots_(allocator),
+ root_vals_(allocator),
+ segv_handler_(allocator),
+ walking_ptr_(0) {
valid_allocations_range_.end = 0;
valid_allocations_range_.begin = ~valid_allocations_range_.end;
- segv_handler_.install(SIGSEGV,
- [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
+ segv_handler_.install(
+ SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
this->HandleSegFault(handler, signal, siginfo, uctx);
- });
+ });
}
~HeapWalker() {}
@@ -68,15 +70,14 @@
bool DetectLeaks();
- bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks,
- size_t* leak_bytes);
+ bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks, size_t* leak_bytes);
size_t Allocations();
size_t AllocationBytes();
- template<class F>
+ template <class F>
void ForEachPtrInRange(const Range& range, F&& f);
- template<class F>
+ template <class F>
void ForEachAllocation(F&& f);
struct AllocationInfo {
@@ -84,7 +85,6 @@
};
private:
-
void RecurseRoot(const Range& root);
bool WordContainsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info);
void HandleSegFault(ScopedSignalHandler&, int, siginfo_t*, void*);
@@ -103,7 +103,7 @@
uintptr_t walking_ptr_;
};
-template<class F>
+template <class F>
inline void HeapWalker::ForEachPtrInRange(const Range& range, F&& f) {
uintptr_t begin = (range.begin + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1);
// TODO(ccross): we might need to consider a pointer to the end of a buffer
@@ -118,7 +118,7 @@
}
}
-template<class F>
+template <class F>
inline void HeapWalker::ForEachAllocation(F&& f) {
for (auto& it : allocations_) {
const Range& range = it.first;
@@ -127,4 +127,6 @@
}
}
+} // namespace android
+
#endif
diff --git a/libmemunreachable/Leak.h b/libmemunreachable/Leak.h
index eaeeea7..de64b64 100644
--- a/libmemunreachable/Leak.h
+++ b/libmemunreachable/Leak.h
@@ -26,9 +26,9 @@
// as a key in std::unordered_map.
namespace std {
-template<>
-struct hash<Leak::Backtrace> {
- std::size_t operator()(const Leak::Backtrace& key) const {
+template <>
+struct hash<android::Leak::Backtrace> {
+ std::size_t operator()(const android::Leak::Backtrace& key) const {
std::size_t seed = 0;
hash_combine(seed, key.num_frames);
@@ -40,7 +40,7 @@
}
private:
- template<typename T>
+ template <typename T>
inline void hash_combine(std::size_t& seed, const T& v) const {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
@@ -49,9 +49,12 @@
} // namespace std
+namespace android {
+
static bool operator==(const Leak::Backtrace& lhs, const Leak::Backtrace& rhs) {
return (lhs.num_frames == rhs.num_frames) &&
- memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0;
+ memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0;
+}
}
#endif
diff --git a/libmemunreachable/LeakFolding.cpp b/libmemunreachable/LeakFolding.cpp
index be4d20c..69f320c 100644
--- a/libmemunreachable/LeakFolding.cpp
+++ b/libmemunreachable/LeakFolding.cpp
@@ -22,6 +22,8 @@
#include "Tarjan.h"
#include "log.h"
+namespace android {
+
// Converts possibly cyclic graph of leaks to a DAG by combining
// strongly-connected components into a object, stored in the scc pointer
// of each node in the component.
@@ -31,11 +33,11 @@
Allocator<SCCInfo> scc_allocator = allocator_;
- for (auto& scc_nodes: scc_list) {
+ for (auto& scc_nodes : scc_list) {
Allocator<SCCInfo>::unique_ptr leak_scc;
leak_scc = scc_allocator.make_unique(scc_allocator);
- for (auto& node: scc_nodes) {
+ for (auto& node : scc_nodes) {
node->ptr->scc = leak_scc.get();
leak_scc->count++;
leak_scc->size += node->ptr->range.size();
@@ -46,7 +48,7 @@
for (auto& it : leak_map_) {
LeakInfo& leak = it.second;
- for (auto& ref: leak.node.references_out) {
+ for (auto& ref : leak.node.references_out) {
if (leak.scc != ref->ptr->scc) {
leak.scc->node.Edge(&ref->ptr->scc->node);
}
@@ -55,17 +57,14 @@
}
void LeakFolding::AccumulateLeaks(SCCInfo* dominator) {
- std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_,
- [&](SCCInfo* scc) {
- if (scc->accumulator != dominator) {
- scc->accumulator = dominator;
- dominator->cuumulative_size += scc->size;
- dominator->cuumulative_count += scc->count;
- scc->node.Foreach([&](SCCInfo* ref) {
- walk(ref);
- });
- }
- });
+ std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, [&](SCCInfo* scc) {
+ if (scc->accumulator != dominator) {
+ scc->accumulator = dominator;
+ dominator->cuumulative_size += scc->size;
+ dominator->cuumulative_count += scc->count;
+ scc->node.Foreach([&](SCCInfo* ref) { walk(ref); });
+ }
+ });
walk(dominator);
}
@@ -73,27 +72,25 @@
Allocator<LeakInfo> leak_allocator = allocator_;
// Find all leaked allocations insert them into leak_map_ and leak_graph_
- heap_walker_.ForEachAllocation(
- [&](const Range& range, HeapWalker::AllocationInfo& allocation) {
- if (!allocation.referenced_from_root) {
- auto it = leak_map_.emplace(std::piecewise_construct,
- std::forward_as_tuple(range),
- std::forward_as_tuple(range, allocator_));
- LeakInfo& leak = it.first->second;
- leak_graph_.push_back(&leak.node);
- }
- });
+ heap_walker_.ForEachAllocation([&](const Range& range, HeapWalker::AllocationInfo& allocation) {
+ if (!allocation.referenced_from_root) {
+ auto it = leak_map_.emplace(std::piecewise_construct, std::forward_as_tuple(range),
+ std::forward_as_tuple(range, allocator_));
+ LeakInfo& leak = it.first->second;
+ leak_graph_.push_back(&leak.node);
+ }
+ });
// Find references between leaked allocations and connect them in leak_graph_
for (auto& it : leak_map_) {
LeakInfo& leak = it.second;
heap_walker_.ForEachPtrInRange(leak.range,
- [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) {
- if (!ptr_info->referenced_from_root) {
- LeakInfo* ptr_leak = &leak_map_.at(ptr_range);
- leak.node.Edge(&ptr_leak->node);
- }
- });
+ [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) {
+ if (!ptr_info->referenced_from_root) {
+ LeakInfo* ptr_leak = &leak_map_.at(ptr_range);
+ leak.node.Edge(&ptr_leak->node);
+ }
+ });
}
// Convert the cyclic graph to a DAG by grouping strongly connected components
@@ -110,8 +107,8 @@
return true;
}
-bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked,
- size_t* num_leaks_out, size_t* leak_bytes_out) {
+bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked, size_t* num_leaks_out,
+ size_t* leak_bytes_out) {
size_t num_leaks = 0;
size_t leak_bytes = 0;
for (auto& it : leak_map_) {
@@ -123,9 +120,8 @@
for (auto& it : leak_map_) {
const LeakInfo& leak = it.second;
if (leak.scc->dominator) {
- leaked.emplace_back(Leak{leak.range,
- leak.scc->cuumulative_count - 1,
- leak.scc->cuumulative_size - leak.range.size()});
+ leaked.emplace_back(Leak{leak.range, leak.scc->cuumulative_count - 1,
+ leak.scc->cuumulative_size - leak.range.size()});
}
}
@@ -138,3 +134,5 @@
return true;
}
+
+} // namespace android
diff --git a/libmemunreachable/LeakFolding.h b/libmemunreachable/LeakFolding.h
index 9c6a525..09affac 100644
--- a/libmemunreachable/LeakFolding.h
+++ b/libmemunreachable/LeakFolding.h
@@ -19,11 +19,16 @@
#include "HeapWalker.h"
+namespace android {
+
class LeakFolding {
public:
LeakFolding(Allocator<void> allocator, HeapWalker& heap_walker)
- : allocator_(allocator), heap_walker_(heap_walker),
- leak_map_(allocator), leak_graph_(allocator), leak_scc_(allocator) {}
+ : allocator_(allocator),
+ heap_walker_(heap_walker),
+ leak_map_(allocator),
+ leak_graph_(allocator),
+ leak_scc_(allocator) {}
bool FoldLeaks();
@@ -33,8 +38,7 @@
size_t referenced_size;
};
- bool Leaked(allocator::vector<Leak>& leaked,
- size_t* num_leaks_out, size_t* leak_bytes_out);
+ bool Leaked(allocator::vector<Leak>& leaked, size_t* num_leaks_out, size_t* leak_bytes_out);
private:
DISALLOW_COPY_AND_ASSIGN(LeakFolding);
@@ -54,9 +58,15 @@
bool dominator;
SCCInfo* accumulator;
- explicit SCCInfo(Allocator<SCCInfo> allocator) : node(this, allocator),
- count(0), size(0), cuumulative_count(0), cuumulative_size(0),
- dominator(false), accumulator(nullptr) {}
+ explicit SCCInfo(Allocator<SCCInfo> allocator)
+ : node(this, allocator),
+ count(0),
+ size(0),
+ cuumulative_count(0),
+ cuumulative_size(0),
+ dominator(false),
+ accumulator(nullptr) {}
+
private:
SCCInfo(SCCInfo&&) = delete;
DISALLOW_COPY_AND_ASSIGN(SCCInfo);
@@ -71,8 +81,7 @@
SCCInfo* scc;
LeakInfo(const Range& range, Allocator<LeakInfo> allocator)
- : node(this, allocator), range(range),
- scc(nullptr) {}
+ : node(this, allocator), range(range), scc(nullptr) {}
private:
DISALLOW_COPY_AND_ASSIGN(LeakInfo);
@@ -86,4 +95,6 @@
allocator::vector<Allocator<SCCInfo>::unique_ptr> leak_scc_;
};
-#endif // LIBMEMUNREACHABLE_LEAK_FOLDING_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_LEAK_FOLDING_H_
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp
index 78117e2..8ea9ad6 100644
--- a/libmemunreachable/LeakPipe.cpp
+++ b/libmemunreachable/LeakPipe.cpp
@@ -21,9 +21,11 @@
#include "log.h"
+namespace android {
+
bool LeakPipe::SendFd(int sock, int fd) {
- struct msghdr hdr{};
- struct iovec iov{};
+ struct msghdr hdr {};
+ struct iovec iov {};
unsigned int data = 0xfdfdfdfd;
alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
@@ -56,8 +58,8 @@
}
int LeakPipe::ReceiveFd(int sock) {
- struct msghdr hdr{};
- struct iovec iov{};
+ struct msghdr hdr {};
+ struct iovec iov {};
unsigned int data;
alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
@@ -87,3 +89,5 @@
return *(int*)CMSG_DATA(cmsg);
}
+
+} // namespace android
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h
index 3ea2d8f..94d4aa4 100644
--- a/libmemunreachable/LeakPipe.h
+++ b/libmemunreachable/LeakPipe.h
@@ -26,6 +26,8 @@
#include "ScopedPipe.h"
#include "log.h"
+namespace android {
+
// LeakPipe implements a pipe that can transfer vectors of simple objects
// between processes. The pipe is created in the sending process and
// transferred over a socketpair that was created before forking. This ensures
@@ -34,15 +36,13 @@
class LeakPipe {
public:
LeakPipe() {
- int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_);
+ int ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv_);
if (ret < 0) {
MEM_LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
}
}
- ~LeakPipe() {
- Close();
- }
+ ~LeakPipe() { Close(); }
void Close() {
close(sv_[0]);
@@ -77,13 +77,9 @@
public:
LeakPipeBase() : fd_(-1) {}
- ~LeakPipeBase() {
- Close();
- }
+ ~LeakPipeBase() { Close(); }
- void SetFd(int fd) {
- fd_ = fd;
- }
+ void SetFd(int fd) { fd_ = fd; }
void Close() {
close(fd_);
@@ -101,7 +97,7 @@
public:
using LeakPipeBase::LeakPipeBase;
- template<typename T>
+ template <typename T>
bool Send(const T& value) {
ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T)));
if (ret < 0) {
@@ -115,7 +111,7 @@
return true;
}
- template<class T, class Alloc = std::allocator<T>>
+ template <class T, class Alloc = std::allocator<T>>
bool SendVector(const std::vector<T, Alloc>& vector) {
size_t size = vector.size() * sizeof(T);
if (!Send(size)) {
@@ -139,7 +135,7 @@
public:
using LeakPipeBase::LeakPipeBase;
- template<typename T>
+ template <typename T>
bool Receive(T* value) {
ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T)));
if (ret < 0) {
@@ -153,7 +149,7 @@
return true;
}
- template<class T, class Alloc = std::allocator<T>>
+ template <class T, class Alloc = std::allocator<T>>
bool ReceiveVector(std::vector<T, Alloc>& vector) {
size_t size = 0;
if (!Receive(&size)) {
@@ -178,16 +174,11 @@
return true;
}
-
};
- LeakPipeReceiver& Receiver() {
- return receiver_;
- }
+ LeakPipeReceiver& Receiver() { return receiver_; }
- LeakPipeSender& Sender() {
- return sender_;
- }
+ LeakPipeSender& Sender() { return sender_; }
private:
LeakPipeReceiver receiver_;
@@ -198,4 +189,6 @@
int sv_[2];
};
-#endif // LIBMEMUNREACHABLE_LEAK_PIPE_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_LEAK_PIPE_H_
diff --git a/libmemunreachable/LineBuffer.cpp b/libmemunreachable/LineBuffer.cpp
index d3580c0..4ea0542 100644
--- a/libmemunreachable/LineBuffer.cpp
+++ b/libmemunreachable/LineBuffer.cpp
@@ -23,8 +23,10 @@
#include "LineBuffer.h"
-LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len) : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {
-}
+namespace android {
+
+LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len)
+ : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {}
bool LineBuffer::GetLine(char** line, size_t* line_len) {
while (true) {
@@ -60,3 +62,5 @@
bytes_ += bytes;
}
}
+
+} // namespace android
diff --git a/libmemunreachable/LineBuffer.h b/libmemunreachable/LineBuffer.h
index a015c46..cc6cd0c 100644
--- a/libmemunreachable/LineBuffer.h
+++ b/libmemunreachable/LineBuffer.h
@@ -19,6 +19,8 @@
#include <stdint.h>
+namespace android {
+
class LineBuffer {
public:
LineBuffer(int fd, char* buffer, size_t buffer_len);
@@ -33,4 +35,6 @@
size_t bytes_ = 0;
};
-#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H
+} // namespace android
+
+#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H
diff --git a/libmemunreachable/LinkedList.h b/libmemunreachable/LinkedList.h
index 132842d..36fe9fd 100644
--- a/libmemunreachable/LinkedList.h
+++ b/libmemunreachable/LinkedList.h
@@ -17,44 +17,47 @@
#ifndef LIBMEMUNREACHABLE_LINKED_LIST_H_
#define LIBMEMUNREACHABLE_LINKED_LIST_H_
-template<class T>
+namespace android {
+
+template <class T>
class LinkedList {
-public:
- LinkedList() : next_(this), prev_(this), data_() {}
- explicit LinkedList(T data) : LinkedList() {
- data_ = data;
- }
- ~LinkedList() {}
- void insert(LinkedList<T>& node) {
- assert(node.empty());
- node.next_ = this->next_;
- node.next_->prev_ = &node;
- this->next_ = &node;
- node.prev_ = this;
- }
- void remove() {
- this->next_->prev_ = this->prev_;
- this->prev_->next_ = this->next_;
- this->next_ = this;
- this->prev_ = this;
- }
- T data() { return data_; }
- bool empty() { return next_ == this && prev_ == this; }
- LinkedList<T> *next() { return next_; }
-private:
- LinkedList<T> *next_;
- LinkedList<T> *prev_;
- T data_;
+ public:
+ LinkedList() : next_(this), prev_(this), data_() {}
+ explicit LinkedList(T data) : LinkedList() { data_ = data; }
+ ~LinkedList() {}
+ void insert(LinkedList<T>& node) {
+ assert(node.empty());
+ node.next_ = this->next_;
+ node.next_->prev_ = &node;
+ this->next_ = &node;
+ node.prev_ = this;
+ }
+ void remove() {
+ this->next_->prev_ = this->prev_;
+ this->prev_->next_ = this->next_;
+ this->next_ = this;
+ this->prev_ = this;
+ }
+ T data() { return data_; }
+ bool empty() { return next_ == this && prev_ == this; }
+ LinkedList<T>* next() { return next_; }
+
+ private:
+ LinkedList<T>* next_;
+ LinkedList<T>* prev_;
+ T data_;
};
-template<class T>
+template <class T>
class LinkedListHead {
-public:
- LinkedListHead() : node_() {}
- ~LinkedListHead() {}
+ public:
+ LinkedListHead() : node_() {}
+ ~LinkedListHead() {}
-private:
- LinkedList<T> node_;
+ private:
+ LinkedList<T> node_;
};
+} // namespace android
+
#endif
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index e7c0beb..5e062fd 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -15,18 +15,20 @@
*/
#include <inttypes.h>
+#include <string.h>
#include <functional>
#include <iomanip>
#include <mutex>
-#include <string>
#include <sstream>
+#include <string>
#include <unordered_map>
-#include <backtrace.h>
#include <android-base/macros.h>
+#include <backtrace.h>
#include "Allocator.h"
+#include "Binder.h"
#include "HeapWalker.h"
#include "Leak.h"
#include "LeakFolding.h"
@@ -37,30 +39,34 @@
#include "Semaphore.h"
#include "ThreadCapture.h"
-#include "memunreachable/memunreachable.h"
#include "bionic.h"
#include "log.h"
-
-const size_t Leak::contents_length;
+#include "memunreachable/memunreachable.h"
using namespace std::chrono_literals;
+namespace android {
+
+const size_t Leak::contents_length;
+
class MemUnreachable {
public:
- MemUnreachable(pid_t pid, Allocator<void> allocator) : pid_(pid), allocator_(allocator),
- heap_walker_(allocator_) {}
+ MemUnreachable(pid_t pid, Allocator<void> allocator)
+ : pid_(pid), allocator_(allocator), heap_walker_(allocator_) {}
bool CollectAllocations(const allocator::vector<ThreadInfo>& threads,
- const allocator::vector<Mapping>& mappings);
- bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
- size_t* num_leaks, size_t* leak_bytes);
+ const allocator::vector<Mapping>& mappings,
+ const allocator::vector<uintptr_t>& refs);
+ bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit, size_t* num_leaks,
+ size_t* leak_bytes);
size_t Allocations() { return heap_walker_.Allocations(); }
size_t AllocationBytes() { return heap_walker_.AllocationBytes(); }
+
private:
bool ClassifyMappings(const allocator::vector<Mapping>& mappings,
- allocator::vector<Mapping>& heap_mappings,
- allocator::vector<Mapping>& anon_mappings,
- allocator::vector<Mapping>& globals_mappings,
- allocator::vector<Mapping>& stack_mappings);
+ allocator::vector<Mapping>& heap_mappings,
+ allocator::vector<Mapping>& anon_mappings,
+ allocator::vector<Mapping>& globals_mappings,
+ allocator::vector<Mapping>& stack_mappings);
DISALLOW_COPY_AND_ASSIGN(MemUnreachable);
pid_t pid_;
Allocator<void> allocator_;
@@ -68,16 +74,18 @@
};
static void HeapIterate(const Mapping& heap_mapping,
- const std::function<void(uintptr_t, size_t)>& func) {
+ const std::function<void(uintptr_t, size_t)>& func) {
malloc_iterate(heap_mapping.begin, heap_mapping.end - heap_mapping.begin,
- [](uintptr_t base, size_t size, void* arg) {
- auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg);
- (*f)(base, size);
- }, const_cast<void*>(reinterpret_cast<const void*>(&func)));
+ [](uintptr_t base, size_t size, void* arg) {
+ auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg);
+ (*f)(base, size);
+ },
+ const_cast<void*>(reinterpret_cast<const void*>(&func)));
}
bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
- const allocator::vector<Mapping>& mappings) {
+ const allocator::vector<Mapping>& mappings,
+ const allocator::vector<uintptr_t>& refs) {
MEM_ALOGI("searching process %d for allocations", pid_);
allocator::vector<Mapping> heap_mappings{mappings};
allocator::vector<Mapping> anon_mappings{mappings};
@@ -113,13 +121,15 @@
heap_walker_.Root(thread_it->regs);
}
+ heap_walker_.Root(refs);
+
MEM_ALOGI("searching done");
return true;
}
-bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks,
- size_t limit, size_t* num_leaks, size_t* leak_bytes) {
+bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
+ size_t* num_leaks, size_t* leak_bytes) {
MEM_ALOGI("sweeping process %d for unreachable memory", pid_);
leaks.clear();
@@ -127,7 +137,6 @@
return false;
}
-
allocator::vector<Range> leaked1{allocator_};
heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
@@ -152,12 +161,12 @@
// in backtrace_map.
leaks.reserve(leaked.size());
- for (auto& it: leaked) {
+ for (auto& it : leaked) {
leaks.emplace_back();
Leak* leak = &leaks.back();
- ssize_t num_backtrace_frames = malloc_backtrace(reinterpret_cast<void*>(it.range.begin),
- leak->backtrace.frames, leak->backtrace.max_frames);
+ ssize_t num_backtrace_frames = malloc_backtrace(
+ reinterpret_cast<void*>(it.range.begin), leak->backtrace.frames, leak->backtrace.max_frames);
if (num_backtrace_frames > 0) {
leak->backtrace.num_frames = num_backtrace_frames;
@@ -183,14 +192,13 @@
leak->referenced_size = it.referenced_size;
leak->total_size = leak->size + leak->referenced_size;
memcpy(leak->contents, reinterpret_cast<void*>(it.range.begin),
- std::min(leak->size, Leak::contents_length));
+ std::min(leak->size, Leak::contents_length));
}
MEM_ALOGI("folding done");
- std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) {
- return a.total_size > b.total_size;
- });
+ std::sort(leaks.begin(), leaks.end(),
+ [](const Leak& a, const Leak& b) { return a.total_size > b.total_size; });
if (leaks.size() > limit) {
leaks.resize(limit);
@@ -205,11 +213,10 @@
}
bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings,
- allocator::vector<Mapping>& heap_mappings,
- allocator::vector<Mapping>& anon_mappings,
- allocator::vector<Mapping>& globals_mappings,
- allocator::vector<Mapping>& stack_mappings)
-{
+ allocator::vector<Mapping>& heap_mappings,
+ allocator::vector<Mapping>& anon_mappings,
+ allocator::vector<Mapping>& globals_mappings,
+ allocator::vector<Mapping>& stack_mappings) {
heap_mappings.clear();
anon_mappings.clear();
globals_mappings.clear();
@@ -245,7 +252,8 @@
stack_mappings.emplace_back(*it);
} else if (mapping_name.size() == 0) {
globals_mappings.emplace_back(*it);
- } else if (has_prefix(mapping_name, "[anon:") && mapping_name != "[anon:leak_detector_malloc]") {
+ } else if (has_prefix(mapping_name, "[anon:") &&
+ mapping_name != "[anon:leak_detector_malloc]") {
// TODO(ccross): it would be nice to treat named anonymous mappings as
// possible leaks, but naming something in a .bss or .data section makes
// it impossible to distinguish them from mmaped and then named mappings.
@@ -256,7 +264,7 @@
return true;
}
-template<typename T>
+template <typename T>
static inline const char* plural(T val) {
return (val == 1) ? "" : "s";
}
@@ -279,6 +287,7 @@
ThreadCapture thread_capture(parent_pid, heap);
allocator::vector<ThreadInfo> thread_info(heap);
allocator::vector<Mapping> mappings(heap);
+ allocator::vector<uintptr_t> refs(heap);
// ptrace all the threads
if (!thread_capture.CaptureThreads()) {
@@ -298,6 +307,11 @@
return 1;
}
+ if (!BinderReferences(refs)) {
+ continue_parent_sem.Post();
+ return 1;
+ }
+
// malloc must be enabled to call fork, at_fork handlers take the same
// locks as ScopedDisableMalloc. All threads are paused in ptrace, so
// memory state is still consistent. Unfreeze the original thread so it
@@ -323,7 +337,7 @@
MemUnreachable unreachable{parent_pid, heap};
- if (!unreachable.CollectAllocations(thread_info, mappings)) {
+ if (!unreachable.CollectAllocations(thread_info, mappings, refs)) {
_exit(2);
}
size_t num_allocations = unreachable.Allocations();
@@ -403,7 +417,6 @@
}
std::string Leak::ToString(bool log_contents) const {
-
std::ostringstream oss;
oss << " " << std::dec << size;
@@ -492,8 +505,8 @@
oss << std::endl;
for (auto it = leaks.begin(); it != leaks.end(); it++) {
- oss << it->ToString(log_contents);
- oss << std::endl;
+ oss << it->ToString(log_contents);
+ oss << std::endl;
}
return oss.str();
@@ -502,15 +515,20 @@
std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
UnreachableMemoryInfo info;
if (!GetUnreachableMemory(info, limit)) {
- return "Failed to get unreachable memory\n";
+ return "Failed to get unreachable memory\n"
+ "If you are trying to get unreachable memory from a system app\n"
+ "(like com.android.systemui), disable selinux first using\n"
+ "setenforce 0\n";
}
return info.ToString(log_contents);
}
+} // namespace android
+
bool LogUnreachableMemory(bool log_contents, size_t limit) {
- UnreachableMemoryInfo info;
- if (!GetUnreachableMemory(info, limit)) {
+ android::UnreachableMemoryInfo info;
+ if (!android::GetUnreachableMemory(info, limit)) {
return false;
}
@@ -520,10 +538,9 @@
return true;
}
-
bool NoLeaks() {
- UnreachableMemoryInfo info;
- if (!GetUnreachableMemory(info, 0)) {
+ android::UnreachableMemoryInfo info;
+ if (!android::GetUnreachableMemory(info, 0)) {
return false;
}
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
index 57b2321..9a06870 100644
--- a/libmemunreachable/ProcessMappings.cpp
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include <inttypes.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <string.h>
#include <unistd.h>
@@ -25,6 +25,8 @@
#include "ProcessMappings.h"
#include "log.h"
+namespace android {
+
// This function is not re-entrant since it uses a static buffer for
// the line data.
bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) {
@@ -42,8 +44,8 @@
int name_pos;
char perms[5];
Mapping mapping{};
- if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n",
- &mapping.begin, &mapping.end, perms, &name_pos) == 3) {
+ if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n", &mapping.begin,
+ &mapping.end, perms, &name_pos) == 3) {
if (perms[0] == 'r') {
mapping.read = true;
}
@@ -64,3 +66,5 @@
}
return true;
}
+
+} // namespace android
diff --git a/libmemunreachable/ProcessMappings.h b/libmemunreachable/ProcessMappings.h
index d3b7496..a0e97e9 100644
--- a/libmemunreachable/ProcessMappings.h
+++ b/libmemunreachable/ProcessMappings.h
@@ -19,6 +19,8 @@
#include "Allocator.h"
+namespace android {
+
struct Mapping {
uintptr_t begin;
uintptr_t end;
@@ -33,4 +35,6 @@
// the line data.
bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings);
-#endif // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
index 73b0493..aca2a82 100644
--- a/libmemunreachable/PtracerThread.cpp
+++ b/libmemunreachable/PtracerThread.cpp
@@ -23,17 +23,19 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <unistd.h>
#include "android-base/macros.h"
+#include "PtracerThread.h"
#include "anon_vma_naming.h"
#include "log.h"
-#include "PtracerThread.h"
+
+namespace android {
class Stack {
public:
@@ -41,7 +43,7 @@
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
page_size_ = sysconf(_SC_PAGE_SIZE);
- size_ += page_size_*2; // guard pages
+ size_ += page_size_ * 2; // guard pages
base_ = mmap(NULL, size_, prot, flags, -1, 0);
if (base_ == MAP_FAILED) {
base_ = NULL;
@@ -52,22 +54,20 @@
mprotect(base_, page_size_, PROT_NONE);
mprotect(top(), page_size_, PROT_NONE);
};
- ~Stack() {
- munmap(base_, size_);
- };
+ ~Stack() { munmap(base_, size_); };
void* top() {
return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_) + size_ - page_size_);
};
+
private:
DISALLOW_COPY_AND_ASSIGN(Stack);
- void *base_;
+ void* base_;
size_t size_;
size_t page_size_;
};
-PtracerThread::PtracerThread(const std::function<int()>& func) :
- child_pid_(0) {
+PtracerThread::PtracerThread(const std::function<int()>& func) : child_pid_(0) {
stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN);
if (stack_->top() == nullptr) {
MEM_LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
@@ -93,14 +93,13 @@
std::unique_lock<std::mutex> lk(m_);
// Convert from void(*)(void*) to lambda with captures
- auto proxy = [](void *arg) -> int {
+ auto proxy = [](void* arg) -> int {
prctl(PR_SET_NAME, "libmemunreachable ptrace thread");
return (*reinterpret_cast<std::function<int()>*>(arg))();
};
- child_pid_ = clone(proxy, stack_->top(),
- CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/,
- reinterpret_cast<void*>(&func_));
+ child_pid_ = clone(proxy, stack_->top(), CLONE_VM | CLONE_FS | CLONE_FILES /*|CLONE_UNTRACED*/,
+ reinterpret_cast<void*>(&func_));
if (child_pid_ < 0) {
MEM_ALOGE("failed to clone child: %s", strerror(errno));
return false;
@@ -151,3 +150,5 @@
void PtracerThread::ClearTracer() {
prctl(PR_SET_PTRACER, 0);
}
+
+} // namespace android
diff --git a/libmemunreachable/PtracerThread.h b/libmemunreachable/PtracerThread.h
index f88b599..4f9c420 100644
--- a/libmemunreachable/PtracerThread.h
+++ b/libmemunreachable/PtracerThread.h
@@ -24,6 +24,8 @@
#include "Allocator.h"
+namespace android {
+
class Stack;
// PtracerThread is similar to std::thread, except that it creates a "thread"
@@ -36,6 +38,7 @@
~PtracerThread();
bool Start();
int Join();
+
private:
void SetTracer(pid_t);
void ClearTracer();
@@ -47,4 +50,6 @@
pid_t child_pid_;
};
-#endif // LIBMEMUNREACHABLE_PTRACER_THREAD_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_PTRACER_THREAD_H_
diff --git a/libmemunreachable/ScopedAlarm.h b/libmemunreachable/ScopedAlarm.h
index 287f479..bb50b9e 100644
--- a/libmemunreachable/ScopedAlarm.h
+++ b/libmemunreachable/ScopedAlarm.h
@@ -23,15 +23,15 @@
#include <chrono>
#include <functional>
+namespace android {
+
class ScopedAlarm {
public:
ScopedAlarm(std::chrono::microseconds us, std::function<void()> func) {
func_ = func;
- struct sigaction oldact{};
- struct sigaction act{};
- act.sa_handler = [](int) {
- ScopedAlarm::func_();
- };
+ struct sigaction oldact {};
+ struct sigaction act {};
+ act.sa_handler = [](int) { ScopedAlarm::func_(); };
sigaction(SIGALRM, &act, &oldact);
std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
@@ -43,11 +43,15 @@
~ScopedAlarm() {
itimerval t = itimerval{};
setitimer(ITIMER_REAL, &t, NULL);
- struct sigaction act{};
+ struct sigaction act {};
act.sa_handler = SIG_DFL;
sigaction(SIGALRM, &act, NULL);
}
+
private:
static std::function<void()> func_;
};
+
+} // namespace android
+
#endif
diff --git a/libmemunreachable/ScopedDisableMalloc.h b/libmemunreachable/ScopedDisableMalloc.h
index 758d317..655e826 100644
--- a/libmemunreachable/ScopedDisableMalloc.h
+++ b/libmemunreachable/ScopedDisableMalloc.h
@@ -21,16 +21,16 @@
#include "android-base/macros.h"
+#include "ScopedAlarm.h"
#include "bionic.h"
#include "log.h"
-#include "ScopedAlarm.h"
-class DisableMallocGuard{
+namespace android {
+
+class DisableMallocGuard {
public:
- DisableMallocGuard() : disabled_(false){}
- ~DisableMallocGuard() {
- Enable();
- }
+ DisableMallocGuard() : disabled_(false) {}
+ ~DisableMallocGuard() { Enable(); }
void Disable() {
if (!disabled_) {
@@ -45,6 +45,7 @@
disabled_ = false;
}
}
+
private:
DISALLOW_COPY_AND_ASSIGN(DisableMallocGuard);
bool disabled_;
@@ -59,13 +60,9 @@
// here.
class ScopedDisableMalloc {
public:
- ScopedDisableMalloc() {
- disable_malloc_.Disable();
- }
+ ScopedDisableMalloc() { disable_malloc_.Disable(); }
- ~ScopedDisableMalloc() {
- disable_malloc_.Enable();
- }
+ ~ScopedDisableMalloc() { disable_malloc_.Enable(); }
private:
DISALLOW_COPY_AND_ASSIGN(ScopedDisableMalloc);
@@ -74,18 +71,15 @@
class ScopedDisableMallocTimeout {
public:
- explicit ScopedDisableMallocTimeout(std::chrono::milliseconds timeout = std::chrono::milliseconds(2000)) :
- timeout_(timeout), timed_out_(false), disable_malloc_() {
+ explicit ScopedDisableMallocTimeout(
+ std::chrono::milliseconds timeout = std::chrono::milliseconds(2000))
+ : timeout_(timeout), timed_out_(false), disable_malloc_() {
Disable();
}
- ~ScopedDisableMallocTimeout() {
- Enable();
- }
+ ~ScopedDisableMallocTimeout() { Enable(); }
- bool timed_out() {
- return timed_out_;
- }
+ bool timed_out() { return timed_out_; }
void Enable() {
disable_malloc_.Enable();
@@ -110,4 +104,6 @@
DisableMallocGuard disable_malloc_;
};
-#endif // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index 7f44953..adabfd8 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -21,6 +21,8 @@
#include "log.h"
+namespace android {
+
class ScopedPipe {
public:
ScopedPipe() : pipefd_{-1, -1} {
@@ -29,28 +31,22 @@
MEM_LOG_ALWAYS_FATAL("failed to open pipe");
}
}
- ~ScopedPipe() {
- Close();
- }
+ ~ScopedPipe() { Close(); }
ScopedPipe(ScopedPipe&& other) {
SetReceiver(other.ReleaseReceiver());
SetSender(other.ReleaseSender());
}
- ScopedPipe& operator = (ScopedPipe&& other) {
+ ScopedPipe& operator=(ScopedPipe&& other) {
SetReceiver(other.ReleaseReceiver());
SetSender(other.ReleaseSender());
return *this;
}
- void CloseReceiver() {
- close(ReleaseReceiver());
- }
+ void CloseReceiver() { close(ReleaseReceiver()); }
- void CloseSender() {
- close(ReleaseSender());
- }
+ void CloseSender() { close(ReleaseSender()); }
void Close() {
CloseReceiver();
@@ -78,4 +74,7 @@
int pipefd_[2];
};
+
+} // namespace android
+
#endif
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index ada2ae4..ff53fad 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -26,18 +26,18 @@
#include "log.h"
+namespace android {
+
class ScopedSignalHandler {
public:
using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>;
explicit ScopedSignalHandler(Allocator<Fn> allocator) : allocator_(allocator), signal_(-1) {}
- ~ScopedSignalHandler() {
- reset();
- }
+ ~ScopedSignalHandler() { reset(); }
template <class F>
void install(int signal, F&& f) {
- MEM_LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
+ if (signal_ != -1) MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed");
handler_ = SignalFn(std::allocator_arg, allocator_,
[=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
@@ -65,7 +65,6 @@
}
}
-
private:
using SignalFn = std::function<void(int, siginfo_t*, void*)>;
DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler);
@@ -77,4 +76,6 @@
static SignalFn handler_;
};
-#endif // LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
diff --git a/libmemunreachable/Semaphore.h b/libmemunreachable/Semaphore.h
index 6bcf4ea..cd73972 100644
--- a/libmemunreachable/Semaphore.h
+++ b/libmemunreachable/Semaphore.h
@@ -22,6 +22,8 @@
#include "android-base/macros.h"
+namespace android {
+
class Semaphore {
public:
explicit Semaphore(int count = 0) : count_(count) {}
@@ -29,7 +31,7 @@
void Wait(std::chrono::milliseconds ms) {
std::unique_lock<std::mutex> lk(m_);
- cv_.wait_for(lk, ms, [&]{
+ cv_.wait_for(lk, ms, [&] {
if (count_ > 0) {
count_--;
return true;
@@ -44,6 +46,7 @@
}
cv_.notify_one();
}
+
private:
DISALLOW_COPY_AND_ASSIGN(Semaphore);
@@ -52,5 +55,6 @@
std::condition_variable cv_;
};
+} // namespace android
-#endif // LIBMEMUNREACHABLE_SEMAPHORE_H_
+#endif // LIBMEMUNREACHABLE_SEMAPHORE_H_
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h
index 2546341..355679f 100644
--- a/libmemunreachable/Tarjan.h
+++ b/libmemunreachable/Tarjan.h
@@ -24,7 +24,9 @@
#include "Allocator.h"
-template<class T>
+namespace android {
+
+template <class T>
class Node {
public:
allocator::set<Node<T>*> references_in;
@@ -34,39 +36,41 @@
T* ptr;
- Node(T* ptr, Allocator<Node> allocator) : references_in(allocator), references_out(allocator),
- ptr(ptr) {};
+ Node(T* ptr, Allocator<Node> allocator)
+ : references_in(allocator), references_out(allocator), ptr(ptr){};
Node(Node&& rhs) = default;
void Edge(Node<T>* ref) {
references_out.emplace(ref);
ref->references_in.emplace(this);
}
- template<class F>
+ template <class F>
void Foreach(F&& f) {
- for (auto& node: references_out) {
+ for (auto& node : references_out) {
f(node->ptr);
}
}
+
private:
DISALLOW_COPY_AND_ASSIGN(Node<T>);
};
-template<class T>
+template <class T>
using Graph = allocator::vector<Node<T>*>;
-template<class T>
+template <class T>
using SCC = allocator::vector<Node<T>*>;
-template<class T>
+template <class T>
using SCCList = allocator::vector<SCC<T>>;
-template<class T>
+template <class T>
class TarjanAlgorithm {
public:
- explicit TarjanAlgorithm(Allocator<void> allocator) : index_(0),
- stack_(allocator), components_(allocator) {}
+ explicit TarjanAlgorithm(Allocator<void> allocator)
+ : index_(0), stack_(allocator), components_(allocator) {}
void Execute(Graph<T>& graph, SCCList<T>& out);
+
private:
static constexpr size_t UNDEFINED_INDEX = static_cast<size_t>(-1);
void Tarjan(Node<T>* vertex, Graph<T>& graph);
@@ -76,17 +80,17 @@
SCCList<T> components_;
};
-template<class T>
+template <class T>
void TarjanAlgorithm<T>::Execute(Graph<T>& graph, SCCList<T>& out) {
stack_.clear();
components_.clear();
index_ = 0;
- for (auto& it: graph) {
+ for (auto& it : graph) {
it->index = UNDEFINED_INDEX;
it->lowlink = UNDEFINED_INDEX;
}
- for (auto& it: graph) {
+ for (auto& it : graph) {
if (it->index == UNDEFINED_INDEX) {
Tarjan(it, graph);
}
@@ -94,14 +98,14 @@
out.swap(components_);
}
-template<class T>
+template <class T>
void TarjanAlgorithm<T>::Tarjan(Node<T>* vertex, Graph<T>& graph) {
assert(vertex->index == UNDEFINED_INDEX);
vertex->index = index_;
vertex->lowlink = index_;
index_++;
stack_.push_back(vertex);
- for (auto& it: vertex->references_out) {
+ for (auto& it : vertex->references_out) {
Node<T>* vertex_next = it;
if (vertex_next->index == UNDEFINED_INDEX) {
Tarjan(vertex_next, graph);
@@ -123,10 +127,12 @@
}
}
-template<class T>
+template <class T>
void Tarjan(Graph<T>& graph, SCCList<T>& out) {
TarjanAlgorithm<T> tarjan{graph.get_allocator()};
tarjan.Execute(graph, out);
}
-#endif // LIBMEMUNREACHABLE_TARJAN_H_
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_TARJAN_H_
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
index 3891f2d..45eb55d 100644
--- a/libmemunreachable/ThreadCapture.cpp
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -21,13 +21,13 @@
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
-#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/wait.h>
+#include <unistd.h>
#include <map>
#include <memory>
@@ -39,6 +39,8 @@
#include "Allocator.h"
#include "log.h"
+namespace android {
+
// bionic interfaces used:
// atoi
// strlcat
@@ -50,12 +52,12 @@
// Convert a pid > 0 to a string. sprintf might allocate, so we can't use it.
// Returns a pointer somewhere in buf to a null terminated string, or NULL
// on error.
-static char *pid_to_str(char *buf, size_t len, pid_t pid) {
+static char* pid_to_str(char* buf, size_t len, pid_t pid) {
if (pid <= 0) {
return nullptr;
}
- char *ptr = buf + len - 1;
+ char* ptr = buf + len - 1;
*ptr = 0;
while (pid > 0) {
ptr--;
@@ -79,6 +81,7 @@
bool ReleaseThread(pid_t tid);
bool CapturedThreadInfo(ThreadInfoList& threads);
void InjectTestFunc(std::function<void(pid_t)>&& f) { inject_test_func_ = f; }
+
private:
int CaptureThread(pid_t tid);
bool ReleaseThread(pid_t tid, unsigned int signal);
@@ -92,9 +95,8 @@
std::function<void(pid_t)> inject_test_func_;
};
-ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator) :
- captured_threads_(allocator), allocator_(allocator), pid_(pid) {
-}
+ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator)
+ : captured_threads_(allocator), allocator_(allocator), pid_(pid) {}
bool ThreadCaptureImpl::ListThreads(TidList& tids) {
tids.clear();
@@ -115,11 +117,11 @@
}
struct linux_dirent64 {
- uint64_t d_ino;
- int64_t d_off;
- uint16_t d_reclen;
- char d_type;
- char d_name[];
+ uint64_t d_ino;
+ int64_t d_off;
+ uint16_t d_reclen;
+ char d_type;
+ char d_name[];
} __attribute((packed));
char dirent_buf[4096];
ssize_t nread;
@@ -209,7 +211,7 @@
bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) {
thread_info.tid = tid;
- const unsigned int max_num_regs = 128; // larger than number of registers on any device
+ const unsigned int max_num_regs = 128; // larger than number of registers on any device
uintptr_t regs[max_num_regs];
struct iovec iovec;
iovec.iov_base = ®s;
@@ -243,7 +245,7 @@
thread_info.stack = std::pair<uintptr_t, uintptr_t>(regs[sp], 0);
- return true;
+ return true;
}
int ThreadCaptureImpl::CaptureThread(pid_t tid) {
@@ -266,7 +268,7 @@
unsigned int resume_signal = 0;
- unsigned int signal = WSTOPSIG(status);
+ unsigned int signal = WSTOPSIG(status);
if ((status >> 16) == PTRACE_EVENT_STOP) {
switch (signal) {
case SIGSTOP:
@@ -307,7 +309,7 @@
bool ThreadCaptureImpl::ReleaseThreads() {
bool ret = true;
- for (auto it = captured_threads_.begin(); it != captured_threads_.end(); ) {
+ for (auto it = captured_threads_.begin(); it != captured_threads_.end();) {
if (ReleaseThread(it->first, it->second)) {
it = captured_threads_.erase(it);
} else {
@@ -361,3 +363,5 @@
void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) {
impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f));
}
+
+} // namespace android
diff --git a/libmemunreachable/ThreadCapture.h b/libmemunreachable/ThreadCapture.h
index 1022cad..961cb60 100644
--- a/libmemunreachable/ThreadCapture.h
+++ b/libmemunreachable/ThreadCapture.h
@@ -21,6 +21,8 @@
#include "Allocator.h"
+namespace android {
+
struct ThreadInfo {
pid_t tid;
allocator::vector<uintptr_t> regs;
@@ -33,7 +35,7 @@
class ThreadCaptureImpl;
class ThreadCapture {
-public:
+ public:
ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator);
~ThreadCapture();
@@ -44,11 +46,13 @@
bool CapturedThreadInfo(ThreadInfoList& threads);
void InjectTestFunc(std::function<void(pid_t)>&& f);
-private:
+ private:
ThreadCapture(const ThreadCapture&) = delete;
void operator=(const ThreadCapture&) = delete;
Allocator<ThreadCaptureImpl>::unique_ptr impl_;
};
+} // namespace android
+
#endif
diff --git a/libmemunreachable/anon_vma_naming.h b/libmemunreachable/anon_vma_naming.h
index 1e4ade1..fb31e41 100644
--- a/libmemunreachable/anon_vma_naming.h
+++ b/libmemunreachable/anon_vma_naming.h
@@ -19,7 +19,7 @@
#include <sys/prctl.h>
-#define PR_SET_VMA 0x53564d41
-#define PR_SET_VMA_ANON_NAME 0
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
-#endif // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+#endif // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
diff --git a/libmemunreachable/bionic.h b/libmemunreachable/bionic.h
index 83d07a8..dd1ec79 100644
--- a/libmemunreachable/bionic.h
+++ b/libmemunreachable/bionic.h
@@ -17,9 +17,9 @@
#ifndef LIBMEMUNREACHABLE_BIONIC_H_
#define LIBMEMUNREACHABLE_BIONIC_H_
-#include <sys/cdefs.h>
#include <stdint.h>
#include <stdlib.h>
+#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -27,9 +27,9 @@
extern void malloc_disable();
extern void malloc_enable();
extern int malloc_iterate(uintptr_t base, size_t size,
- void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+ void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
extern ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
__END_DECLS
-#endif // LIBMEMUNREACHABLE_BIONIC_H_
+#endif // LIBMEMUNREACHABLE_BIONIC_H_
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index 9b227fd..438fcaf 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -17,12 +17,15 @@
#ifndef LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
#define LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
+#include <string.h>
#include <sys/cdefs.h>
#ifdef __cplusplus
-#include <vector>
#include <string>
+#include <vector>
+
+namespace android {
struct Leak {
uintptr_t begin;
@@ -73,6 +76,8 @@
std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100);
+} // namespace android
+
#endif
__BEGIN_DECLS
@@ -83,4 +88,4 @@
__END_DECLS
-#endif // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
+#endif // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
index 1725c53..44c5f85 100644
--- a/libmemunreachable/log.h
+++ b/libmemunreachable/log.h
@@ -26,13 +26,21 @@
#define MEM_ALOGE(...) async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, ##__VA_ARGS__)
#define MEM_ALOGW(...) async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, ##__VA_ARGS__)
#define MEM_ALOGI(...) async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG, ##__VA_ARGS__)
-#define MEM_ALOGV(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGV_IMPL(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+
+#ifdef NDEBUG
+#define MEM_ALOGV(...) \
+ do { \
+ if (0) { \
+ MEM_ALOGV_IMPL(__VA_ARGS__); \
+ } \
+ } while (0)
+#else
+#define MEM_ALOGV(...) MEM_ALOGV_IMPL(__VA_ARGS__)
+#endif
#define MEM_LOG_ALWAYS_FATAL(...) async_safe_fatal(__VA_ARGS__)
-#define MEM_LOG_ALWAYS_FATAL_IF(cond, ...) \
- ((__predict_false(cond)) ? async_safe_fatal(__VA_ARGS__) : (void)0)
-
#else
#include <log/log.h>
@@ -43,8 +51,7 @@
#define MEM_ALOGI ALOGI
#define MEM_LOG_ALWAYS_FATAL LOG_ALWAYS_FATAL
-#define MEM_LOG_ALWAYS_FATAL_IF LOG_ALWAYS_FATAL_IF
#endif
-#endif // LIBMEMUNREACHABLE_LOG_H_
+#endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libmemunreachable/tests/Allocator_test.cpp b/libmemunreachable/tests/Allocator_test.cpp
index 21c8218..8991a7b 100644
--- a/libmemunreachable/tests/Allocator_test.cpp
+++ b/libmemunreachable/tests/Allocator_test.cpp
@@ -16,44 +16,44 @@
#include <Allocator.h>
-#include <gtest/gtest.h>
#include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
+namespace android {
std::function<void()> ScopedAlarm::func_;
class AllocatorTest : public testing::Test {
protected:
AllocatorTest() : heap(), disable_malloc_() {}
- virtual void SetUp() {
- heap_count = 0;
- }
+ virtual void SetUp() { heap_count = 0; }
virtual void TearDown() {
ASSERT_EQ(heap_count, 0);
ASSERT_TRUE(heap.empty());
ASSERT_FALSE(disable_malloc_.timed_out());
}
Heap heap;
+
private:
ScopedDisableMallocTimeout disable_malloc_;
};
TEST_F(AllocatorTest, simple) {
Allocator<char[100]> allocator(heap);
- void *ptr = allocator.allocate();
+ void* ptr = allocator.allocate();
ASSERT_TRUE(ptr != NULL);
allocator.deallocate(ptr);
}
TEST_F(AllocatorTest, multiple) {
Allocator<char[100]> allocator(heap);
- void *ptr1 = allocator.allocate();
+ void* ptr1 = allocator.allocate();
ASSERT_TRUE(ptr1 != NULL);
- void *ptr2 = allocator.allocate();
+ void* ptr2 = allocator.allocate();
ASSERT_TRUE(ptr2 != NULL);
ASSERT_NE(ptr1, ptr2);
allocator.deallocate(ptr1);
- void *ptr3 = allocator.allocate();
+ void* ptr3 = allocator.allocate();
ASSERT_EQ(ptr1, ptr3);
allocator.deallocate(ptr3);
allocator.deallocate(ptr2);
@@ -63,7 +63,7 @@
const int num = 4096;
const int size = 128;
Allocator<char[size]> allocator(heap);
- void *ptr[num];
+ void* ptr[num];
for (int i = 0; i < num; i++) {
ptr[i] = allocator.allocate();
memset(ptr[i], 0xaa, size);
@@ -87,7 +87,7 @@
TEST_F(AllocatorTest, large) {
const size_t size = 1024 * 1024;
Allocator<char[size]> allocator(heap);
- void *ptr = allocator.allocate();
+ void* ptr = allocator.allocate();
memset(ptr, 0xaa, size);
allocator.deallocate(ptr);
}
@@ -96,7 +96,7 @@
const int num = 128;
const int size = 1024 * 1024;
Allocator<char[size]> allocator(heap);
- void *ptr[num];
+ void* ptr[num];
for (int i = 0; i < num; i++) {
ptr[i] = allocator.allocate();
memset(ptr[i], 0xaa, size);
@@ -172,3 +172,5 @@
ASSERT_NE(ptr, nullptr);
}
+
+} // namespace android
diff --git a/libmemunreachable/tests/AndroidTest.xml b/libmemunreachable/tests/AndroidTest.xml
new file mode 100644
index 0000000..604c0ec
--- /dev/null
+++ b/libmemunreachable/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for memunreachable_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="memunreachable_test->/data/local/tmp/memunreachable_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="memunreachable_test" />
+ </test>
+</configuration>
diff --git a/libmemunreachable/tests/Binder_test.cpp b/libmemunreachable/tests/Binder_test.cpp
new file mode 100644
index 0000000..6e85d5a
--- /dev/null
+++ b/libmemunreachable/tests/Binder_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <gtest/gtest.h>
+
+#include "Allocator.h"
+#include "Binder.h"
+
+namespace android {
+
+static const String16 service_name("test.libmemunreachable_binder");
+
+class BinderService : public BBinder {
+ public:
+ BinderService() = default;
+ virtual ~BinderService() = default;
+
+ virtual status_t onTransact(uint32_t /*code*/, const Parcel& data, Parcel* reply,
+ uint32_t /*flags*/ = 0) {
+ reply->writeStrongBinder(ref);
+ ref = data.readStrongBinder();
+ return 0;
+ }
+
+ private:
+ sp<IBinder> ref;
+};
+
+class BinderObject : public BBinder {
+ public:
+ BinderObject() = default;
+ ~BinderObject() = default;
+};
+
+class ServiceProcess {
+ public:
+ ServiceProcess() : child_(0) {}
+ ~ServiceProcess() { Stop(); }
+
+ bool Run() {
+ pid_t ret = fork();
+ if (ret < 0) {
+ return false;
+ } else if (ret == 0) {
+ // child
+ _exit(Service());
+ } else {
+ // parent
+ child_ = ret;
+ return true;
+ }
+ }
+
+ bool Stop() {
+ if (child_ > 0) {
+ if (kill(child_, SIGTERM)) {
+ return false;
+ }
+ int status = 0;
+ if (TEMP_FAILURE_RETRY(waitpid(child_, &status, 0)) != child_) {
+ return false;
+ }
+ child_ = 0;
+ return WIFEXITED(status) && WEXITSTATUS(status) == 0;
+ }
+
+ return true;
+ }
+
+ int Service() {
+ sp<ProcessState> proc{ProcessState::self()};
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (sm == nullptr) {
+ fprintf(stderr, "Failed to get service manager\n");
+ return 1;
+ }
+ if (sm->addService(service_name, new BinderService()) != OK) {
+ fprintf(stderr, "Failed to add test service\n");
+ return 1;
+ }
+ proc->startThreadPool();
+ pause();
+ return 0;
+ }
+
+ private:
+ pid_t child_;
+};
+
+class BinderTest : public ::testing::Test {
+ protected:
+ ServiceProcess service_process_;
+};
+
+TEST_F(BinderTest, binder) {
+ ServiceProcess service_process;
+ ASSERT_TRUE(service_process.Run());
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ ASSERT_TRUE(sm != nullptr);
+
+ // A small sleep allows the service to start, which
+ // prevents a longer sleep in getService.
+ usleep(100000);
+
+ sp<IBinder> service = sm->getService(service_name);
+ ASSERT_TRUE(service != nullptr);
+
+ sp<IBinder> binder{new BinderObject()};
+
+ Parcel send;
+ Parcel reply;
+
+ send.writeStrongBinder(binder);
+ status_t rv = service->transact(0, send, &reply);
+ ASSERT_EQ(static_cast<status_t>(OK), rv);
+
+ Heap heap;
+ allocator::vector<uintptr_t> refs{heap};
+
+ ASSERT_TRUE(BinderReferences(refs));
+
+ bool found_ref = false;
+ for (auto ref : refs) {
+ if (ref == reinterpret_cast<uintptr_t>(binder.get())) {
+ found_ref = true;
+ }
+ }
+
+ ASSERT_TRUE(found_ref);
+}
+
+} // namespace android
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
index 4e6155b..c630049 100644
--- a/libmemunreachable/tests/DisableMalloc_test.cpp
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -19,11 +19,13 @@
#include <chrono>
#include <functional>
-#include <gtest/gtest.h>
#include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
using namespace std::chrono_literals;
+namespace android {
+
class DisableMallocTest : public ::testing::Test {
protected:
void alarm(std::chrono::microseconds us) {
@@ -36,75 +38,83 @@
};
TEST_F(DisableMallocTest, reenable) {
- ASSERT_EXIT({
- alarm(100ms);
- void *ptr1 = malloc(128);
- ASSERT_NE(ptr1, nullptr);
- free(ptr1);
- {
- ScopedDisableMalloc disable_malloc;
- }
- void *ptr2 = malloc(128);
- ASSERT_NE(ptr2, nullptr);
- free(ptr2);
- _exit(1);
- }, ::testing::ExitedWithCode(1), "");
+ ASSERT_EXIT(
+ {
+ alarm(100ms);
+ void* ptr1 = malloc(128);
+ ASSERT_NE(ptr1, nullptr);
+ free(ptr1);
+ { ScopedDisableMalloc disable_malloc; }
+ void* ptr2 = malloc(128);
+ ASSERT_NE(ptr2, nullptr);
+ free(ptr2);
+ _exit(1);
+ },
+ ::testing::ExitedWithCode(1), "");
}
TEST_F(DisableMallocTest, deadlock_allocate) {
- ASSERT_DEATH({
- void *ptr = malloc(128);
- ASSERT_NE(ptr, nullptr);
- free(ptr);
- {
- alarm(100ms);
- ScopedDisableMalloc disable_malloc;
- void* ptr = malloc(128);
- ASSERT_NE(ptr, nullptr);
- free(ptr);
- }
- }, "");
+ ASSERT_DEATH(
+ {
+ void* ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ free(ptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ void* ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ free(ptr);
+ }
+ },
+ "");
}
TEST_F(DisableMallocTest, deadlock_new) {
- ASSERT_DEATH({
- char* ptr = new(char);
- ASSERT_NE(ptr, nullptr);
- delete(ptr);
- {
- alarm(100ms);
- ScopedDisableMalloc disable_malloc;
- char* ptr = new (std::nothrow)(char);
- ASSERT_NE(ptr, nullptr);
- delete(ptr);
- }
- }, "");
+ ASSERT_DEATH(
+ {
+ char* ptr = new (char);
+ ASSERT_NE(ptr, nullptr);
+ delete (ptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ char* ptr = new (std::nothrow)(char);
+ ASSERT_NE(ptr, nullptr);
+ delete (ptr);
+ }
+ },
+ "");
}
TEST_F(DisableMallocTest, deadlock_delete) {
- ASSERT_DEATH({
- char* ptr = new(char);
- ASSERT_NE(ptr, nullptr);
- {
- alarm(250ms);
- ScopedDisableMalloc disable_malloc;
- delete(ptr);
- // Force ptr usage or this code gets optimized away by the arm64 compiler.
- ASSERT_NE(ptr, nullptr);
- }
- }, "");
+ ASSERT_DEATH(
+ {
+ char* ptr = new (char);
+ ASSERT_NE(ptr, nullptr);
+ {
+ alarm(250ms);
+ ScopedDisableMalloc disable_malloc;
+ delete (ptr);
+ // Force ptr usage or this code gets optimized away by the arm64 compiler.
+ ASSERT_NE(ptr, nullptr);
+ }
+ },
+ "");
}
TEST_F(DisableMallocTest, deadlock_free) {
- ASSERT_DEATH({
- void *ptr = malloc(128);
- ASSERT_NE(ptr, nullptr);
- {
- alarm(100ms);
- ScopedDisableMalloc disable_malloc;
- free(ptr);
- }
- }, "");
+ ASSERT_DEATH(
+ {
+ void* ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ free(ptr);
+ }
+ },
+ "");
}
TEST_F(DisableMallocTest, deadlock_fork) {
@@ -113,6 +123,8 @@
alarm(100ms);
ScopedDisableMalloc disable_malloc;
fork();
- }
- }, "");
}
+}, "");
+}
+
+} // namespace android
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
index 98e4aa1..84a0ec6 100644
--- a/libmemunreachable/tests/HeapWalker_test.cpp
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -19,10 +19,12 @@
#include "HeapWalker.h"
-#include <gtest/gtest.h>
#include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
#include "Allocator.h"
+namespace android {
+
class HeapWalkerTest : public ::testing::Test {
public:
HeapWalkerTest() : disable_malloc_(), heap_() {}
@@ -172,20 +174,20 @@
ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
EXPECT_EQ(2U, num_leaks);
- EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(2U, leaked.size());
}
TEST_F(HeapWalkerTest, segv) {
const size_t page_size = sysconf(_SC_PAGE_SIZE);
- void* buffer1 = mmap(NULL, page_size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+ void* buffer1 = mmap(NULL, page_size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
ASSERT_NE(buffer1, nullptr);
void* buffer2;
buffer2 = &buffer1;
HeapWalker heap_walker(heap_);
- heap_walker.Allocation(buffer_begin(buffer1), buffer_begin(buffer1)+page_size);
+ heap_walker.Allocation(buffer_begin(buffer1), buffer_begin(buffer1) + page_size);
heap_walker.Root(buffer_begin(buffer2), buffer_end(buffer2));
ASSERT_EQ(true, heap_walker.DetectLeaks());
@@ -199,3 +201,5 @@
EXPECT_EQ(0U, leaked_bytes);
ASSERT_EQ(0U, leaked.size());
}
+
+} // namespace android
diff --git a/libmemunreachable/tests/HostMallocStub.cpp b/libmemunreachable/tests/HostMallocStub.cpp
index a7e3f07..0ef0487 100644
--- a/libmemunreachable/tests/HostMallocStub.cpp
+++ b/libmemunreachable/tests/HostMallocStub.cpp
@@ -16,8 +16,6 @@
#include "bionic.h"
-void malloc_disable() {
-}
+void malloc_disable() {}
-void malloc_enable() {
-}
+void malloc_enable() {}
diff --git a/libmemunreachable/tests/LeakFolding_test.cpp b/libmemunreachable/tests/LeakFolding_test.cpp
index e85df5f..f5b3631 100644
--- a/libmemunreachable/tests/LeakFolding_test.cpp
+++ b/libmemunreachable/tests/LeakFolding_test.cpp
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-#include "HeapWalker.h"
#include "LeakFolding.h"
+#include "HeapWalker.h"
-#include <gtest/gtest.h>
#include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
#include "Allocator.h"
+namespace android {
+
class LeakFoldingTest : public ::testing::Test {
public:
LeakFoldingTest() : disable_malloc_(), heap_() {}
@@ -84,7 +86,7 @@
ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
EXPECT_EQ(2U, num_leaks);
- EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(2U, leaked.size());
EXPECT_EQ(0U, leaked[0].referenced_count);
EXPECT_EQ(0U, leaked[0].referenced_size);
@@ -113,7 +115,7 @@
ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
EXPECT_EQ(2U, num_leaks);
- EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(1U, leaked.size());
EXPECT_EQ(1U, leaked[0].referenced_count);
EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
@@ -144,10 +146,10 @@
ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
EXPECT_EQ(3U, num_leaks);
- EXPECT_EQ(3*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(3 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(1U, leaked.size());
EXPECT_EQ(2U, leaked[0].referenced_count);
- EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(2 * sizeof(uintptr_t), leaked[0].referenced_size);
}
TEST_F(LeakFoldingTest, dominator_cycle) {
@@ -175,13 +177,13 @@
ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
EXPECT_EQ(3U, num_leaks);
- EXPECT_EQ(5*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(5 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(2U, leaked.size());
EXPECT_EQ(2U, leaked[0].referenced_count);
- EXPECT_EQ(3*sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(3 * sizeof(uintptr_t), leaked[0].referenced_size);
EXPECT_EQ(2U, leaked[1].referenced_count);
- EXPECT_EQ(3*sizeof(uintptr_t), leaked[1].referenced_size);
+ EXPECT_EQ(3 * sizeof(uintptr_t), leaked[1].referenced_size);
}
TEST_F(LeakFoldingTest, two_cycles) {
@@ -218,12 +220,12 @@
ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
EXPECT_EQ(6U, num_leaks);
- EXPECT_EQ(6*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(2U, leaked.size());
EXPECT_EQ(2U, leaked[0].referenced_count);
- EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(2 * sizeof(uintptr_t), leaked[0].referenced_size);
EXPECT_EQ(2U, leaked[1].referenced_count);
- EXPECT_EQ(2*sizeof(uintptr_t), leaked[1].referenced_size);
+ EXPECT_EQ(2 * sizeof(uintptr_t), leaked[1].referenced_size);
}
TEST_F(LeakFoldingTest, two_dominator_cycles) {
@@ -254,7 +256,7 @@
ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
EXPECT_EQ(4U, num_leaks);
- EXPECT_EQ(4*sizeof(uintptr_t), leaked_bytes);
+ EXPECT_EQ(4 * sizeof(uintptr_t), leaked_bytes);
ASSERT_EQ(4U, leaked.size());
EXPECT_EQ(1U, leaked[0].referenced_count);
EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
@@ -272,13 +274,13 @@
HeapWalker heap_walker(heap_);
- for (size_t i = 0; i < n; i ++) {
+ for (size_t i = 0; i < n; i++) {
ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
- reinterpret_cast<uintptr_t>(&buffer[i+1])));
+ reinterpret_cast<uintptr_t>(&buffer[i + 1])));
}
for (size_t i = 0; i < n - 1; i++) {
- buffer[i] = &buffer[i+1];
+ buffer[i] = &buffer[i + 1];
}
buffer[n - 1] = &buffer[0];
@@ -306,15 +308,15 @@
HeapWalker heap_walker(heap_);
for (size_t i = 0; i < n - 1; i++) {
- buffer[i] = &buffer[i+1];
+ buffer[i] = &buffer[i + 1];
}
buffer[n - 1] = &buffer[0];
buffer1[0] = &buffer[0];
- for (size_t i = 0; i < n; i ++) {
+ for (size_t i = 0; i < n; i++) {
ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
- reinterpret_cast<uintptr_t>(&buffer[i+1])));
+ reinterpret_cast<uintptr_t>(&buffer[i + 1])));
}
ALLOCATION(heap_walker, buffer1);
@@ -425,3 +427,5 @@
EXPECT_EQ(3U, leaked[3].referenced_count);
EXPECT_EQ(6 * sizeof(uintptr_t), leaked[3].referenced_size);
}
+
+} // namespace android
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index 71da365..ec89388 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -16,36 +16,32 @@
#include <fcntl.h>
#include <stdlib.h>
-#include <unistd.h>
#include <sys/prctl.h>
+#include <unistd.h>
#include <gtest/gtest.h>
#include <memunreachable/memunreachable.h>
+namespace android {
+
class HiddenPointer {
public:
- explicit HiddenPointer(size_t size = 256) {
- Set(malloc(size));
- }
- ~HiddenPointer() {
- Free();
- }
- void* Get() {
- return reinterpret_cast<void*>(~ptr_);
- }
+ explicit HiddenPointer(size_t size = 256) { Set(malloc(size)); }
+ ~HiddenPointer() { Free(); }
+ void* Get() { return reinterpret_cast<void*>(~ptr_); }
void Free() {
free(Get());
Set(nullptr);
}
+
private:
- void Set(void* ptr) {
- ptr_ = ~reinterpret_cast<uintptr_t>(ptr);
- }
+ void Set(void* ptr) { ptr_ = ~reinterpret_cast<uintptr_t>(ptr); }
volatile uintptr_t ptr_;
};
-static void Ref(void* ptr) {
+// Trick the compiler into thinking a value on the stack is still referenced.
+static void Ref(void** ptr) {
write(0, ptr, 0);
}
@@ -63,14 +59,14 @@
{
void* ptr = hidden_ptr.Get();
- Ref(ptr);
+ Ref(&ptr);
UnreachableMemoryInfo info;
ASSERT_TRUE(GetUnreachableMemory(info));
ASSERT_EQ(0U, info.leaks.size());
- Ref(ptr);
+ ptr = nullptr;
}
{
@@ -216,3 +212,5 @@
ASSERT_TRUE(LogUnreachableMemory(true, 100));
}
+
+} // namespace android
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
index 44aabd7..4fbf729 100644
--- a/libmemunreachable/tests/ThreadCapture_test.cpp
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -34,6 +34,8 @@
using namespace std::chrono_literals;
+namespace android {
+
class ThreadListTest : public ::testing::TestWithParam<int> {
public:
ThreadListTest() : stop_(false) {}
@@ -45,12 +47,10 @@
WaitForThreads();
}
- virtual void TearDown() {
- ASSERT_TRUE(heap.empty());
- }
+ virtual void TearDown() { ASSERT_TRUE(heap.empty()); }
protected:
- template<class Function>
+ template <class Function>
void StartThreads(unsigned int threads, Function&& func) {
threads_.reserve(threads);
tids_.reserve(threads);
@@ -68,14 +68,14 @@
{
std::unique_lock<std::mutex> lk(m_);
- cv_stop_.wait(lk, [&] {return stop_;});
+ cv_stop_.wait(lk, [&] { return stop_; });
}
});
}
{
std::unique_lock<std::mutex> lk(m_);
- cv_start_.wait(lk, [&]{ return tids_.size() == threads; });
+ cv_start_.wait(lk, [&] { return tids_.size() == threads; });
}
}
@@ -93,9 +93,7 @@
tids_.clear();
}
- std::vector<pid_t>& tids() {
- return tids_;
- }
+ std::vector<pid_t>& tids() { return tids_; }
Heap heap;
@@ -143,7 +141,7 @@
TEST_P(ThreadListTest, list_some) {
const unsigned int threads = GetParam() - 1;
- StartThreads(threads, [](){});
+ StartThreads(threads, []() {});
std::vector<pid_t> expected_tids = tids();
expected_tids.push_back(getpid());
@@ -176,10 +174,8 @@
public:
ThreadCaptureTest() {}
~ThreadCaptureTest() {}
- void Fork(std::function<void()>&& child_init,
- std::function<void()>&& child_cleanup,
- std::function<void(pid_t)>&& parent) {
-
+ void Fork(std::function<void()>&& child_init, std::function<void()>&& child_cleanup,
+ std::function<void(pid_t)>&& parent) {
ScopedPipe start_pipe;
ScopedPipe stop_pipe;
@@ -211,39 +207,40 @@
TEST_P(ThreadCaptureTest, capture_some) {
const unsigned int threads = GetParam();
- Fork([&](){
- // child init
- StartThreads(threads - 1, [](){});
- },
- [&](){
- // child cleanup
- StopThreads();
- },
- [&](pid_t child){
- // parent
- ASSERT_GT(child, 0);
+ Fork(
+ [&]() {
+ // child init
+ StartThreads(threads - 1, []() {});
+ },
+ [&]() {
+ // child cleanup
+ StopThreads();
+ },
+ [&](pid_t child) {
+ // parent
+ ASSERT_GT(child, 0);
- {
- ScopedDisableMallocTimeout disable_malloc;
+ {
+ ScopedDisableMallocTimeout disable_malloc;
- ThreadCapture thread_capture(child, heap);
- auto list_tids = allocator::vector<pid_t>(heap);
+ ThreadCapture thread_capture(child, heap);
+ auto list_tids = allocator::vector<pid_t>(heap);
- ASSERT_TRUE(thread_capture.ListThreads(list_tids));
- ASSERT_EQ(threads, list_tids.size());
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+ ASSERT_EQ(threads, list_tids.size());
- ASSERT_TRUE(thread_capture.CaptureThreads());
+ ASSERT_TRUE(thread_capture.CaptureThreads());
- auto thread_info = allocator::vector<ThreadInfo>(heap);
- ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
- ASSERT_EQ(threads, thread_info.size());
- ASSERT_TRUE(thread_capture.ReleaseThreads());
+ auto thread_info = allocator::vector<ThreadInfo>(heap);
+ ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+ ASSERT_EQ(threads, thread_info.size());
+ ASSERT_TRUE(thread_capture.ReleaseThreads());
- if (!HasFailure()) {
- ASSERT_FALSE(disable_malloc.timed_out());
- }
-}
- });
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+ }
+ });
}
INSTANTIATE_TEST_CASE_P(ThreadCaptureTest, ThreadCaptureTest, ::testing::Values(1, 2, 10, 1024));
@@ -262,7 +259,7 @@
ScopedDisableMallocTimeout disable_malloc;
ThreadCapture thread_capture(ret, heap);
- thread_capture.InjectTestFunc([&](pid_t tid){
+ thread_capture.InjectTestFunc([&](pid_t tid) {
syscall(SYS_tgkill, ret, tid, SIGKILL);
usleep(10000);
});
@@ -288,62 +285,65 @@
// For signal handler
static ScopedPipe* g_pipe;
- Fork([&](){
- // child init
- pipe.CloseReceiver();
+ Fork(
+ [&]() {
+ // child init
+ pipe.CloseReceiver();
- g_pipe = &pipe;
+ g_pipe = &pipe;
- struct sigaction act{};
- act.sa_handler = [](int){
- char buf = '+';
- write(g_pipe->Sender(), &buf, 1);
- g_pipe->CloseSender();
- };
- sigaction(sig, &act, NULL);
- sigset_t set;
- sigemptyset(&set);
- sigaddset(&set, sig);
- pthread_sigmask(SIG_UNBLOCK, &set, NULL);
- },
- [&](){
- // child cleanup
- g_pipe = nullptr;
- pipe.Close();
- },
- [&](pid_t child){
- // parent
- ASSERT_GT(child, 0);
- pipe.CloseSender();
+ struct sigaction act {};
+ act.sa_handler = [](int) {
+ char buf = '+';
+ write(g_pipe->Sender(), &buf, 1);
+ g_pipe->CloseSender();
+ };
+ sigaction(sig, &act, NULL);
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, sig);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+ },
+ [&]() {
+ // child cleanup
+ g_pipe = nullptr;
+ pipe.Close();
+ },
+ [&](pid_t child) {
+ // parent
+ ASSERT_GT(child, 0);
+ pipe.CloseSender();
- {
- ScopedDisableMallocTimeout disable_malloc;
+ {
+ ScopedDisableMallocTimeout disable_malloc;
- ThreadCapture thread_capture(child, heap);
- thread_capture.InjectTestFunc([&](pid_t tid){
- syscall(SYS_tgkill, child, tid, sig);
- usleep(10000);
+ ThreadCapture thread_capture(child, heap);
+ thread_capture.InjectTestFunc([&](pid_t tid) {
+ syscall(SYS_tgkill, child, tid, sig);
+ usleep(10000);
+ });
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+ ASSERT_EQ(1U, list_tids.size());
+
+ ASSERT_TRUE(thread_capture.CaptureThreads());
+
+ auto thread_info = allocator::vector<ThreadInfo>(heap);
+ ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+ ASSERT_EQ(1U, thread_info.size());
+ ASSERT_TRUE(thread_capture.ReleaseThreads());
+
+ usleep(100000);
+ char buf;
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1)));
+ ASSERT_EQ(buf, '+');
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+ }
});
- auto list_tids = allocator::vector<pid_t>(heap);
-
- ASSERT_TRUE(thread_capture.ListThreads(list_tids));
- ASSERT_EQ(1U, list_tids.size());
-
- ASSERT_TRUE(thread_capture.CaptureThreads());
-
- auto thread_info = allocator::vector<ThreadInfo>(heap);
- ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
- ASSERT_EQ(1U, thread_info.size());
- ASSERT_TRUE(thread_capture.ReleaseThreads());
-
- usleep(100000);
- char buf;
- ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1)));
- ASSERT_EQ(buf, '+');
-
- if (!HasFailure()) {
- ASSERT_FALSE(disable_malloc.timed_out());
- }
- }
- });
}
+
+} // namespace android
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 377b7dd..1cea4cd 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -1,4 +1,11 @@
+cc_library_headers {
+ name: "libnativebridge-dummy-headers",
+
+ host_supported: true,
+ export_include_dirs=["include"],
+}
+
cc_library {
name: "libnativebridge",
@@ -7,6 +14,8 @@
shared_libs: ["liblog"],
clang: true,
+ export_include_dirs=["include"],
+
cflags: [
"-Werror",
"-Wall",
@@ -23,4 +32,4 @@
},
}
-subdirs = ["tests"]
\ No newline at end of file
+subdirs = ["tests"]
diff --git a/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
similarity index 96%
rename from include/nativebridge/native_bridge.h
rename to libnativebridge/include/nativebridge/native_bridge.h
index 929b8ae..9bfc935 100644
--- a/include/nativebridge/native_bridge.h
+++ b/libnativebridge/include/nativebridge/native_bridge.h
@@ -161,6 +161,9 @@
// Use NativeBridgeLoadLibrary() instead in non-namespace scenario.
void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns);
+// Returns vendor namespace if it is enabled for the device and null otherwise
+native_bridge_namespace_t* NativeBridgeGetVendorNamespace();
+
// Native bridge interfaces to runtime.
struct NativeBridgeCallbacks {
// Version number of the interface.
@@ -348,6 +351,15 @@
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Use loadLibrary instead in non-namespace scenario.
void* (*loadLibraryExt)(const char* libpath, int flag, native_bridge_namespace_t* ns);
+
+ // Get native bridge version of vendor namespace.
+ // The vendor namespace is the namespace used to load vendor public libraries.
+ // With O release this namespace can be different from the default namespace.
+ // For the devices without enable vendor namespaces this function should return null
+ //
+ // Returns:
+ // vendor namespace or null if it was not set up for the device
+ native_bridge_namespace_t* (*getVendorNamespace)();
};
// Runtime interfaces to native bridge.
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 050373a..02b4fe7 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -85,12 +85,14 @@
// Nativebridge implementation.
// Used by isCompatibleWith() which is introduced in v2.
enum NativeBridgeImplementationVersion {
- // first version, not used.
- DEFAULT_VERSION = 1,
- // The version which signal semantic is introduced.
- SIGNAL_VERSION = 2,
- // The version which namespace semantic is introduced.
- NAMESPACE_VERSION = 3,
+ // first version, not used.
+ DEFAULT_VERSION = 1,
+ // The version which signal semantic is introduced.
+ SIGNAL_VERSION = 2,
+ // The version which namespace semantic is introduced.
+ NAMESPACE_VERSION = 3,
+ // The version with vendor namespaces
+ VENDOR_NAMESPACE_VERSION = 4,
};
// Whether we had an error at some point.
@@ -621,6 +623,14 @@
return false;
}
+native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
+ if (!NativeBridgeInitialized() || !isCompatibleWith(VENDOR_NAMESPACE_VERSION)) {
+ return nullptr;
+ }
+
+ return callbacks->getVendorNamespace();
+}
+
void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) {
if (NativeBridgeInitialized()) {
if (isCompatibleWith(NAMESPACE_VERSION)) {
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index efd3978..e31dae0 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -23,6 +23,7 @@
"-Wextra",
"-Werror",
],
+ header_libs: ["libnativebridge-dummy-headers"],
cppflags: ["-fvisibility=protected"],
target: {
android: {
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
index 9121277..43c9329 100644
--- a/libnativeloader/include/nativeloader/dlext_namespaces.h
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -124,6 +124,8 @@
*/
extern void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size);
+extern android_namespace_t* android_get_exported_namespace(const char* name);
+
__END_DECLS
#endif /* __ANDROID_DLEXT_NAMESPACES_H__ */
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index d9cb90d..36a2e44 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -83,6 +83,12 @@
static constexpr const char* kPublicNativeLibrariesVendorConfig =
"/vendor/etc/public.libraries.txt";
+// The device may be configured to have the vendor libraries loaded to a separate namespace.
+// For historical reasons this namespace was named sphal but effectively it is intended
+// to use to load vendor libraries to separate namespace with controlled interface between
+// vendor and system namespaces.
+static constexpr const char* kVendorNamespaceName = "sphal";
+
// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
// System.load() with an absolute path which is outside of the classloader library search path.
// This list includes all directories app is allowed to access this way.
@@ -170,11 +176,23 @@
return false;
}
- if (!android_link_namespaces(ns, nullptr, public_libraries_.c_str())) {
+ // Note that when vendor_ns is not configured this function will return nullptr
+ // and it will result in linking vendor_public_libraries_ to the default namespace
+ // which is expected behavior in this case.
+ android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
+
+ if (!android_link_namespaces(ns, nullptr, system_public_libraries_.c_str())) {
*error_msg = dlerror();
return false;
}
+ if (!vendor_public_libraries_.empty()) {
+ if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+ *error_msg = dlerror();
+ return false;
+ }
+ }
+
native_loader_ns = NativeLoaderNamespace(ns);
} else {
native_bridge_namespace_t* ns = NativeBridgeCreateNamespace("classloader-namespace",
@@ -183,16 +201,26 @@
namespace_type,
permitted_path.c_str(),
parent_ns.get_native_bridge_ns());
+
if (ns == nullptr) {
*error_msg = NativeBridgeGetError();
return false;
}
- if (!NativeBridgeLinkNamespaces(ns, nullptr, public_libraries_.c_str())) {
+ native_bridge_namespace_t* vendor_ns = NativeBridgeGetVendorNamespace();
+
+ if (!NativeBridgeLinkNamespaces(ns, nullptr, system_public_libraries_.c_str())) {
*error_msg = NativeBridgeGetError();
return false;
}
+ if (!vendor_public_libraries_.empty()) {
+ if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+ *error_msg = NativeBridgeGetError();
+ return false;
+ }
+ }
+
native_loader_ns = NativeLoaderNamespace(ns);
}
@@ -249,9 +277,6 @@
}
}
- // This file is optional, quietly ignore if the file does not exist.
- ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);
-
// android_init_namespaces() expects all the public libraries
// to be loaded so that they can be found by soname alone.
//
@@ -266,7 +291,13 @@
soname.c_str(), dlerror());
}
- public_libraries_ = base::Join(sonames, ':');
+ system_public_libraries_ = base::Join(sonames, ':');
+
+ sonames.clear();
+ // This file is optional, quietly ignore if the file does not exist.
+ ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);
+
+ vendor_public_libraries_ = base::Join(sonames, ':');
}
void Reset() {
@@ -325,7 +356,7 @@
// code is one example) unknown to linker in which case linker uses anonymous
// namespace. The second argument specifies the search path for the anonymous
// namespace which is the library_path of the classloader.
- initialized_ = android_init_anonymous_namespace(public_libraries_.c_str(),
+ initialized_ = android_init_anonymous_namespace(system_public_libraries_.c_str(),
is_native_bridge ? nullptr : library_path);
if (!initialized_) {
*error_msg = dlerror();
@@ -334,7 +365,7 @@
// and now initialize native bridge namespaces if necessary.
if (NativeBridgeInitialized()) {
- initialized_ = NativeBridgeInitAnonymousNamespace(public_libraries_.c_str(),
+ initialized_ = NativeBridgeInitAnonymousNamespace(system_public_libraries_.c_str(),
is_native_bridge ? library_path : nullptr);
if (!initialized_) {
*error_msg = NativeBridgeGetError();
@@ -371,8 +402,8 @@
bool initialized_;
std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
- std::string public_libraries_;
-
+ std::string system_public_libraries_;
+ std::string vendor_public_libraries_;
DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
};
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 0caf145..9967ef8 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -1,5 +1,6 @@
cc_library_shared {
name: "libnetutils",
+ vendor_available: true,
srcs: [
"dhcpclient.c",
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c
index f28d726..1856e5c 100644
--- a/libpixelflinger/codeflinger/mips64_disassem.c
+++ b/libpixelflinger/codeflinger/mips64_disassem.c
@@ -555,6 +555,7 @@
} else {
vprintf(fmt, argp);
}
+ va_end(argp);
}
/*
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c
index 3007b15..83a9740 100644
--- a/libpixelflinger/codeflinger/mips_disassem.c
+++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -562,6 +562,7 @@
} else {
vprintf(fmt, argp);
}
+ va_end(argp);
}
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 27b4065..9b8248e 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -27,10 +27,12 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <unistd.h>
#include <chrono>
#include <memory>
#include <mutex>
+#include <set>
#include <thread>
#include <android-base/logging.h>
@@ -41,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"
@@ -89,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
@@ -98,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)
@@ -258,6 +253,12 @@
return -errno;
}
+ // We separate all of the pids in the cgroup into those pids that are also the leaders of
+ // process groups (stored in the pgids set) and those that are not (stored in the pids set).
+ std::set<pid_t> pgids;
+ pgids.emplace(initialPid);
+ std::set<pid_t> pids;
+
int ret;
pid_t pid;
int processes = 0;
@@ -269,8 +270,40 @@
LOG(WARNING) << "Yikes, we've been told to kill pid 0! How about we don't do that?";
continue;
}
+ pid_t pgid = getpgid(pid);
+ if (pgid == -1) PLOG(ERROR) << "getpgid(" << pid << ") failed";
+ if (pgid == pid) {
+ pgids.emplace(pid);
+ } else {
+ pids.emplace(pid);
+ }
+ }
+
+ // Erase all pids that will be killed when we kill the process groups.
+ for (auto it = pids.begin(); it != pids.end();) {
+ pid_t pgid = getpgid(pid);
+ if (pgids.count(pgid) == 1) {
+ it = pids.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ // Kill all process groups.
+ for (const auto pgid : pgids) {
+ LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
+ << " as part of process cgroup " << initialPid;
+
+ if (kill(-pgid, signal) == -1) {
+ PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
+ }
+ }
+
+ // Kill remaining pids.
+ for (const auto pid : pids) {
LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
<< initialPid;
+
if (kill(pid, signal) == -1) {
PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
}
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index c13ffe9..aedaa38 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -22,6 +22,7 @@
cc_library {
name: "libprocinfo",
+ vendor_available: true,
host_supported: true,
srcs: [
"process.cpp",
diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h
index fb140ff..db56fc1 100644
--- a/libprocinfo/include/procinfo/process.h
+++ b/libprocinfo/include/procinfo/process.h
@@ -35,8 +35,18 @@
#if defined(__linux__)
+enum ProcessState {
+ kProcessStateUnknown,
+ kProcessStateRunning,
+ kProcessStateSleeping,
+ kProcessStateUninterruptibleWait,
+ kProcessStateStopped,
+ kProcessStateZombie,
+};
+
struct ProcessInfo {
std::string name;
+ ProcessState state;
pid_t tid;
pid_t pid;
pid_t ppid;
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
index c513e16..6e5be6e 100644
--- a/libprocinfo/process.cpp
+++ b/libprocinfo/process.cpp
@@ -44,6 +44,24 @@
return GetProcessInfoFromProcPidFd(dirfd.get(), process_info);
}
+static ProcessState parse_state(const char* state) {
+ switch (*state) {
+ case 'R':
+ return kProcessStateRunning;
+ case 'S':
+ return kProcessStateSleeping;
+ case 'D':
+ return kProcessStateUninterruptibleWait;
+ case 'T':
+ return kProcessStateStopped;
+ case 'Z':
+ return kProcessStateZombie;
+ default:
+ LOG(ERROR) << "unknown process state: " << *state;
+ return kProcessStateUnknown;
+ }
+}
+
bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) {
int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
@@ -60,7 +78,7 @@
}
int field_bitmap = 0;
- static constexpr int finished_bitmap = 127;
+ static constexpr int finished_bitmap = 255;
char* line = nullptr;
size_t len = 0;
@@ -98,6 +116,9 @@
} else if (header == "Gid:") {
process_info->gid = atoi(tab + 1);
field_bitmap |= 64;
+ } else if (header == "State:") {
+ process_info->state = parse_state(tab + 1);
+ field_bitmap |= 128;
}
}
diff --git a/libprocinfo/process_test.cpp b/libprocinfo/process_test.cpp
index 5ffd236..9da9278 100644
--- a/libprocinfo/process_test.cpp
+++ b/libprocinfo/process_test.cpp
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <chrono>
#include <set>
#include <thread>
#include <vector>
@@ -29,6 +30,8 @@
#include <android-base/stringprintf.h>
+using namespace std::chrono_literals;
+
#if !defined(__BIONIC__)
#include <syscall.h>
static pid_t gettid() {
@@ -82,3 +85,34 @@
}
}).join();
}
+
+TEST(process_info, process_state) {
+ int pipefd[2];
+ ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
+ pid_t forkpid = fork();
+
+ ASSERT_NE(-1, forkpid);
+ if (forkpid == 0) {
+ close(pipefd[1]);
+ char buf;
+ TEMP_FAILURE_RETRY(read(pipefd[0], &buf, 1));
+ _exit(0);
+ }
+
+ // Give the child some time to get to the read.
+ std::this_thread::sleep_for(100ms);
+
+ android::procinfo::ProcessInfo procinfo;
+ ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
+ ASSERT_EQ(android::procinfo::kProcessStateSleeping, procinfo.state);
+
+ ASSERT_EQ(0, kill(forkpid, SIGKILL));
+
+ // Give the kernel some time to kill the child.
+ std::this_thread::sleep_for(100ms);
+
+ ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
+ ASSERT_EQ(android::procinfo::kProcessStateZombie, procinfo.state);
+
+ ASSERT_EQ(forkpid, waitpid(forkpid, nullptr, 0));
+}
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index d27ceea..7883151 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -2,6 +2,8 @@
cc_library {
name: "libsuspend",
+ vendor_available: true,
+
srcs: [
"autosuspend.c",
"autosuspend_autosleep.c",
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 296bd26..550ef42 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -1,5 +1,7 @@
cc_library_shared {
name: "libsysutils",
+ vendor_available: true,
+
srcs: [
"src/SocketListener.cpp",
"src/FrameworkListener.cpp",
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index 1b6076f..87e2684 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -68,7 +68,7 @@
android_errorWriteLog(0x534e4554, "29831647");
c->sendMsg(500, "Command too large for buffer", false);
mSkipToNextNullByte = true;
- return false;
+ return true;
}
int offset = 0;
@@ -211,7 +211,6 @@
return;
overflow:
- LOG_EVENT_INT(78001, cli->getUid());
cli->sendMsg(500, "Command too long", false);
goto out;
}
diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp
index 896dad3..aad0394 100644
--- a/libsysutils/src/NetlinkListener.cpp
+++ b/libsysutils/src/NetlinkListener.cpp
@@ -57,8 +57,6 @@
count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,
mBuffer, sizeof(mBuffer), require_group, &uid));
if (count < 0) {
- if (uid > 0)
- LOG_EVENT_INT(65537, uid);
SLOGE("recvmsg failed (%s)", strerror(errno));
return false;
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 32ed6c3..94f0f8e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -31,63 +31,61 @@
},
},
- multilib: {
- lib32: {
- suffix: "32",
+ arch: {
+ mips: {
+ enabled: false,
},
- lib64: {
- suffix: "64",
+ mips64: {
+ enabled: false,
},
},
}
-cc_defaults {
- name: "libunwindstack_common",
+cc_library {
+ name: "libunwindstack",
defaults: ["libunwindstack_flags"],
+ export_include_dirs: ["include"],
srcs: [
"ArmExidx.cpp",
"DwarfCfa.cpp",
+ "DwarfDebugFrame.cpp",
+ "DwarfEhFrame.cpp",
"DwarfMemory.cpp",
"DwarfOp.cpp",
+ "DwarfSection.cpp",
"Elf.cpp",
"ElfInterface.cpp",
"ElfInterfaceArm.cpp",
"Log.cpp",
- "Regs.cpp",
"MapInfo.cpp",
"Maps.cpp",
"Memory.cpp",
+ "Regs.cpp",
"Symbols.cpp",
],
+ arch: {
+ x86: {
+ srcs: ["AsmGetRegsX86.S"],
+ },
+ x86_64: {
+ srcs: ["AsmGetRegsX86_64.S"],
+ },
+ },
+
shared_libs: [
"libbase",
"liblog",
- ],
-}
-
-cc_library {
- name: "libunwindstack",
- defaults: ["libunwindstack_common"],
-}
-
-cc_library {
- name: "libunwindstack_debug",
- defaults: ["libunwindstack_common"],
-
- cflags: [
- "-UNDEBUG",
- "-O0",
- "-g",
+ "liblzma",
],
}
//-------------------------------------------------------------------------
// Unit Tests
//-------------------------------------------------------------------------
-cc_defaults {
- name: "libunwindstack_test_common",
+cc_test {
+ name: "libunwindstack_test",
defaults: ["libunwindstack_flags"],
srcs: [
@@ -95,22 +93,31 @@
"tests/ArmExidxExtractTest.cpp",
"tests/DwarfCfaLogTest.cpp",
"tests/DwarfCfaTest.cpp",
+ "tests/DwarfDebugFrameTest.cpp",
+ "tests/DwarfEhFrameTest.cpp",
"tests/DwarfMemoryTest.cpp",
"tests/DwarfOpLogTest.cpp",
"tests/DwarfOpTest.cpp",
+ "tests/DwarfSectionTest.cpp",
+ "tests/DwarfSectionImplTest.cpp",
"tests/ElfInterfaceArmTest.cpp",
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
+ "tests/ElfTestUtils.cpp",
"tests/LogFake.cpp",
- "tests/MapInfoTest.cpp",
+ "tests/MapInfoCreateMemoryTest.cpp",
+ "tests/MapInfoGetElfTest.cpp",
"tests/MapsTest.cpp",
+ "tests/MemoryBufferTest.cpp",
"tests/MemoryFake.cpp",
"tests/MemoryFileTest.cpp",
"tests/MemoryLocalTest.cpp",
"tests/MemoryRangeTest.cpp",
"tests/MemoryRemoteTest.cpp",
+ "tests/MemoryTest.cpp",
"tests/RegsTest.cpp",
"tests/SymbolsTest.cpp",
+ "tests/UnwindTest.cpp",
],
cflags: [
@@ -121,6 +128,48 @@
shared_libs: [
"libbase",
"liblog",
+ "liblzma",
+ "libunwindstack",
+ ],
+
+ static_libs: [
+ "libgmock",
+ ],
+
+ target: {
+ linux: {
+ host_ldlibs: [
+ "-lrt",
+ ],
+ },
+ },
+
+ data: [
+ "tests/files/elf32.xz",
+ "tests/files/elf64.xz",
+ ],
+}
+
+//-------------------------------------------------------------------------
+// Tools
+//-------------------------------------------------------------------------
+cc_defaults {
+ name: "libunwindstack_tools",
+ defaults: ["libunwindstack_flags"],
+
+ shared_libs: [
+ "libunwindstack",
+ "libbase",
+ "liblzma",
+ ],
+}
+
+cc_binary {
+ name: "unwind",
+ defaults: ["libunwindstack_tools"],
+
+ srcs: [
+ "tools/unwind.cpp",
],
target: {
@@ -132,50 +181,31 @@
},
}
-// These unit tests run against the shared library.
-cc_test {
- name: "libunwindstack_test",
- defaults: ["libunwindstack_test_common"],
+cc_binary {
+ name: "unwind_info",
+ defaults: ["libunwindstack_tools"],
- shared_libs: [
- "libunwindstack",
+ srcs: [
+ "tools/unwind_info.cpp",
],
}
-// These unit tests run against the static debug library.
-cc_test {
- name: "libunwindstack_test_debug",
- defaults: ["libunwindstack_test_common"],
-
- static_libs: [
- "libunwindstack_debug",
- ],
-}
-
-//-------------------------------------------------------------------------
-// Utility Executables
-//-------------------------------------------------------------------------
-cc_defaults {
- name: "libunwindstack_executables",
- defaults: ["libunwindstack_flags"],
-
- shared_libs: [
- "libunwindstack",
- "libbase",
- ],
-
- static_libs: [
- "liblog",
- ],
-
- compile_multilib: "both",
-}
-
cc_binary {
- name: "unwind_info",
- defaults: ["libunwindstack_executables"],
+ name: "unwind_symbols",
+ defaults: ["libunwindstack_tools"],
srcs: [
- "unwind_info.cpp",
+ "tools/unwind_symbols.cpp",
+ ],
+}
+
+// Generates the elf data for use in the tests for .gnu_debugdata frames.
+// Once these files are generated, use the xz command to compress the data.
+cc_binary_host {
+ name: "gen_gnudebugdata",
+ defaults: ["libunwindstack_flags"],
+
+ srcs: [
+ "tests/GenGnuDebugdata.cpp",
],
}
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 12adf57..fed3e0e 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <assert.h>
#include <stdint.h>
#include <deque>
@@ -22,11 +21,15 @@
#include <android-base/stringprintf.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
#include "ArmExidx.h"
-#include "Log.h"
+#include "Check.h"
#include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
void ArmExidx::LogRawData() {
std::string log_str("Raw Data:");
@@ -173,7 +176,7 @@
}
inline bool ArmExidx::DecodePrefix_10_00(uint8_t byte) {
- assert((byte >> 4) == 0x8);
+ CHECK((byte >> 4) == 0x8);
uint16_t registers = (byte & 0xf) << 8;
if (!GetByte(&byte)) {
@@ -232,7 +235,7 @@
}
inline bool ArmExidx::DecodePrefix_10_01(uint8_t byte) {
- assert((byte >> 4) == 0x9);
+ CHECK((byte >> 4) == 0x9);
uint8_t bits = byte & 0xf;
if (bits == 13 || bits == 15) {
@@ -258,7 +261,7 @@
}
inline bool ArmExidx::DecodePrefix_10_10(uint8_t byte) {
- assert((byte >> 4) == 0xa);
+ CHECK((byte >> 4) == 0xa);
// 10100nnn: Pop r4-r[4+nnn]
// 10101nnn: Pop r4-r[4+nnn], r14
@@ -419,7 +422,7 @@
}
inline bool ArmExidx::DecodePrefix_10_11_1nnn(uint8_t byte) {
- assert((byte & ~0x07) == 0xb8);
+ CHECK((byte & ~0x07) == 0xb8);
// 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
if (log_) {
@@ -439,7 +442,7 @@
}
inline bool ArmExidx::DecodePrefix_10(uint8_t byte) {
- assert((byte >> 6) == 0x2);
+ CHECK((byte >> 6) == 0x2);
switch ((byte >> 4) & 0x3) {
case 0:
@@ -469,7 +472,7 @@
}
inline bool ArmExidx::DecodePrefix_11_000(uint8_t byte) {
- assert((byte & ~0x07) == 0xc0);
+ CHECK((byte & ~0x07) == 0xc0);
uint8_t bits = byte & 0x7;
if (bits == 6) {
@@ -550,7 +553,7 @@
}
inline bool ArmExidx::DecodePrefix_11_001(uint8_t byte) {
- assert((byte & ~0x07) == 0xc8);
+ CHECK((byte & ~0x07) == 0xc8);
uint8_t bits = byte & 0x7;
if (bits == 0) {
@@ -605,7 +608,7 @@
}
inline bool ArmExidx::DecodePrefix_11_010(uint8_t byte) {
- assert((byte & ~0x07) == 0xd0);
+ CHECK((byte & ~0x07) == 0xd0);
// 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
if (log_) {
@@ -624,7 +627,7 @@
}
inline bool ArmExidx::DecodePrefix_11(uint8_t byte) {
- assert((byte >> 6) == 0x3);
+ CHECK((byte >> 6) == 0x3);
switch ((byte >> 3) & 0x7) {
case 0:
@@ -684,3 +687,5 @@
while (Decode());
return status_ == ARM_STATUS_FINISH;
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
index 8c7f15a..f4361d4 100644
--- a/libunwindstack/ArmExidx.h
+++ b/libunwindstack/ArmExidx.h
@@ -21,6 +21,8 @@
#include <deque>
+namespace unwindstack {
+
// Forward declarations.
class Memory;
class RegsArm;
@@ -105,4 +107,6 @@
bool pc_set_ = false;
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_ARM_EXIDX_H
diff --git a/libunwindstack/AsmGetRegsX86.S b/libunwindstack/AsmGetRegsX86.S
new file mode 100644
index 0000000..14927a3
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ .text
+ .global AsmGetRegs
+ .balign 16
+ .type AsmGetRegs, @function
+AsmGetRegs:
+ .cfi_startproc
+ mov 4(%esp), %eax
+ movl $0, (%eax)
+ movl %ecx, 4(%eax)
+ movl %edx, 8(%eax)
+ movl %ebx, 12(%eax)
+
+ /* ESP */
+ leal 4(%esp), %ecx
+ movl %ecx, 16(%eax)
+
+ movl %ebp, 20(%eax)
+ movl %esi, 24(%eax)
+ movl %edi, 28(%eax)
+
+ /* EIP */
+ movl (%esp), %ecx
+ movl %ecx, 32(%eax)
+
+ movl %cs, 36(%eax)
+ movl %ss, 40(%eax)
+ movl %ds, 44(%eax)
+ movl %es, 48(%eax)
+ movl %fs, 52(%eax)
+ movl %gs, 56(%eax)
+ ret
+
+ .cfi_endproc
+ .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsX86_64.S b/libunwindstack/AsmGetRegsX86_64.S
new file mode 100644
index 0000000..4cd3b6f
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86_64.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ .text
+ .global AsmGetRegs
+ .balign 16
+ .type AsmGetRegs, @function
+AsmGetRegs:
+ .cfi_startproc
+ movq %rax, (%rdi)
+ movq %rdx, 8(%rdi)
+ movq %rcx, 16(%rdi)
+ movq %rbx, 24(%rdi)
+ movq %rsi, 32(%rdi)
+ movq %rdi, 40(%rdi)
+ movq %rbp, 48(%rdi)
+
+ /* RSP */
+ lea 8(%rsp), %rax
+ movq %rax, 56(%rdi)
+
+ movq %r8, 64(%rdi)
+ movq %r9, 72(%rdi)
+ movq %r10, 80(%rdi)
+ movq %r11, 88(%rdi)
+ movq %r12, 96(%rdi)
+ movq %r13, 104(%rdi)
+ movq %r14, 112(%rdi)
+ movq %r15, 120(%rdi)
+
+ /* RIP */
+ movq (%rsp), %rax
+ movq %rax, 128(%rdi)
+ ret
+
+ .cfi_endproc
+ .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/Check.h b/libunwindstack/Check.h
new file mode 100644
index 0000000..2d216d7
--- /dev/null
+++ b/libunwindstack/Check.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_ERROR_H
+#define _LIBUNWINDSTACK_ERROR_H
+
+#include <stdlib.h>
+
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
+
+#define CHECK(assertion) \
+ if (__builtin_expect(!(assertion), false)) { \
+ log(0, "%s:%d: %s\n", __FILE__, __LINE__, #assertion); \
+ abort(); \
+ }
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_ERROR_H
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 006f039..b1d55ba 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -23,12 +23,15 @@
#include <android-base/stringprintf.h>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/Log.h>
+
#include "DwarfCfa.h"
#include "DwarfEncoding.h"
-#include "DwarfMemory.h"
+#include "DwarfError.h"
#include "DwarfOp.h"
-#include "DwarfStructs.h"
-#include "Log.h"
+
+namespace unwindstack {
template <typename AddressType>
constexpr typename DwarfCfa<AddressType>::process_func DwarfCfa<AddressType>::kCallbackTable[64];
@@ -711,3 +714,5 @@
// Explicitly instantiate DwarfCfa.
template class DwarfCfa<uint32_t>;
template class DwarfCfa<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index ce7da4a..62b9b7a 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -24,10 +24,13 @@
#include <type_traits>
#include <vector>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+
#include "DwarfError.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
+
+namespace unwindstack {
// DWARF Standard home: http://dwarfstd.org/
// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
@@ -63,7 +66,7 @@
typedef typename std::make_signed<AddressType>::type SignedType;
public:
- DwarfCfa(DwarfMemory* memory, const DwarfFDE* fde) : memory_(memory), fde_(fde) {}
+ DwarfCfa(DwarfMemory* memory, const DwarfFde* fde) : memory_(memory), fde_(fde) {}
virtual ~DwarfCfa() = default;
bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
@@ -88,7 +91,7 @@
private:
DwarfError last_error_;
DwarfMemory* memory_;
- const DwarfFDE* fde_;
+ const DwarfFde* fde_;
AddressType cur_pc_;
const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
@@ -252,4 +255,6 @@
};
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_DWARF_CFA_H
diff --git a/libunwindstack/DwarfDebugFrame.cpp b/libunwindstack/DwarfDebugFrame.cpp
new file mode 100644
index 0000000..5707596
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Memory.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+namespace unwindstack {
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
+ offset_ = offset;
+ end_offset_ = offset + size;
+
+ memory_.clear_func_offset();
+ memory_.clear_text_offset();
+ memory_.set_data_offset(offset);
+ memory_.set_cur_offset(offset);
+
+ return CreateSortedFdeList();
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
+ uint8_t version;
+ if (!memory_.ReadBytes(&version, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ // Read the augmentation string.
+ std::vector<char> aug_string;
+ char aug_value;
+ bool get_encoding = false;
+ do {
+ if (!memory_.ReadBytes(&aug_value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (aug_value == 'R') {
+ get_encoding = true;
+ }
+ aug_string.push_back(aug_value);
+ } while (aug_value != '\0');
+
+ if (version == 4) {
+ // Skip the Address Size field.
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+
+ // Read the segment size.
+ if (!memory_.ReadBytes(segment_size, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } else {
+ *segment_size = 0;
+ }
+
+ if (aug_string[0] != 'z' || !get_encoding) {
+ // No encoding
+ return true;
+ }
+
+ // Skip code alignment factor
+ uint8_t value;
+ do {
+ if (!memory_.ReadBytes(&value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } while (value & 0x80);
+
+ // Skip data alignment factor
+ do {
+ if (!memory_.ReadBytes(&value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } while (value & 0x80);
+
+ if (version == 1) {
+ // Skip return address register.
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+ } else {
+ // Skip return address register.
+ do {
+ if (!memory_.ReadBytes(&value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } while (value & 0x80);
+ }
+
+ // Skip the augmentation length.
+ do {
+ if (!memory_.ReadBytes(&value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } while (value & 0x80);
+
+ for (size_t i = 1; i < aug_string.size(); i++) {
+ if (aug_string[i] == 'R') {
+ if (!memory_.ReadBytes(encoding, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ // Got the encoding, that's all we are looking for.
+ return true;
+ } else if (aug_string[i] == 'L') {
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+ } else if (aug_string[i] == 'P') {
+ uint8_t encoding;
+ if (!memory_.ReadBytes(&encoding, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ uint64_t value;
+ if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ }
+ }
+
+ // It should be impossible to get here.
+ abort();
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::AddFdeInfo(uint64_t entry_offset, uint8_t segment_size,
+ uint8_t encoding) {
+ if (segment_size != 0) {
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+ }
+
+ uint64_t start;
+ if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ uint64_t length;
+ if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (length != 0) {
+ fdes_.emplace_back(entry_offset, start, length);
+ }
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::CreateSortedFdeList() {
+ memory_.set_cur_offset(offset_);
+
+ // Loop through all of the entries and read just enough to create
+ // a sorted list of pcs.
+ // This code assumes that first comes the cie, then the fdes that
+ // it applies to.
+ uint64_t cie_offset = 0;
+ uint8_t address_encoding;
+ uint8_t segment_size;
+ while (memory_.cur_offset() < end_offset_) {
+ uint64_t cur_entry_offset = memory_.cur_offset();
+
+ // Figure out the entry length and type.
+ uint32_t value32;
+ if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ uint64_t next_entry_offset;
+ if (value32 == static_cast<uint32_t>(-1)) {
+ uint64_t value64;
+ if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ next_entry_offset = memory_.cur_offset() + value64;
+
+ // Read the Cie Id of a Cie or the pointer of the Fde.
+ if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (value64 == static_cast<uint64_t>(-1)) {
+ // Cie 64 bit
+ address_encoding = DW_EH_PE_sdata8;
+ if (!GetCieInfo(&segment_size, &address_encoding)) {
+ return false;
+ }
+ cie_offset = cur_entry_offset;
+ } else {
+ if (offset_ + value64 != cie_offset) {
+ // This means that this Fde is not following the Cie.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Fde 64 bit
+ if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
+ return false;
+ }
+ }
+ } else {
+ next_entry_offset = memory_.cur_offset() + value32;
+
+ // Read the Cie Id of a Cie or the pointer of the Fde.
+ if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (value32 == static_cast<uint32_t>(-1)) {
+ // Cie 32 bit
+ address_encoding = DW_EH_PE_sdata4;
+ if (!GetCieInfo(&segment_size, &address_encoding)) {
+ return false;
+ }
+ cie_offset = cur_entry_offset;
+ } else {
+ if (offset_ + value32 != cie_offset) {
+ // This means that this Fde is not following the Cie.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Fde 32 bit
+ if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
+ return false;
+ }
+ }
+ }
+
+ if (next_entry_offset < memory_.cur_offset()) {
+ // This indicates some kind of corruption, or malformed section data.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ memory_.set_cur_offset(next_entry_offset);
+ }
+
+ // Sort the entries.
+ std::sort(fdes_.begin(), fdes_.end(), [](const FdeInfo& a, const FdeInfo& b) {
+ if (a.start == b.start) return a.end < b.end;
+ return a.start < b.start;
+ });
+
+ fde_count_ = fdes_.size();
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+ if (fde_count_ == 0) {
+ return false;
+ }
+
+ size_t first = 0;
+ size_t last = fde_count_;
+ while (first < last) {
+ size_t current = (first + last) / 2;
+ const FdeInfo* info = &fdes_[current];
+ if (pc >= info->start && pc <= info->end) {
+ *fde_offset = info->offset;
+ return true;
+ }
+
+ if (pc < info->start) {
+ last = current;
+ } else {
+ first = current + 1;
+ }
+ }
+ return false;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfDebugFrame<AddressType>::GetFdeFromIndex(size_t index) {
+ if (index >= fdes_.size()) {
+ return nullptr;
+ }
+ return this->GetFdeFromOffset(fdes_[index].offset);
+}
+
+// Explicitly instantiate DwarfDebugFrame.
+template class DwarfDebugFrame<uint32_t>;
+template class DwarfDebugFrame<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
new file mode 100644
index 0000000..6a6178e
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+#define _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
+template <typename AddressType>
+class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
+ public:
+ // Add these so that the protected members of DwarfSectionImpl
+ // can be accessed without needing a this->.
+ using DwarfSectionImpl<AddressType>::memory_;
+ using DwarfSectionImpl<AddressType>::fde_count_;
+ using DwarfSectionImpl<AddressType>::last_error_;
+
+ struct FdeInfo {
+ FdeInfo(uint64_t offset, uint64_t start, uint64_t length)
+ : offset(offset), start(start), end(start + length) {}
+
+ uint64_t offset;
+ AddressType start;
+ AddressType end;
+ };
+
+ DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+ virtual ~DwarfDebugFrame() = default;
+
+ bool Init(uint64_t offset, uint64_t size) override;
+
+ bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+
+ const DwarfFde* GetFdeFromIndex(size_t index) override;
+
+ bool IsCie32(uint32_t value32) override { return value32 == static_cast<uint32_t>(-1); }
+
+ bool IsCie64(uint64_t value64) override { return value64 == static_cast<uint64_t>(-1); }
+
+ uint64_t GetCieOffsetFromFde32(uint32_t pointer) override { return offset_ + pointer; }
+
+ uint64_t GetCieOffsetFromFde64(uint64_t pointer) override { return offset_ + pointer; }
+
+ uint64_t AdjustPcFromFde(uint64_t pc) override { return pc; }
+
+ bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);
+
+ bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding);
+
+ bool CreateSortedFdeList();
+
+ protected:
+ uint64_t offset_;
+ uint64_t end_offset_;
+
+ std::vector<FdeInfo> fdes_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrame.cpp
new file mode 100644
index 0000000..d0b35c3
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+#include "DwarfEhFrame.h"
+#include "DwarfError.h"
+
+namespace unwindstack {
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
+ uint8_t data[4];
+
+ memory_.clear_func_offset();
+ memory_.clear_text_offset();
+ memory_.set_data_offset(offset);
+ memory_.set_cur_offset(offset);
+
+ // Read the first four bytes all at once.
+ if (!memory_.ReadBytes(data, 4)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ version_ = data[0];
+ if (version_ != 1) {
+ // Unknown version.
+ last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+ return false;
+ }
+
+ ptr_encoding_ = data[1];
+ uint8_t fde_count_encoding = data[2];
+ table_encoding_ = data[3];
+ table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
+
+ memory_.set_pc_offset(memory_.cur_offset());
+ if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ memory_.set_pc_offset(memory_.cur_offset());
+ if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ entries_offset_ = memory_.cur_offset();
+ entries_end_ = offset + size;
+ entries_data_offset_ = offset;
+ cur_entries_offset_ = entries_offset_;
+
+ return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfEhFrame<AddressType>::GetFdeFromIndex(size_t index) {
+ const FdeInfo* info = GetFdeInfoFromIndex(index);
+ if (info == nullptr) {
+ return nullptr;
+ }
+ return this->GetFdeFromOffset(info->offset);
+}
+
+template <typename AddressType>
+const typename DwarfEhFrame<AddressType>::FdeInfo* DwarfEhFrame<AddressType>::GetFdeInfoFromIndex(
+ size_t index) {
+ auto entry = fde_info_.find(index);
+ if (entry != fde_info_.end()) {
+ return &fde_info_[index];
+ }
+ FdeInfo* info = &fde_info_[index];
+
+ memory_.set_data_offset(entries_data_offset_);
+ memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
+ memory_.set_pc_offset(memory_.cur_offset());
+ uint64_t value;
+ if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
+ !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ fde_info_.erase(index);
+ return nullptr;
+ }
+ info->pc = value;
+ return info;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
+ uint64_t total_entries) {
+ CHECK(fde_count_ > 0);
+ CHECK(total_entries <= fde_count_);
+
+ size_t first = 0;
+ size_t last = total_entries;
+ while (first < last) {
+ size_t current = (first + last) / 2;
+ const FdeInfo* info = GetFdeInfoFromIndex(current);
+ if (pc == info->pc) {
+ *fde_offset = info->offset;
+ return true;
+ }
+ if (pc < info->pc) {
+ last = current;
+ } else {
+ first = current + 1;
+ }
+ }
+ if (last != 0) {
+ const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
+ *fde_offset = info->offset;
+ return true;
+ }
+ return false;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
+ CHECK(fde_count_ != 0);
+ last_error_ = DWARF_ERROR_NONE;
+ // We can do a binary search if the pc is in the range of the elements
+ // that have already been cached.
+ if (!fde_info_.empty()) {
+ const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
+ if (pc >= info->pc) {
+ *fde_offset = info->offset;
+ return true;
+ }
+ if (pc < info->pc) {
+ return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
+ }
+ }
+
+ if (cur_entries_offset_ == 0) {
+ // All entries read, or error encountered.
+ return false;
+ }
+
+ memory_.set_data_offset(entries_data_offset_);
+ memory_.set_cur_offset(cur_entries_offset_);
+ cur_entries_offset_ = 0;
+
+ FdeInfo* prev_info = nullptr;
+ for (size_t current = fde_info_.size();
+ current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
+ memory_.set_pc_offset(memory_.cur_offset());
+ uint64_t value;
+ if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ FdeInfo* info = &fde_info_[current];
+ if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+ fde_info_.erase(current);
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ info->pc = value;
+
+ if (pc < info->pc) {
+ if (prev_info == nullptr) {
+ return false;
+ }
+ cur_entries_offset_ = memory_.cur_offset();
+ *fde_offset = prev_info->offset;
+ return true;
+ }
+ prev_info = info;
+ }
+
+ if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
+ *fde_offset = prev_info->offset;
+ return true;
+ }
+ return false;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+ if (fde_count_ == 0) {
+ return false;
+ }
+
+ if (table_entry_size_ > 0) {
+ // Do a binary search since the size of each table entry is fixed.
+ return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
+ } else {
+ // Do a sequential search since each table entry size is variable.
+ return GetFdeOffsetSequential(pc, fde_offset);
+ }
+}
+
+// Explicitly instantiate DwarfEhFrame.
+template class DwarfEhFrame<uint32_t>;
+template class DwarfEhFrame<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
new file mode 100644
index 0000000..4207b42
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+#define _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+
+#include <stdint.h>
+
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+template <typename AddressType>
+class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
+ public:
+ // Add these so that the protected members of DwarfSectionImpl
+ // can be accessed without needing a this->.
+ using DwarfSectionImpl<AddressType>::memory_;
+ using DwarfSectionImpl<AddressType>::fde_count_;
+ using DwarfSectionImpl<AddressType>::last_error_;
+
+ struct FdeInfo {
+ AddressType pc;
+ uint64_t offset;
+ };
+
+ DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+ virtual ~DwarfEhFrame() = default;
+
+ bool Init(uint64_t offset, uint64_t size) override;
+
+ bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+
+ const DwarfFde* GetFdeFromIndex(size_t index) override;
+
+ bool IsCie32(uint32_t value32) override { return value32 == 0; }
+
+ bool IsCie64(uint64_t value64) override { return value64 == 0; }
+
+ uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+ return memory_.cur_offset() - pointer - 4;
+ }
+
+ uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+ return memory_.cur_offset() - pointer - 8;
+ }
+
+ uint64_t AdjustPcFromFde(uint64_t pc) override {
+ // The eh_frame uses relative pcs.
+ return pc + memory_.cur_offset();
+ }
+
+ const FdeInfo* GetFdeInfoFromIndex(size_t index);
+
+ bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset);
+
+ bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
+
+ protected:
+ uint8_t version_;
+ uint8_t ptr_encoding_;
+ uint8_t table_encoding_;
+ size_t table_entry_size_;
+
+ uint64_t ptr_offset_;
+
+ uint64_t entries_offset_;
+ uint64_t entries_end_;
+ uint64_t entries_data_offset_;
+ uint64_t cur_entries_offset_ = 0;
+
+ std::unordered_map<uint64_t, FdeInfo> fde_info_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h
index 0ff3b8c..20db222 100644
--- a/libunwindstack/DwarfEncoding.h
+++ b/libunwindstack/DwarfEncoding.h
@@ -19,6 +19,8 @@
#include <stdint.h>
+namespace unwindstack {
+
enum DwarfEncoding : uint8_t {
DW_EH_PE_omit = 0xff,
@@ -44,4 +46,6 @@
DW_EH_PE_block = 0x0f,
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/libunwindstack/DwarfError.h b/libunwindstack/DwarfError.h
index 824c307..54199b8 100644
--- a/libunwindstack/DwarfError.h
+++ b/libunwindstack/DwarfError.h
@@ -19,6 +19,8 @@
#include <stdint.h>
+namespace unwindstack {
+
enum DwarfError : uint8_t {
DWARF_ERROR_NONE,
DWARF_ERROR_MEMORY_INVALID,
@@ -27,6 +29,10 @@
DWARF_ERROR_STACK_INDEX_NOT_VALID,
DWARF_ERROR_NOT_IMPLEMENTED,
DWARF_ERROR_TOO_MANY_ITERATIONS,
+ DWARF_ERROR_CFA_NOT_DEFINED,
+ DWARF_ERROR_UNSUPPORTED_VERSION,
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
index 11806ea..b6e0412 100644
--- a/libunwindstack/DwarfMemory.cpp
+++ b/libunwindstack/DwarfMemory.cpp
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-#include <assert.h>
#include <stdint.h>
#include <string>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
#include "DwarfEncoding.h"
-#include "DwarfMemory.h"
-#include "Memory.h"
+
+namespace unwindstack {
bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) {
if (!memory_->Read(cur_offset_, dst, num_bytes)) {
@@ -100,8 +103,8 @@
}
bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) {
- assert((encoding & 0x0f) == 0);
- assert(encoding != DW_EH_PE_aligned);
+ CHECK((encoding & 0x0f) == 0);
+ CHECK(encoding != DW_EH_PE_aligned);
// Handle the encoding.
switch (encoding) {
@@ -246,3 +249,5 @@
template bool DwarfMemory::ReadEncodedValue<uint32_t>(uint8_t, uint64_t*);
template bool DwarfMemory::ReadEncodedValue<uint64_t>(uint8_t, uint64_t*);
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
index 507ca08..b3fd0df 100644
--- a/libunwindstack/DwarfOp.cpp
+++ b/libunwindstack/DwarfOp.cpp
@@ -22,12 +22,15 @@
#include <android-base/stringprintf.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
#include "DwarfError.h"
-#include "DwarfMemory.h"
#include "DwarfOp.h"
-#include "Log.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
template <typename AddressType>
constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];
@@ -460,3 +463,5 @@
// Explicitly instantiate DwarfOp.
template class DwarfOp<uint32_t>;
template class DwarfOp<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
index ed6537a..c29bf35 100644
--- a/libunwindstack/DwarfOp.h
+++ b/libunwindstack/DwarfOp.h
@@ -25,6 +25,9 @@
#include <vector>
#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+namespace unwindstack {
enum DwarfVersion : uint8_t {
DWARF_VERSION_2 = 2,
@@ -37,7 +40,7 @@
class DwarfMemory;
class Memory;
template <typename AddressType>
-class RegsTmpl;
+class RegsImpl;
template <typename AddressType>
class DwarfOp {
@@ -67,7 +70,7 @@
AddressType StackAt(size_t index) { return stack_[index]; }
size_t StackSize() { return stack_.size(); }
- void set_regs(RegsTmpl<AddressType>* regs) { regs_ = regs; }
+ void set_regs(RegsImpl<AddressType>* regs) { regs_ = regs; }
DwarfError last_error() { return last_error_; }
@@ -91,7 +94,7 @@
DwarfMemory* memory_;
Memory* regular_memory_;
- RegsTmpl<AddressType>* regs_;
+ RegsImpl<AddressType>* regs_;
bool is_register_ = false;
DwarfError last_error_ = DWARF_ERROR_NONE;
uint8_t cur_op_;
@@ -1633,4 +1636,6 @@
};
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
new file mode 100644
index 0000000..1234eb1
--- /dev/null
+++ b/libunwindstack/DwarfSection.cpp
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "DwarfCfa.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+#include "DwarfOp.h"
+
+namespace unwindstack {
+
+DwarfSection::DwarfSection(Memory* memory) : memory_(memory), last_error_(DWARF_ERROR_NONE) {}
+
+const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
+ uint64_t fde_offset;
+ if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
+ return nullptr;
+ }
+ const DwarfFde* fde = GetFdeFromOffset(fde_offset);
+ // Guaranteed pc >= pc_start, need to check pc in the fde range.
+ if (pc < fde->pc_end) {
+ return fde;
+ }
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return nullptr;
+}
+
+bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+ last_error_ = DWARF_ERROR_NONE;
+ const DwarfFde* fde = GetFdeFromPc(pc);
+ if (fde == nullptr || fde->cie == nullptr) {
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+
+ // Now get the location information for this pc.
+ dwarf_loc_regs_t loc_regs;
+ if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
+ return false;
+ }
+
+ // Now eval the actual registers.
+ return Eval(fde->cie, process_memory, loc_regs, regs);
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uint8_t version,
+ Memory* regular_memory, AddressType* value) {
+ DwarfOp<AddressType> op(&memory_, regular_memory);
+
+ // Need to evaluate the op data.
+ uint64_t start = loc.values[1];
+ uint64_t end = start + loc.values[0];
+ if (!op.Eval(start, end, version)) {
+ last_error_ = op.last_error();
+ return false;
+ }
+ if (op.StackSize() == 0) {
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ // We don't support an expression that evaluates to a register number.
+ if (op.is_register()) {
+ last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
+ return false;
+ }
+ *value = op.StackAt(0);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
+ const dwarf_loc_regs_t& loc_regs, Regs* regs) {
+ RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
+ if (cie->return_address_register >= cur_regs->total_regs()) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Get the cfa value;
+ auto cfa_entry = loc_regs.find(CFA_REG);
+ if (cfa_entry == loc_regs.end()) {
+ last_error_ = DWARF_ERROR_CFA_NOT_DEFINED;
+ return false;
+ }
+
+ AddressType prev_pc = regs->pc();
+ AddressType prev_cfa = regs->sp();
+
+ AddressType cfa;
+ const DwarfLocation* loc = &cfa_entry->second;
+ // Only a few location types are valid for the cfa.
+ switch (loc->type) {
+ case DWARF_LOCATION_REGISTER:
+ if (loc->values[0] >= cur_regs->total_regs()) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ // If the stack pointer register is the CFA, and the stack
+ // pointer register does not have any associated location
+ // information, use the current cfa value.
+ if (regs->sp_reg() == loc->values[0] && loc_regs.count(regs->sp_reg()) == 0) {
+ cfa = prev_cfa;
+ } else {
+ cfa = (*cur_regs)[loc->values[0]];
+ }
+ cfa += loc->values[1];
+ break;
+ case DWARF_LOCATION_EXPRESSION:
+ case DWARF_LOCATION_VAL_EXPRESSION: {
+ AddressType value;
+ if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
+ return false;
+ }
+ if (loc->type == DWARF_LOCATION_EXPRESSION) {
+ if (!regular_memory->Read(value, &cfa, sizeof(AddressType))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } else {
+ cfa = value;
+ }
+ break;
+ }
+ default:
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // This code is not guaranteed to work in cases where a register location
+ // is a double indirection to the actual value. For example, if r3 is set
+ // to r5 + 4, and r5 is set to CFA + 4, then this won't necessarily work
+ // because it does not guarantee that r5 is evaluated before r3.
+ // Check that this case does not exist, and error if it does.
+ bool return_address_undefined = false;
+ for (const auto& entry : loc_regs) {
+ uint16_t reg = entry.first;
+ // Already handled the CFA register.
+ if (reg == CFA_REG) continue;
+
+ if (reg >= cur_regs->total_regs()) {
+ // Skip this unknown register.
+ continue;
+ }
+
+ const DwarfLocation* loc = &entry.second;
+ switch (loc->type) {
+ case DWARF_LOCATION_OFFSET:
+ if (!regular_memory->Read(cfa + loc->values[0], &(*cur_regs)[reg], sizeof(AddressType))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ break;
+ case DWARF_LOCATION_VAL_OFFSET:
+ (*cur_regs)[reg] = cfa + loc->values[0];
+ break;
+ case DWARF_LOCATION_REGISTER: {
+ uint16_t cur_reg = loc->values[0];
+ if (cur_reg >= cur_regs->total_regs()) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ if (loc_regs.find(cur_reg) != loc_regs.end()) {
+ // This is a double indirection, a register definition references
+ // another register which is also defined as something other
+ // than a register.
+ log(0,
+ "Invalid indirection: register %d references register %d which is "
+ "not a plain register.\n",
+ reg, cur_reg);
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ (*cur_regs)[reg] = (*cur_regs)[cur_reg] + loc->values[1];
+ break;
+ }
+ case DWARF_LOCATION_EXPRESSION:
+ case DWARF_LOCATION_VAL_EXPRESSION: {
+ AddressType value;
+ if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
+ return false;
+ }
+ if (loc->type == DWARF_LOCATION_EXPRESSION) {
+ if (!regular_memory->Read(value, &(*cur_regs)[reg], sizeof(AddressType))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } else {
+ (*cur_regs)[reg] = value;
+ }
+ break;
+ }
+ case DWARF_LOCATION_UNDEFINED:
+ if (reg == cie->return_address_register) {
+ return_address_undefined = true;
+ }
+ default:
+ break;
+ }
+ }
+
+ // Find the return address location.
+ if (return_address_undefined) {
+ cur_regs->set_pc(0);
+ } else {
+ cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
+ }
+ cur_regs->set_sp(cfa);
+ // Stop if the cfa and pc are the same.
+ return prev_cfa != cfa || prev_pc != cur_regs->pc();
+}
+
+template <typename AddressType>
+const DwarfCie* DwarfSectionImpl<AddressType>::GetCie(uint64_t offset) {
+ auto cie_entry = cie_entries_.find(offset);
+ if (cie_entry != cie_entries_.end()) {
+ return &cie_entry->second;
+ }
+ DwarfCie* cie = &cie_entries_[offset];
+ memory_.set_cur_offset(offset);
+ if (!FillInCie(cie)) {
+ // Erase the cached entry.
+ cie_entries_.erase(offset);
+ return nullptr;
+ }
+ return cie;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
+ uint32_t length32;
+ if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ // Set the default for the lsda encoding.
+ cie->lsda_encoding = DW_EH_PE_omit;
+
+ if (length32 == static_cast<uint32_t>(-1)) {
+ // 64 bit Cie
+ uint64_t length64;
+ if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ cie->cfa_instructions_end = memory_.cur_offset() + length64;
+ cie->fde_address_encoding = DW_EH_PE_sdata8;
+
+ uint64_t cie_id;
+ if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (!IsCie64(cie_id)) {
+ // This is not a Cie, something has gone horribly wrong.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ } else {
+ // 32 bit Cie
+ cie->cfa_instructions_end = memory_.cur_offset() + length32;
+ cie->fde_address_encoding = DW_EH_PE_sdata4;
+
+ uint32_t cie_id;
+ if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (!IsCie32(cie_id)) {
+ // This is not a Cie, something has gone horribly wrong.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ }
+
+ if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
+ // Unrecognized version.
+ last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+ return false;
+ }
+
+ // Read the augmentation string.
+ char aug_value;
+ do {
+ if (!memory_.ReadBytes(&aug_value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ cie->augmentation_string.push_back(aug_value);
+ } while (aug_value != '\0');
+
+ if (cie->version == 4) {
+ // Skip the Address Size field since we only use it for validation.
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+
+ // Segment Size
+ if (!memory_.ReadBytes(&cie->segment_size, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ }
+
+ // Code Alignment Factor
+ if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ // Data Alignment Factor
+ if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (cie->version == 1) {
+ // Return Address is a single byte.
+ uint8_t return_address_register;
+ if (!memory_.ReadBytes(&return_address_register, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ cie->return_address_register = return_address_register;
+ } else if (!memory_.ReadULEB128(&cie->return_address_register)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (cie->augmentation_string[0] != 'z') {
+ cie->cfa_instructions_offset = memory_.cur_offset();
+ return true;
+ }
+
+ uint64_t aug_length;
+ if (!memory_.ReadULEB128(&aug_length)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
+
+ for (size_t i = 1; i < cie->augmentation_string.size(); i++) {
+ switch (cie->augmentation_string[i]) {
+ case 'L':
+ if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ break;
+ case 'P': {
+ uint8_t encoding;
+ if (!memory_.ReadBytes(&encoding, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } break;
+ case 'R':
+ if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromOffset(uint64_t offset) {
+ auto fde_entry = fde_entries_.find(offset);
+ if (fde_entry != fde_entries_.end()) {
+ return &fde_entry->second;
+ }
+ DwarfFde* fde = &fde_entries_[offset];
+ memory_.set_cur_offset(offset);
+ if (!FillInFde(fde)) {
+ fde_entries_.erase(offset);
+ return nullptr;
+ }
+ return fde;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
+ uint32_t length32;
+ if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (length32 == static_cast<uint32_t>(-1)) {
+ // 64 bit Fde.
+ uint64_t length64;
+ if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ fde->cfa_instructions_end = memory_.cur_offset() + length64;
+
+ uint64_t value64;
+ if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (IsCie64(value64)) {
+ // This is a Cie, this means something has gone wrong.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Get the Cie pointer, which is necessary to properly read the rest of
+ // of the Fde information.
+ fde->cie_offset = GetCieOffsetFromFde64(value64);
+ } else {
+ // 32 bit Fde.
+ fde->cfa_instructions_end = memory_.cur_offset() + length32;
+
+ uint32_t value32;
+ if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (IsCie32(value32)) {
+ // This is a Cie, this means something has gone wrong.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Get the Cie pointer, which is necessary to properly read the rest of
+ // of the Fde information.
+ fde->cie_offset = GetCieOffsetFromFde32(value32);
+ }
+ uint64_t cur_offset = memory_.cur_offset();
+
+ const DwarfCie* cie = GetCie(fde->cie_offset);
+ if (cie == nullptr) {
+ return false;
+ }
+ fde->cie = cie;
+
+ if (cie->segment_size != 0) {
+ // Skip over the segment selector for now.
+ cur_offset += cie->segment_size;
+ }
+ memory_.set_cur_offset(cur_offset);
+
+ if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_start)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ fde->pc_start = AdjustPcFromFde(fde->pc_start);
+
+ if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_end)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ fde->pc_end += fde->pc_start;
+ if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
+ // Augmentation Size
+ uint64_t aug_length;
+ if (!memory_.ReadULEB128(&aug_length)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ uint64_t cur_offset = memory_.cur_offset();
+
+ if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ // Set our position to after all of the augmentation data.
+ memory_.set_cur_offset(cur_offset + aug_length);
+ }
+ fde->cfa_instructions_offset = memory_.cur_offset();
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
+ dwarf_loc_regs_t* loc_regs) {
+ DwarfCfa<AddressType> cfa(&memory_, fde);
+
+ // Look for the cached copy of the cie data.
+ auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
+ if (reg_entry == cie_loc_regs_.end()) {
+ if (!cfa.GetLocationInfo(pc, fde->cie->cfa_instructions_offset, fde->cie->cfa_instructions_end,
+ loc_regs)) {
+ last_error_ = cfa.last_error();
+ return false;
+ }
+ cie_loc_regs_[fde->cie_offset] = *loc_regs;
+ }
+ cfa.set_cie_loc_regs(&cie_loc_regs_[fde->cie_offset]);
+ if (!cfa.GetLocationInfo(pc, fde->cfa_instructions_offset, fde->cfa_instructions_end, loc_regs)) {
+ last_error_ = cfa.last_error();
+ return false;
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, uint64_t load_bias,
+ const DwarfFde* fde) {
+ DwarfCfa<AddressType> cfa(&memory_, fde);
+
+ // Always print the cie information.
+ const DwarfCie* cie = fde->cie;
+ if (!cfa.Log(indent, pc, load_bias, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
+ last_error_ = cfa.last_error();
+ return false;
+ }
+ if (!cfa.Log(indent, pc, load_bias, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
+ last_error_ = cfa.last_error();
+ return false;
+ }
+ return true;
+}
+
+// Explicitly instantiate DwarfSectionImpl
+template class DwarfSectionImpl<uint32_t>;
+template class DwarfSectionImpl<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 272b5f0..a800c31 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -23,12 +23,17 @@
#define LOG_TAG "unwind"
#include <log/log.h>
-#include "Elf.h"
-#include "ElfInterface.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
#include "ElfInterfaceArm.h"
#include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+#include "Symbols.h"
+
+namespace unwindstack {
bool Elf::Init() {
if (!memory_) {
@@ -49,6 +54,58 @@
return valid_;
}
+// It is expensive to initialize the .gnu_debugdata section. Provide a method
+// to initialize this data separately.
+void Elf::InitGnuDebugdata() {
+ if (!valid_ || interface_->gnu_debugdata_offset() == 0) {
+ return;
+ }
+
+ gnu_debugdata_memory_.reset(interface_->CreateGnuDebugdataMemory());
+ gnu_debugdata_interface_.reset(CreateInterfaceFromMemory(gnu_debugdata_memory_.get()));
+ ElfInterface* gnu = gnu_debugdata_interface_.get();
+ if (gnu == nullptr) {
+ return;
+ }
+ if (gnu->Init()) {
+ gnu->InitHeaders();
+ } else {
+ // Free all of the memory associated with the gnu_debugdata section.
+ gnu_debugdata_memory_.reset(nullptr);
+ gnu_debugdata_interface_.reset(nullptr);
+ }
+}
+
+bool Elf::GetSoname(std::string* name) {
+ return valid_ && interface_->GetSoname(name);
+}
+
+uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {
+ uint64_t load_bias = 0;
+ if (valid()) {
+ load_bias = interface_->load_bias();
+ }
+
+ return pc - map_info->start + load_bias + map_info->elf_offset;
+}
+
+bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
+ return valid_ && (interface_->GetFunctionName(addr, name, func_offset) ||
+ (gnu_debugdata_interface_ &&
+ gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
+}
+
+bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
+ return valid_ && (interface_->Step(rel_pc, regs, process_memory) ||
+ (gnu_debugdata_interface_ &&
+ gnu_debugdata_interface_->Step(rel_pc, regs, process_memory)));
+}
+
+uint64_t Elf::GetLoadBias() {
+ if (!valid_) return 0;
+ return interface_->load_bias();
+}
+
bool Elf::IsValidElf(Memory* memory) {
if (memory == nullptr) {
return false;
@@ -111,3 +168,5 @@
return interface.release();
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 087457c..75abc85 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -20,9 +20,102 @@
#include <memory>
#include <string>
-#include "ElfInterface.h"
-#include "Memory.h"
-#include "Regs.h"
+#include <7zCrc.h>
+#include <Xz.h>
+#include <XzCrc64.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEhFrame.h"
+#include "Symbols.h"
+
+namespace unwindstack {
+
+ElfInterface::~ElfInterface() {
+ for (auto symbol : symbols_) {
+ delete symbol;
+ }
+}
+
+Memory* ElfInterface::CreateGnuDebugdataMemory() {
+ if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
+ return nullptr;
+ }
+
+ // TODO: Only call these initialization functions once.
+ CrcGenerateTable();
+ Crc64GenerateTable();
+
+ std::vector<uint8_t> src(gnu_debugdata_size_);
+ if (!memory_->Read(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) {
+ gnu_debugdata_offset_ = 0;
+ gnu_debugdata_size_ = static_cast<uint64_t>(-1);
+ return nullptr;
+ }
+
+ ISzAlloc alloc;
+ CXzUnpacker state;
+ alloc.Alloc = [](void*, size_t size) { return malloc(size); };
+ alloc.Free = [](void*, void* ptr) { return free(ptr); };
+
+ XzUnpacker_Construct(&state, &alloc);
+
+ std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer);
+ int return_val;
+ size_t src_offset = 0;
+ size_t dst_offset = 0;
+ ECoderStatus status;
+ dst->Resize(5 * gnu_debugdata_size_);
+ do {
+ size_t src_remaining = src.size() - src_offset;
+ size_t dst_remaining = dst->Size() - dst_offset;
+ if (dst_remaining < 2 * gnu_debugdata_size_) {
+ dst->Resize(dst->Size() + 2 * gnu_debugdata_size_);
+ dst_remaining += 2 * gnu_debugdata_size_;
+ }
+ return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset],
+ &src_remaining, CODER_FINISH_ANY, &status);
+ src_offset += src_remaining;
+ dst_offset += dst_remaining;
+ } while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED);
+ XzUnpacker_Free(&state);
+ if (return_val != SZ_OK || !XzUnpacker_IsStreamWasFinished(&state)) {
+ gnu_debugdata_offset_ = 0;
+ gnu_debugdata_size_ = static_cast<uint64_t>(-1);
+ return nullptr;
+ }
+
+ // Shrink back down to the exact size.
+ dst->Resize(dst_offset);
+
+ return dst.release();
+}
+
+template <typename AddressType>
+void ElfInterface::InitHeadersWithTemplate() {
+ if (eh_frame_offset_ != 0) {
+ eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
+ if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) {
+ eh_frame_.reset(nullptr);
+ eh_frame_offset_ = 0;
+ eh_frame_size_ = static_cast<uint64_t>(-1);
+ }
+ }
+
+ if (debug_frame_offset_ != 0) {
+ debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
+ if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_)) {
+ debug_frame_.reset(nullptr);
+ debug_frame_offset_ = 0;
+ debug_frame_size_ = static_cast<uint64_t>(-1);
+ }
+ }
+}
template <typename EhdrType, typename PhdrType, typename ShdrType>
bool ElfInterface::ReadAllHeaders() {
@@ -34,7 +127,13 @@
if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr)) {
return false;
}
- return ReadSectionHeaders<EhdrType, ShdrType>(ehdr);
+
+ // We could still potentially unwind without the section header
+ // information, so ignore any errors.
+ if (!ReadSectionHeaders<EhdrType, ShdrType>(ehdr)) {
+ log(0, "Malformed section header found, ignoring...");
+ }
+ return true;
}
template <typename EhdrType, typename PhdrType>
@@ -124,12 +223,39 @@
}
// Skip the first header, it's always going to be NULL.
+ offset += ehdr.e_shentsize;
for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
return false;
}
- if (shdr.sh_type == SHT_PROGBITS) {
+ if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
+ if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
+ return false;
+ }
+ // Need to go get the information about the section that contains
+ // the string terminated names.
+ ShdrType str_shdr;
+ if (shdr.sh_link >= ehdr.e_shnum) {
+ return false;
+ }
+ uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
+ if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_type, sizeof(str_shdr.sh_type))) {
+ return false;
+ }
+ if (str_shdr.sh_type != SHT_STRTAB) {
+ return false;
+ }
+ if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_offset,
+ sizeof(str_shdr.sh_offset))) {
+ return false;
+ }
+ if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_size, sizeof(str_shdr.sh_size))) {
+ return false;
+ }
+ symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
+ str_shdr.sh_offset, str_shdr.sh_size));
+ } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) {
// Look for the .debug_frame and .gnu_debugdata.
if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
return false;
@@ -137,18 +263,20 @@
if (shdr.sh_name < sec_size) {
std::string name;
if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
+ uint64_t* offset_ptr = nullptr;
+ uint64_t* size_ptr = nullptr;
if (name == ".debug_frame") {
- if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
- memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
- debug_frame_offset_ = shdr.sh_offset;
- debug_frame_size_ = shdr.sh_size;
- }
+ offset_ptr = &debug_frame_offset_;
+ size_ptr = &debug_frame_size_;
} else if (name == ".gnu_debugdata") {
- if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
- memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
- gnu_debugdata_offset_ = shdr.sh_offset;
- gnu_debugdata_size_ = shdr.sh_size;
- }
+ offset_ptr = &gnu_debugdata_offset_;
+ size_ptr = &gnu_debugdata_size_;
+ }
+ if (offset_ptr != nullptr &&
+ memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+ memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+ *offset_ptr = shdr.sh_offset;
+ *size_ptr = shdr.sh_size;
}
}
}
@@ -205,11 +333,47 @@
return true;
}
-bool ElfInterface::Step(uint64_t, Regs*, Memory*) {
+template <typename SymType>
+bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
+ uint64_t* func_offset) {
+ if (symbols_.empty()) {
+ return false;
+ }
+
+ for (const auto symbol : symbols_) {
+ if (symbol->GetName<SymType>(addr, load_bias_, memory_, name, func_offset)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+ // Need to subtract off the load_bias to get the correct pc.
+ if (pc < load_bias_) {
+ return false;
+ }
+ pc -= load_bias_;
+
+ // Try the eh_frame first.
+ DwarfSection* eh_frame = eh_frame_.get();
+ if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory)) {
+ return true;
+ }
+
+ // Try the debug_frame next.
+ DwarfSection* debug_frame = debug_frame_.get();
+ if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory)) {
+ return true;
+ }
+
return false;
}
// Instantiate all of the needed template functions.
+template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
+template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
+
template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
@@ -221,3 +385,10 @@
template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
+
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
+ uint64_t*);
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
+ uint64_t*);
+
+} // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index bab84cc..66bc51f 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -17,12 +17,14 @@
#include <elf.h>
#include <stdint.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
#include "ArmExidx.h"
-#include "ElfInterface.h"
#include "ElfInterfaceArm.h"
#include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) {
if (start_offset_ == 0 || total_entries_ == 0) {
@@ -127,3 +129,5 @@
}
return false;
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index ece694f..1f4e8cb 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -23,8 +23,10 @@
#include <iterator>
#include <unordered_map>
-#include "ElfInterface.h"
-#include "Memory.h"
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
class ElfInterfaceArm : public ElfInterface32 {
public:
@@ -87,4 +89,6 @@
std::unordered_map<size_t, uint32_t> addrs_;
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
diff --git a/libunwindstack/Log.cpp b/libunwindstack/Log.cpp
index faeb66c..436e23c 100644
--- a/libunwindstack/Log.cpp
+++ b/libunwindstack/Log.cpp
@@ -25,7 +25,9 @@
#include <android-base/stringprintf.h>
-#include "Log.h"
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
static bool g_print_to_stdout = false;
@@ -51,3 +53,5 @@
}
va_end(args);
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Machine.h b/libunwindstack/Machine.h
index 323ce80..1fb9309 100644
--- a/libunwindstack/Machine.h
+++ b/libunwindstack/Machine.h
@@ -19,6 +19,8 @@
#include <stdint.h>
+namespace unwindstack {
+
enum ArmReg : uint16_t {
ARM_REG_R0 = 0,
ARM_REG_R1,
@@ -83,51 +85,57 @@
ARM64_REG_LR = ARM64_REG_R30,
};
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
enum X86Reg : uint16_t {
X86_REG_EAX = 0,
- X86_REG_ECX,
- X86_REG_EDX,
- X86_REG_EBX,
- X86_REG_ESP,
- X86_REG_EBP,
- X86_REG_ESI,
- X86_REG_EDI,
- X86_REG_EIP,
- X86_REG_EFL,
- X86_REG_CS,
- X86_REG_SS,
- X86_REG_DS,
- X86_REG_ES,
- X86_REG_FS,
- X86_REG_GS,
+ X86_REG_ECX = 1,
+ X86_REG_EDX = 2,
+ X86_REG_EBX = 3,
+ X86_REG_ESP = 4,
+ X86_REG_EBP = 5,
+ X86_REG_ESI = 6,
+ X86_REG_EDI = 7,
+ X86_REG_EIP = 8,
+ X86_REG_EFL = 9,
+ X86_REG_CS = 10,
+ X86_REG_SS = 11,
+ X86_REG_DS = 12,
+ X86_REG_ES = 13,
+ X86_REG_FS = 14,
+ X86_REG_GS = 15,
X86_REG_LAST,
X86_REG_SP = X86_REG_ESP,
X86_REG_PC = X86_REG_EIP,
};
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
enum X86_64Reg : uint16_t {
X86_64_REG_RAX = 0,
- X86_64_REG_RDX,
- X86_64_REG_RCX,
- X86_64_REG_RBX,
- X86_64_REG_RSI,
- X86_64_REG_RDI,
- X86_64_REG_RBP,
- X86_64_REG_RSP,
- X86_64_REG_R8,
- X86_64_REG_R9,
- X86_64_REG_R10,
- X86_64_REG_R11,
- X86_64_REG_R12,
- X86_64_REG_R13,
- X86_64_REG_R14,
- X86_64_REG_R15,
- X86_64_REG_RIP,
+ X86_64_REG_RDX = 1,
+ X86_64_REG_RCX = 2,
+ X86_64_REG_RBX = 3,
+ X86_64_REG_RSI = 4,
+ X86_64_REG_RDI = 5,
+ X86_64_REG_RBP = 6,
+ X86_64_REG_RSP = 7,
+ X86_64_REG_R8 = 8,
+ X86_64_REG_R9 = 9,
+ X86_64_REG_R10 = 10,
+ X86_64_REG_R11 = 11,
+ X86_64_REG_R12 = 12,
+ X86_64_REG_R13 = 13,
+ X86_64_REG_R14 = 14,
+ X86_64_REG_R15 = 15,
+ X86_64_REG_RIP = 16,
X86_64_REG_LAST,
X86_64_REG_SP = X86_64_REG_RSP,
X86_64_REG_PC = X86_64_REG_RIP,
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_MACHINE_H
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 051f700..d0e1216 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -20,10 +20,12 @@
#include <memory>
#include <string>
-#include "Elf.h"
-#include "MapInfo.h"
-#include "Maps.h"
-#include "Memory.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
Memory* MapInfo::CreateMemory(pid_t pid) {
if (end <= start) {
@@ -73,14 +75,18 @@
return new MemoryRange(memory, start, end);
}
-Elf* MapInfo::GetElf(pid_t pid, bool) {
+Elf* MapInfo::GetElf(pid_t pid, bool init_gnu_debugdata) {
if (elf) {
return elf;
}
elf = new Elf(CreateMemory(pid));
- elf->Init();
+ if (elf->Init() && init_gnu_debugdata) {
+ elf->InitGnuDebugdata();
+ }
// If the init fails, keep the elf around as an invalid object so we
// don't try to reinit the object.
return elf;
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index b369c43..8a90423 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -29,7 +29,11 @@
#include <string>
#include <vector>
-#include "Maps.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
MapInfo* Maps::Find(uint64_t pc) {
if (maps_.empty()) {
@@ -71,15 +75,17 @@
map_info->flags |= PROT_EXEC;
}
- map_info->name = &line[name_pos];
- size_t length = map_info->name.length() - 1;
- if (map_info->name[length] == '\n') {
- map_info->name.erase(length);
- }
- // Mark a device map in /dev/and not in /dev/ashmem/ specially.
- if (!map_info->name.empty() && map_info->name.substr(0, 5) == "/dev/" &&
- map_info->name.substr(5, 7) != "ashmem/") {
- map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
+ if (line[name_pos] != '\0') {
+ map_info->name = &line[name_pos];
+ size_t length = map_info->name.length() - 1;
+ if (map_info->name[length] == '\n') {
+ map_info->name.erase(length);
+ }
+
+ // Mark a device map in /dev/and not in /dev/ashmem/ specially.
+ if (map_info->name.substr(0, 5) == "/dev/" && map_info->name.substr(5, 7) != "ashmem/") {
+ map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
+ }
}
return true;
@@ -194,3 +200,5 @@
}
return true;
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 9e46509..8c36055 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -28,7 +28,11 @@
#include <android-base/unique_fd.h>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+
+namespace unwindstack {
bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
string->clear();
@@ -245,6 +249,11 @@
return true;
}
+MemoryRange::MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
+ : memory_(memory), begin_(begin), length_(end - begin) {
+ CHECK(end > begin);
+}
+
bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
uint64_t max_read;
if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) {
@@ -253,3 +262,5 @@
// The check above guarantees that addr + begin_ will not overflow.
return memory_->Read(addr + begin_, dst, size);
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index adb6522..aa59e31 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <assert.h>
#include <elf.h>
#include <stdint.h>
#include <sys/ptrace.h>
@@ -22,28 +21,23 @@
#include <vector>
-#include "Elf.h"
-#include "ElfInterface.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "Check.h"
#include "Machine.h"
-#include "MapInfo.h"
-#include "Regs.h"
+#include "Ucontext.h"
#include "User.h"
-template <typename AddressType>
-uint64_t RegsTmpl<AddressType>::GetRelPc(Elf* elf, const MapInfo* map_info) {
- uint64_t load_bias = 0;
- if (elf->valid()) {
- load_bias = elf->interface()->load_bias();
- }
-
- return pc_ - map_info->start + load_bias + map_info->elf_offset;
-}
+namespace unwindstack {
template <typename AddressType>
-bool RegsTmpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
+bool RegsImpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
switch (return_loc_.type) {
case LOCATION_REGISTER:
- assert(return_loc_.value < total_regs_);
+ CHECK(return_loc_.value < total_regs_);
*value = regs_[return_loc_.value];
return true;
case LOCATION_SP_OFFSET:
@@ -59,9 +53,8 @@
}
}
-RegsArm::RegsArm() : RegsTmpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP,
- Location(LOCATION_REGISTER, ARM_REG_LR)) {
-}
+RegsArm::RegsArm()
+ : RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
if (!elf->valid()) {
@@ -89,10 +82,14 @@
return rel_pc - 4;
}
-RegsArm64::RegsArm64() : RegsTmpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP,
- Location(LOCATION_REGISTER, ARM64_REG_LR)) {
+void RegsArm::SetFromRaw() {
+ set_pc(regs_[ARM_REG_PC]);
+ set_sp(regs_[ARM_REG_SP]);
}
+RegsArm64::RegsArm64()
+ : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
+
uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
if (!elf->valid()) {
return rel_pc;
@@ -104,10 +101,14 @@
return rel_pc - 4;
}
-RegsX86::RegsX86() : RegsTmpl<uint32_t>(X86_REG_LAST, X86_REG_SP,
- Location(LOCATION_SP_OFFSET, -4)) {
+void RegsArm64::SetFromRaw() {
+ set_pc(regs_[ARM64_REG_PC]);
+ set_sp(regs_[ARM64_REG_SP]);
}
+RegsX86::RegsX86()
+ : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
+
uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
if (!elf->valid()) {
return rel_pc;
@@ -119,10 +120,14 @@
return rel_pc - 1;
}
-RegsX86_64::RegsX86_64() : RegsTmpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP,
- Location(LOCATION_SP_OFFSET, -8)) {
+void RegsX86::SetFromRaw() {
+ set_pc(regs_[X86_REG_PC]);
+ set_sp(regs_[X86_REG_SP]);
}
+RegsX86_64::RegsX86_64()
+ : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
+
uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
if (!elf->valid()) {
return rel_pc;
@@ -135,15 +140,17 @@
return rel_pc - 1;
}
+void RegsX86_64::SetFromRaw() {
+ set_pc(regs_[X86_64_REG_PC]);
+ set_sp(regs_[X86_64_REG_SP]);
+}
+
static Regs* ReadArm(void* remote_data) {
arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
RegsArm* regs = new RegsArm();
memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t));
-
- regs->set_pc(user->regs[ARM_REG_PC]);
- regs->set_sp(user->regs[ARM_REG_SP]);
-
+ regs->SetFromRaw();
return regs;
}
@@ -152,9 +159,10 @@
RegsArm64* regs = new RegsArm64();
memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t));
- regs->set_pc(user->pc);
- regs->set_sp(user->sp);
-
+ uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+ reg_data[ARM64_REG_PC] = user->pc;
+ reg_data[ARM64_REG_SP] = user->sp;
+ regs->SetFromRaw();
return regs;
}
@@ -172,9 +180,7 @@
(*regs)[X86_REG_ESP] = user->esp;
(*regs)[X86_REG_EIP] = user->eip;
- regs->set_pc(user->eip);
- regs->set_sp(user->esp);
-
+ regs->SetFromRaw();
return regs;
}
@@ -200,9 +206,7 @@
(*regs)[X86_64_REG_RSP] = user->rsp;
(*regs)[X86_64_REG_RIP] = user->rip;
- regs->set_pc(user->rip);
- regs->set_sp(user->rsp);
-
+ regs->SetFromRaw();
return regs;
}
@@ -235,3 +239,113 @@
}
return nullptr;
}
+
+static Regs* CreateFromArmUcontext(void* ucontext) {
+ arm_ucontext_t* arm_ucontext = reinterpret_cast<arm_ucontext_t*>(ucontext);
+
+ RegsArm* regs = new RegsArm();
+ memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t));
+ regs->SetFromRaw();
+ return regs;
+}
+
+static Regs* CreateFromArm64Ucontext(void* ucontext) {
+ arm64_ucontext_t* arm64_ucontext = reinterpret_cast<arm64_ucontext_t*>(ucontext);
+
+ RegsArm64* regs = new RegsArm64();
+ memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t));
+ regs->SetFromRaw();
+ return regs;
+}
+
+static Regs* CreateFromX86Ucontext(void* ucontext) {
+ x86_ucontext_t* x86_ucontext = reinterpret_cast<x86_ucontext_t*>(ucontext);
+
+ RegsX86* regs = new RegsX86();
+ // Put the registers in the expected order.
+ (*regs)[X86_REG_GS] = x86_ucontext->uc_mcontext.gs;
+ (*regs)[X86_REG_FS] = x86_ucontext->uc_mcontext.fs;
+ (*regs)[X86_REG_ES] = x86_ucontext->uc_mcontext.es;
+ (*regs)[X86_REG_DS] = x86_ucontext->uc_mcontext.ds;
+ (*regs)[X86_REG_EDI] = x86_ucontext->uc_mcontext.edi;
+ (*regs)[X86_REG_ESI] = x86_ucontext->uc_mcontext.esi;
+ (*regs)[X86_REG_EBP] = x86_ucontext->uc_mcontext.ebp;
+ (*regs)[X86_REG_ESP] = x86_ucontext->uc_mcontext.esp;
+ (*regs)[X86_REG_EBX] = x86_ucontext->uc_mcontext.ebx;
+ (*regs)[X86_REG_EDX] = x86_ucontext->uc_mcontext.edx;
+ (*regs)[X86_REG_ECX] = x86_ucontext->uc_mcontext.ecx;
+ (*regs)[X86_REG_EAX] = x86_ucontext->uc_mcontext.eax;
+ (*regs)[X86_REG_EIP] = x86_ucontext->uc_mcontext.eip;
+ regs->SetFromRaw();
+ return regs;
+}
+
+static Regs* CreateFromX86_64Ucontext(void* ucontext) {
+ x86_64_ucontext_t* x86_64_ucontext = reinterpret_cast<x86_64_ucontext_t*>(ucontext);
+
+ RegsX86_64* regs = new RegsX86_64();
+ // Put the registers in the expected order.
+
+ // R8-R15
+ memcpy(&(*regs)[X86_64_REG_R8], &x86_64_ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
+
+ // Rest of the registers.
+ (*regs)[X86_64_REG_RDI] = x86_64_ucontext->uc_mcontext.rdi;
+ (*regs)[X86_64_REG_RSI] = x86_64_ucontext->uc_mcontext.rsi;
+ (*regs)[X86_64_REG_RBP] = x86_64_ucontext->uc_mcontext.rbp;
+ (*regs)[X86_64_REG_RBX] = x86_64_ucontext->uc_mcontext.rbx;
+ (*regs)[X86_64_REG_RDX] = x86_64_ucontext->uc_mcontext.rdx;
+ (*regs)[X86_64_REG_RAX] = x86_64_ucontext->uc_mcontext.rax;
+ (*regs)[X86_64_REG_RCX] = x86_64_ucontext->uc_mcontext.rcx;
+ (*regs)[X86_64_REG_RSP] = x86_64_ucontext->uc_mcontext.rsp;
+ (*regs)[X86_64_REG_RIP] = x86_64_ucontext->uc_mcontext.rip;
+
+ regs->SetFromRaw();
+ return regs;
+}
+
+Regs* Regs::CreateFromUcontext(uint32_t machine_type, void* ucontext) {
+ switch (machine_type) {
+ case EM_386:
+ return CreateFromX86Ucontext(ucontext);
+ case EM_X86_64:
+ return CreateFromX86_64Ucontext(ucontext);
+ case EM_ARM:
+ return CreateFromArmUcontext(ucontext);
+ case EM_AARCH64:
+ return CreateFromArm64Ucontext(ucontext);
+ }
+ return nullptr;
+}
+
+uint32_t Regs::GetMachineType() {
+#if defined(__arm__)
+ return EM_ARM;
+#elif defined(__aarch64__)
+ return EM_AARCH64;
+#elif defined(__i386__)
+ return EM_386;
+#elif defined(__x86_64__)
+ return EM_X86_64;
+#else
+ abort();
+#endif
+}
+
+Regs* Regs::CreateFromLocal() {
+ Regs* regs;
+#if defined(__arm__)
+ regs = new RegsArm();
+#elif defined(__aarch64__)
+ regs = new RegsArm64();
+#elif defined(__i386__)
+ regs = new RegsX86();
+#elif defined(__x86_64__)
+ regs = new RegsX86_64();
+#else
+ abort();
+#endif
+ return regs;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index 86c1233..42d816a 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -14,15 +14,18 @@
* limitations under the License.
*/
-#include <assert.h>
#include <elf.h>
#include <stdint.h>
#include <string>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
#include "Symbols.h"
+namespace unwindstack {
+
Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
uint64_t str_size)
: cur_offset_(offset),
@@ -58,7 +61,7 @@
if (symbols_.size() != 0) {
const Info* info = GetInfoFromCache(addr);
if (info) {
- assert(addr >= info->start_offset && addr <= info->end_offset);
+ CHECK(addr >= info->start_offset && addr <= info->end_offset);
*func_offset = addr - info->start_offset;
return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset);
}
@@ -108,3 +111,5 @@
// Instantiate all of the needed template functions.
template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
+
+} // namespace unwindstack
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 3c0d033..689144b 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -22,6 +22,8 @@
#include <string>
#include <vector>
+namespace unwindstack {
+
// Forward declaration.
class Memory;
@@ -61,4 +63,6 @@
std::vector<Info> symbols_;
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/Ucontext.h b/libunwindstack/Ucontext.h
new file mode 100644
index 0000000..410af26
--- /dev/null
+++ b/libunwindstack/Ucontext.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_H
+#define _LIBUNWINDSTACK_UCONTEXT_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+//-------------------------------------------------------------------
+// ARM ucontext structures
+//-------------------------------------------------------------------
+struct arm_stack_t {
+ uint32_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint32_t ss_size; // size_t
+};
+
+struct arm_mcontext_t {
+ uint32_t trap_no; // unsigned long
+ uint32_t error_code; // unsigned long
+ uint32_t oldmask; // unsigned long
+ uint32_t regs[ARM_REG_LAST]; // unsigned long
+ uint32_t cpsr; // unsigned long
+ uint32_t fault_address; // unsigned long
+};
+
+struct arm_ucontext_t {
+ uint32_t uc_flags; // unsigned long
+ uint32_t uc_link; // struct ucontext*
+ arm_stack_t uc_stack;
+ arm_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// ARM64 ucontext structures
+//-------------------------------------------------------------------
+struct arm64_stack_t {
+ uint64_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint64_t ss_size; // size_t
+};
+
+struct arm64_sigset_t {
+ uint64_t sig; // unsigned long
+};
+
+struct arm64_mcontext_t {
+ uint64_t fault_address; // __u64
+ uint64_t regs[ARM64_REG_LAST]; // __u64
+ uint64_t pstate; // __u64
+ // Nothing else is used, so don't define it.
+};
+
+struct arm64_ucontext_t {
+ uint64_t uc_flags; // unsigned long
+ uint64_t uc_link; // struct ucontext*
+ arm64_stack_t uc_stack;
+ arm64_sigset_t uc_sigmask;
+ // The kernel adds extra padding after uc_sigmask to match glibc sigset_t on ARM64.
+ char __padding[128 - sizeof(arm64_sigset_t)];
+ // The full structure requires 16 byte alignment, but our partial structure
+ // doesn't, so force the alignment.
+ arm64_mcontext_t uc_mcontext __attribute__((aligned(16)));
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// X86 ucontext structures
+//-------------------------------------------------------------------
+struct x86_stack_t {
+ uint32_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint32_t ss_size; // size_t
+};
+
+struct x86_mcontext_t {
+ uint32_t gs;
+ uint32_t fs;
+ uint32_t es;
+ uint32_t ds;
+ uint32_t edi;
+ uint32_t esi;
+ uint32_t ebp;
+ uint32_t esp;
+ uint32_t ebx;
+ uint32_t edx;
+ uint32_t ecx;
+ uint32_t eax;
+ uint32_t trapno;
+ uint32_t err;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t efl;
+ uint32_t uesp;
+ uint32_t ss;
+ // Only care about the registers, skip everything else.
+};
+
+struct x86_ucontext_t {
+ uint32_t uc_flags; // unsigned long
+ uint32_t uc_link; // struct ucontext*
+ x86_stack_t uc_stack;
+ x86_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// X86_64 ucontext structures
+//-------------------------------------------------------------------
+struct x86_64_stack_t {
+ uint64_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint64_t ss_size; // size_t
+};
+
+struct x86_64_mcontext_t {
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rdi;
+ uint64_t rsi;
+ uint64_t rbp;
+ uint64_t rbx;
+ uint64_t rdx;
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rsp;
+ uint64_t rip;
+ uint64_t efl;
+ uint64_t csgsfs;
+ uint64_t err;
+ uint64_t trapno;
+ uint64_t oldmask;
+ uint64_t cr2;
+ // Only care about the registers, skip everything else.
+};
+
+typedef struct x86_64_ucontext {
+ uint64_t uc_flags; // unsigned long
+ uint64_t uc_link; // struct ucontext*
+ x86_64_stack_t uc_stack;
+ x86_64_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+} x86_64_ucontext_t;
+//-------------------------------------------------------------------
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_UCONTEXT_H
diff --git a/libunwindstack/User.h b/libunwindstack/User.h
index a695467..53f7e50 100644
--- a/libunwindstack/User.h
+++ b/libunwindstack/User.h
@@ -29,6 +29,8 @@
#ifndef _LIBUNWINDSTACK_USER_H
#define _LIBUNWINDSTACK_USER_H
+namespace unwindstack {
+
struct x86_user_regs {
uint32_t ebx;
uint32_t ecx;
@@ -93,4 +95,6 @@
// The largest user structure.
constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10;
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_USER_H
diff --git a/libunwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
similarity index 95%
rename from libunwindstack/DwarfLocation.h
rename to libunwindstack/include/unwindstack/DwarfLocation.h
index 062d125..3467e6a 100644
--- a/libunwindstack/DwarfLocation.h
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -21,6 +21,8 @@
#include <unordered_map>
+namespace unwindstack {
+
enum DwarfLocationEnum : uint8_t {
DWARF_LOCATION_INVALID = 0,
DWARF_LOCATION_UNDEFINED,
@@ -38,4 +40,6 @@
typedef std::unordered_map<uint16_t, DwarfLocation> dwarf_loc_regs_t;
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/DwarfMemory.h b/libunwindstack/include/unwindstack/DwarfMemory.h
similarity index 97%
rename from libunwindstack/DwarfMemory.h
rename to libunwindstack/include/unwindstack/DwarfMemory.h
index a304dd9..8dd8d2b 100644
--- a/libunwindstack/DwarfMemory.h
+++ b/libunwindstack/include/unwindstack/DwarfMemory.h
@@ -19,6 +19,8 @@
#include <stdint.h>
+namespace unwindstack {
+
// Forward declarations.
class Memory;
@@ -69,4 +71,6 @@
uint64_t text_offset_ = static_cast<uint64_t>(-1);
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
new file mode 100644
index 0000000..a97ca2b
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_SECTION_H
+#define _LIBUNWINDSTACK_DWARF_SECTION_H
+
+#include <stdint.h>
+
+#include <iterator>
+#include <unordered_map>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+enum DwarfError : uint8_t;
+class Memory;
+class Regs;
+
+class DwarfSection {
+ public:
+ DwarfSection(Memory* memory);
+ virtual ~DwarfSection() = default;
+
+ class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> {
+ public:
+ iterator(DwarfSection* section, size_t index) : section_(section), index_(index) {}
+
+ iterator& operator++() {
+ index_++;
+ return *this;
+ }
+ iterator& operator++(int increment) {
+ index_ += increment;
+ return *this;
+ }
+ iterator& operator--() {
+ index_--;
+ return *this;
+ }
+ iterator& operator--(int decrement) {
+ index_ -= decrement;
+ return *this;
+ }
+
+ bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
+ bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
+
+ const DwarfFde* operator*() { return section_->GetFdeFromIndex(index_); }
+
+ private:
+ DwarfSection* section_ = nullptr;
+ size_t index_ = 0;
+ };
+
+ iterator begin() { return iterator(this, 0); }
+ iterator end() { return iterator(this, fde_count_); }
+
+ DwarfError last_error() { return last_error_; }
+
+ virtual bool Init(uint64_t offset, uint64_t size) = 0;
+
+ virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*) = 0;
+
+ virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
+
+ virtual bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) = 0;
+
+ virtual const DwarfFde* GetFdeFromIndex(size_t index) = 0;
+
+ const DwarfFde* GetFdeFromPc(uint64_t pc);
+
+ virtual const DwarfFde* GetFdeFromOffset(uint64_t fde_offset) = 0;
+
+ virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
+
+ virtual bool IsCie32(uint32_t value32) = 0;
+
+ virtual bool IsCie64(uint64_t value64) = 0;
+
+ virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
+
+ virtual uint64_t GetCieOffsetFromFde64(uint64_t pointer) = 0;
+
+ virtual uint64_t AdjustPcFromFde(uint64_t pc) = 0;
+
+ bool Step(uint64_t pc, Regs* regs, Memory* process_memory);
+
+ protected:
+ DwarfMemory memory_;
+ DwarfError last_error_;
+
+ uint64_t fde_count_;
+ std::unordered_map<uint64_t, DwarfFde> fde_entries_;
+ std::unordered_map<uint64_t, DwarfCie> cie_entries_;
+ std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
+};
+
+template <typename AddressType>
+class DwarfSectionImpl : public DwarfSection {
+ public:
+ DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
+ virtual ~DwarfSectionImpl() = default;
+
+ bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
+ Regs* regs) override;
+
+ const DwarfCie* GetCie(uint64_t offset);
+ bool FillInCie(DwarfCie* cie);
+
+ const DwarfFde* GetFdeFromOffset(uint64_t offset) override;
+ bool FillInFde(DwarfFde* fde);
+
+ bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
+
+ bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) override;
+
+ protected:
+ bool EvalExpression(const DwarfLocation& loc, uint8_t version, Memory* regular_memory,
+ AddressType* value);
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_SECTION_H
diff --git a/libunwindstack/DwarfStructs.h b/libunwindstack/include/unwindstack/DwarfStructs.h
similarity index 87%
rename from libunwindstack/DwarfStructs.h
rename to libunwindstack/include/unwindstack/DwarfStructs.h
index 57aac88..4b481f0 100644
--- a/libunwindstack/DwarfStructs.h
+++ b/libunwindstack/include/unwindstack/DwarfStructs.h
@@ -21,12 +21,12 @@
#include <vector>
-#include "DwarfEncoding.h"
+namespace unwindstack {
-struct DwarfCIE {
+struct DwarfCie {
uint8_t version = 0;
- uint8_t fde_address_encoding = DW_EH_PE_absptr;
- uint8_t lsda_encoding = DW_EH_PE_omit;
+ uint8_t fde_address_encoding = 0;
+ uint8_t lsda_encoding = 0;
uint8_t segment_size = 0;
std::vector<char> augmentation_string;
uint64_t personality_handler = 0;
@@ -37,16 +37,18 @@
uint64_t return_address_register = 0;
};
-struct DwarfFDE {
+struct DwarfFde {
uint64_t cie_offset = 0;
uint64_t cfa_instructions_offset = 0;
uint64_t cfa_instructions_end = 0;
uint64_t pc_start = 0;
uint64_t pc_end = 0;
uint64_t lsda_address = 0;
- const DwarfCIE* cie = nullptr;
+ const DwarfCie* cie = nullptr;
};
constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
similarity index 74%
rename from libunwindstack/Elf.h
rename to libunwindstack/include/unwindstack/Elf.h
index 7bf45b8..d89a746 100644
--- a/libunwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -22,14 +22,17 @@
#include <memory>
#include <string>
-#include "ElfInterface.h"
-#include "Memory.h"
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
#if !defined(EM_AARCH64)
#define EM_AARCH64 183
#endif
+namespace unwindstack {
+
// Forward declaration.
+struct MapInfo;
class Regs;
class Elf {
@@ -41,20 +44,18 @@
void InitGnuDebugdata();
- bool GetSoname(std::string* name) {
- return valid_ && interface_->GetSoname(name);
- }
+ bool GetSoname(std::string* name);
- bool GetFunctionName(uint64_t, std::string*, uint64_t*) {
- return false;
- }
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
- bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
- return valid_ && interface_->Step(rel_pc, regs, process_memory);
- }
+ uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
+
+ bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory);
ElfInterface* CreateInterfaceFromMemory(Memory* memory);
+ uint64_t GetLoadBias();
+
bool valid() { return valid_; }
uint32_t machine_type() { return machine_type_; }
@@ -65,6 +66,8 @@
ElfInterface* interface() { return interface_.get(); }
+ ElfInterface* gnu_debugdata_interface() { return gnu_debugdata_interface_.get(); }
+
static bool IsValidElf(Memory* memory);
protected:
@@ -73,6 +76,11 @@
std::unique_ptr<Memory> memory_;
uint32_t machine_type_;
uint8_t class_type_;
+
+ std::unique_ptr<Memory> gnu_debugdata_memory_;
+ std::unique_ptr<ElfInterface> gnu_debugdata_interface_;
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_ELF_H
diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
similarity index 75%
rename from libunwindstack/ElfInterface.h
rename to libunwindstack/include/unwindstack/ElfInterface.h
index 944146c..5cac0d3 100644
--- a/libunwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -25,9 +25,14 @@
#include <unordered_map>
#include <vector>
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
// Forward declarations.
class Memory;
class Regs;
+class Symbols;
struct LoadInfo {
uint64_t offset;
@@ -44,7 +49,7 @@
class ElfInterface {
public:
ElfInterface(Memory* memory) : memory_(memory) {}
- virtual ~ElfInterface() = default;
+ virtual ~ElfInterface();
virtual bool Init() = 0;
@@ -68,10 +73,18 @@
uint64_t dynamic_size() { return dynamic_size_; }
uint64_t eh_frame_offset() { return eh_frame_offset_; }
uint64_t eh_frame_size() { return eh_frame_size_; }
+ uint64_t debug_frame_offset() { return debug_frame_offset_; }
+ uint64_t debug_frame_size() { return debug_frame_size_; }
uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; }
uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; }
+ DwarfSection* eh_frame() { return eh_frame_.get(); }
+ DwarfSection* debug_frame() { return debug_frame_.get(); }
+
protected:
+ template <typename AddressType>
+ void InitHeadersWithTemplate();
+
template <typename EhdrType, typename PhdrType, typename ShdrType>
bool ReadAllHeaders();
@@ -84,6 +97,9 @@
template <typename DynType>
bool GetSonameWithTemplate(std::string* soname);
+ template <typename SymType>
+ bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
+
virtual bool HandleType(uint64_t, uint32_t) { return false; }
Memory* memory_;
@@ -105,6 +121,11 @@
uint8_t soname_type_ = SONAME_UNKNOWN;
std::string soname_;
+
+ std::unique_ptr<DwarfSection> eh_frame_;
+ std::unique_ptr<DwarfSection> debug_frame_;
+
+ std::vector<Symbols*> symbols_;
};
class ElfInterface32 : public ElfInterface {
@@ -116,15 +137,14 @@
return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
}
- void InitHeaders() override {
- }
+ void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
bool GetSoname(std::string* soname) override {
return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
}
- bool GetFunctionName(uint64_t, std::string*, uint64_t*) override {
- return false;
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+ return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
}
};
@@ -137,16 +157,17 @@
return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
}
- void InitHeaders() override {
- }
+ void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
bool GetSoname(std::string* soname) override {
return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
}
- bool GetFunctionName(uint64_t, std::string*, uint64_t*) override {
- return false;
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+ return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
}
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_ELF_INTERFACE_H
diff --git a/libunwindstack/Log.h b/libunwindstack/include/unwindstack/Log.h
similarity index 93%
rename from libunwindstack/Log.h
rename to libunwindstack/include/unwindstack/Log.h
index 2d01aa8..aa1219c 100644
--- a/libunwindstack/Log.h
+++ b/libunwindstack/include/unwindstack/Log.h
@@ -19,7 +19,11 @@
#include <stdint.h>
+namespace unwindstack {
+
void log_to_stdout(bool enable);
void log(uint8_t indent, const char* format, ...);
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_LOG_H
diff --git a/libunwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
similarity index 95%
rename from libunwindstack/MapInfo.h
rename to libunwindstack/include/unwindstack/MapInfo.h
index 79a2ada..2a97dde 100644
--- a/libunwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -21,6 +21,8 @@
#include <string>
+namespace unwindstack {
+
// Forward declarations.
class Elf;
class Memory;
@@ -42,4 +44,6 @@
Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
similarity index 96%
rename from libunwindstack/Maps.h
rename to libunwindstack/include/unwindstack/Maps.h
index 239b64a..0b02739 100644
--- a/libunwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -23,8 +23,9 @@
#include <string>
#include <vector>
-#include "Elf.h"
-#include "MapInfo.h"
+#include <unwindstack/MapInfo.h>
+
+namespace unwindstack {
// Special flag to indicate a map is in /dev/. However, a map in
// /dev/ashmem/... does not set this flag.
@@ -104,4 +105,6 @@
bool Parse() override;
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
similarity index 89%
rename from libunwindstack/Memory.h
rename to libunwindstack/include/unwindstack/Memory.h
index f9f6d56..0c05266 100644
--- a/libunwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -17,7 +17,6 @@
#ifndef _LIBUNWINDSTACK_MEMORY_H
#define _LIBUNWINDSTACK_MEMORY_H
-#include <assert.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
@@ -25,6 +24,8 @@
#include <string>
#include <vector>
+namespace unwindstack {
+
class Memory {
public:
Memory() = default;
@@ -46,13 +47,9 @@
return Read(offset, field, size);
}
- inline bool Read32(uint64_t addr, uint32_t* dst) {
- return Read(addr, dst, sizeof(uint32_t));
- }
+ inline bool Read32(uint64_t addr, uint32_t* dst) { return Read(addr, dst, sizeof(uint32_t)); }
- inline bool Read64(uint64_t addr, uint64_t* dst) {
- return Read(addr, dst, sizeof(uint64_t));
- }
+ inline bool Read64(uint64_t addr, uint64_t* dst) { return Read(addr, dst, sizeof(uint64_t)); }
};
class MemoryBuffer : public Memory {
@@ -128,10 +125,7 @@
class MemoryRange : public Memory {
public:
- MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
- : memory_(memory), begin_(begin), length_(end - begin) {
- assert(end > begin);
- }
+ MemoryRange(Memory* memory, uint64_t begin, uint64_t end);
virtual ~MemoryRange() { delete memory_; }
bool Read(uint64_t addr, void* dst, size_t size) override;
@@ -142,4 +136,6 @@
uint64_t length_;
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
similarity index 80%
rename from libunwindstack/Regs.h
rename to libunwindstack/include/unwindstack/Regs.h
index 718fc85..ab98f32 100644
--- a/libunwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -21,9 +21,12 @@
#include <vector>
+namespace unwindstack {
+
// Forward declarations.
class Elf;
struct MapInfo;
+class Memory;
class Regs {
public:
@@ -50,14 +53,17 @@
virtual bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) = 0;
- virtual uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) = 0;
-
virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
+ virtual void SetFromRaw() = 0;
+
uint16_t sp_reg() { return sp_reg_; }
uint16_t total_regs() { return total_regs_; }
+ static uint32_t GetMachineType();
static Regs* RemoteGet(pid_t pid, uint32_t* machine_type);
+ static Regs* CreateFromUcontext(uint32_t machine_type, void* ucontext);
+ static Regs* CreateFromLocal();
protected:
uint16_t total_regs_;
@@ -66,13 +72,11 @@
};
template <typename AddressType>
-class RegsTmpl : public Regs {
+class RegsImpl : public Regs {
public:
- RegsTmpl(uint16_t total_regs, uint16_t sp_reg, Location return_loc)
+ RegsImpl(uint16_t total_regs, uint16_t sp_reg, Location return_loc)
: Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {}
- virtual ~RegsTmpl() = default;
-
- uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) override;
+ virtual ~RegsImpl() = default;
bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) override;
@@ -92,36 +96,46 @@
std::vector<AddressType> regs_;
};
-class RegsArm : public RegsTmpl<uint32_t> {
+class RegsArm : public RegsImpl<uint32_t> {
public:
RegsArm();
virtual ~RegsArm() = default;
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
};
-class RegsArm64 : public RegsTmpl<uint64_t> {
+class RegsArm64 : public RegsImpl<uint64_t> {
public:
RegsArm64();
virtual ~RegsArm64() = default;
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
};
-class RegsX86 : public RegsTmpl<uint32_t> {
+class RegsX86 : public RegsImpl<uint32_t> {
public:
RegsX86();
virtual ~RegsX86() = default;
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
};
-class RegsX86_64 : public RegsTmpl<uint64_t> {
+class RegsX86_64 : public RegsImpl<uint64_t> {
public:
RegsX86_64();
virtual ~RegsX86_64() = default;
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
new file mode 100644
index 0000000..ffec213
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+#define _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+
+namespace unwindstack {
+
+#if defined(__arm__)
+
+inline void RegsGetLocal(Regs* regs) {
+ void* reg_data = regs->RawData();
+ asm volatile(
+ ".align 2\n"
+ "bx pc\n"
+ "nop\n"
+ ".code 32\n"
+ "stmia %[base], {r0-r12}\n"
+ "add %[base], #52\n"
+ "mov r1, r13\n"
+ "mov r2, r14\n"
+ "mov r3, r15\n"
+ "stmia %[base], {r1-r3}\n"
+ "orr %[base], pc, #1\n"
+ "bx %[base]\n"
+ : [base] "+r"(reg_data)
+ :
+ : "memory");
+
+ regs->SetFromRaw();
+}
+
+#elif defined(__aarch64__)
+
+inline void RegsGetLocal(Regs* regs) {
+ void* reg_data = regs->RawData();
+ asm volatile(
+ "1:\n"
+ "stp x0, x1, [%[base], #0]\n"
+ "stp x2, x3, [%[base], #16]\n"
+ "stp x4, x5, [%[base], #32]\n"
+ "stp x6, x7, [%[base], #48]\n"
+ "stp x8, x9, [%[base], #64]\n"
+ "stp x10, x11, [%[base], #80]\n"
+ "stp x12, x13, [%[base], #96]\n"
+ "stp x14, x15, [%[base], #112]\n"
+ "stp x16, x17, [%[base], #128]\n"
+ "stp x18, x19, [%[base], #144]\n"
+ "stp x20, x21, [%[base], #160]\n"
+ "stp x22, x23, [%[base], #176]\n"
+ "stp x24, x25, [%[base], #192]\n"
+ "stp x26, x27, [%[base], #208]\n"
+ "stp x28, x29, [%[base], #224]\n"
+ "str x30, [%[base], #240]\n"
+ "mov x12, sp\n"
+ "adr x13, 1b\n"
+ "stp x12, x13, [%[base], #248]\n"
+ : [base] "+r"(reg_data)
+ :
+ : "x12", "x13", "memory");
+
+ regs->SetFromRaw();
+}
+
+#elif defined(__i386__) || defined(__x86_64__)
+
+extern "C" void AsmGetRegs(void* regs);
+
+inline void RegsGetLocal(Regs* regs) {
+ AsmGetRegs(regs->RawData());
+
+ regs->SetFromRaw();
+}
+
+#endif
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_REGS_GET_LOCAL_H
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 4fff48e..94cb493 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -23,13 +23,16 @@
#include <gtest/gtest.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Regs.h>
+
#include "ArmExidx.h"
-#include "Regs.h"
-#include "Log.h"
#include "LogFake.h"
#include "MemoryFake.h"
+namespace unwindstack {
+
class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
protected:
void Init(Memory* process_memory = nullptr) {
@@ -1092,3 +1095,5 @@
}
INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging"));
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
index aed75bf..caad131 100644
--- a/libunwindstack/tests/ArmExidxExtractTest.cpp
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -21,12 +21,15 @@
#include <gtest/gtest.h>
+#include <unwindstack/Log.h>
+
#include "ArmExidx.h"
-#include "Log.h"
#include "LogFake.h"
#include "MemoryFake.h"
+namespace unwindstack {
+
class ArmExidxExtractTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -329,3 +332,5 @@
ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
ASSERT_EQ("4 unwind Raw Data: 0x11 0x22 0x33 0xb0\n", GetFakeLogPrint());
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index 3185bc3..b17ca33 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -23,15 +23,18 @@
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+
#include "DwarfCfa.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
-#include "Log.h"
#include "LogFake.h"
#include "MemoryFake.h"
+namespace unwindstack {
+
template <typename TypeParam>
class DwarfCfaLogTest : public ::testing::Test {
protected:
@@ -60,8 +63,8 @@
MemoryFake memory_;
std::unique_ptr<DwarfMemory> dmem_;
std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
- DwarfCIE cie_;
- DwarfFDE fde_;
+ DwarfCie cie_;
+ DwarfFde fde_;
};
TYPED_TEST_CASE_P(DwarfCfaLogTest);
@@ -812,3 +815,5 @@
typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 6cf028a..73a67ac 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -21,15 +21,19 @@
#include <gtest/gtest.h>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+
#include "DwarfCfa.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
-#include "Log.h"
+#include "DwarfError.h"
#include "LogFake.h"
#include "MemoryFake.h"
+namespace unwindstack {
+
template <typename TypeParam>
class DwarfCfaTest : public ::testing::Test {
protected:
@@ -57,8 +61,8 @@
MemoryFake memory_;
std::unique_ptr<DwarfMemory> dmem_;
std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
- DwarfCIE cie_;
- DwarfFDE fde_;
+ DwarfCie cie_;
+ DwarfFde fde_;
};
TYPED_TEST_CASE_P(DwarfCfaTest);
@@ -957,3 +961,5 @@
typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
new file mode 100644
index 0000000..69813e5
--- /dev/null
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class MockDwarfDebugFrame : public DwarfDebugFrame<TypeParam> {
+ public:
+ MockDwarfDebugFrame(Memory* memory) : DwarfDebugFrame<TypeParam>(memory) {}
+ ~MockDwarfDebugFrame() = default;
+
+ void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
+ void TestSetOffset(uint64_t offset) { this->offset_ = offset; }
+ void TestSetEndOffset(uint64_t offset) { this->end_offset_ = offset; }
+ void TestPushFdeInfo(const typename DwarfDebugFrame<TypeParam>::FdeInfo& info) {
+ this->fdes_.push_back(info);
+ }
+
+ uint64_t TestGetFdeCount() { return this->fde_count_; }
+ uint8_t TestGetOffset() { return this->offset_; }
+ uint8_t TestGetEndOffset() { return this->end_offset_; }
+ void TestGetFdeInfo(size_t index, typename DwarfDebugFrame<TypeParam>::FdeInfo* info) {
+ *info = this->fdes_[index];
+ }
+};
+
+template <typename TypeParam>
+class DwarfDebugFrameTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ debug_frame_ = new MockDwarfDebugFrame<TypeParam>(&memory_);
+ ResetLogs();
+ }
+
+ void TearDown() override { delete debug_frame_; }
+
+ MemoryFake memory_;
+ MockDwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfDebugFrameTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init32) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 1);
+ this->memory_.SetData8(0x5009, '\0');
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0);
+ this->memory_.SetData32(0x5108, 0x1500);
+ this->memory_.SetData32(0x510c, 0x200);
+
+ this->memory_.SetData32(0x5200, 0xfc);
+ this->memory_.SetData32(0x5204, 0);
+ this->memory_.SetData32(0x5208, 0x2500);
+ this->memory_.SetData32(0x520c, 0x300);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0x5300, 0xfc);
+ this->memory_.SetData32(0x5304, 0xffffffff);
+ this->memory_.SetData8(0x5308, 1);
+ this->memory_.SetData8(0x5309, '\0');
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5400, 0xfc);
+ this->memory_.SetData32(0x5404, 0x300);
+ this->memory_.SetData32(0x5408, 0x3500);
+ this->memory_.SetData32(0x540c, 0x400);
+
+ this->memory_.SetData32(0x5500, 0xfc);
+ this->memory_.SetData32(0x5504, 0x300);
+ this->memory_.SetData32(0x5508, 0x4500);
+ this->memory_.SetData32(0x550c, 0x500);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x1500U, info.start);
+ EXPECT_EQ(0x1700U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(1, &info);
+ EXPECT_EQ(0x5200U, info.offset);
+ EXPECT_EQ(0x2500U, info.start);
+ EXPECT_EQ(0x2800U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(2, &info);
+ EXPECT_EQ(0x5400U, info.offset);
+ EXPECT_EQ(0x3500U, info.start);
+ EXPECT_EQ(0x3900U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(3, &info);
+ EXPECT_EQ(0x5500U, info.offset);
+ EXPECT_EQ(0x4500U, info.start);
+ EXPECT_EQ(0x4a00U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init32_fde_not_following_cie) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 1);
+ this->memory_.SetData8(0x5009, '\0');
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0x1000);
+ this->memory_.SetData32(0x5108, 0x1500);
+ this->memory_.SetData32(0x510c, 0x200);
+
+ ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init64) {
+ // CIE 64 information.
+ this->memory_.SetData32(0x5000, 0xffffffff);
+ this->memory_.SetData64(0x5004, 0xf4);
+ this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
+ this->memory_.SetData8(0x5014, 1);
+ this->memory_.SetData8(0x5015, '\0');
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x5100, 0xffffffff);
+ this->memory_.SetData64(0x5104, 0xf4);
+ this->memory_.SetData64(0x510c, 0);
+ this->memory_.SetData64(0x5114, 0x1500);
+ this->memory_.SetData64(0x511c, 0x200);
+
+ this->memory_.SetData32(0x5200, 0xffffffff);
+ this->memory_.SetData64(0x5204, 0xf4);
+ this->memory_.SetData64(0x520c, 0);
+ this->memory_.SetData64(0x5214, 0x2500);
+ this->memory_.SetData64(0x521c, 0x300);
+
+ // CIE 64 information.
+ this->memory_.SetData32(0x5300, 0xffffffff);
+ this->memory_.SetData64(0x5304, 0xf4);
+ this->memory_.SetData64(0x530c, 0xffffffffffffffffULL);
+ this->memory_.SetData8(0x5314, 1);
+ this->memory_.SetData8(0x5315, '\0');
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x5400, 0xffffffff);
+ this->memory_.SetData64(0x5404, 0xf4);
+ this->memory_.SetData64(0x540c, 0x300);
+ this->memory_.SetData64(0x5414, 0x3500);
+ this->memory_.SetData64(0x541c, 0x400);
+
+ this->memory_.SetData32(0x5500, 0xffffffff);
+ this->memory_.SetData64(0x5504, 0xf4);
+ this->memory_.SetData64(0x550c, 0x300);
+ this->memory_.SetData64(0x5514, 0x4500);
+ this->memory_.SetData64(0x551c, 0x500);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x1500U, info.start);
+ EXPECT_EQ(0x1700U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(1, &info);
+ EXPECT_EQ(0x5200U, info.offset);
+ EXPECT_EQ(0x2500U, info.start);
+ EXPECT_EQ(0x2800U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(2, &info);
+ EXPECT_EQ(0x5400U, info.offset);
+ EXPECT_EQ(0x3500U, info.start);
+ EXPECT_EQ(0x3900U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(3, &info);
+ EXPECT_EQ(0x5500U, info.offset);
+ EXPECT_EQ(0x4500U, info.start);
+ EXPECT_EQ(0x4a00U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init64_fde_not_following_cie) {
+ // CIE 64 information.
+ this->memory_.SetData32(0x5000, 0xffffffff);
+ this->memory_.SetData64(0x5004, 0xf4);
+ this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
+ this->memory_.SetData8(0x5014, 1);
+ this->memory_.SetData8(0x5015, '\0');
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x5100, 0xffffffff);
+ this->memory_.SetData64(0x5104, 0xf4);
+ this->memory_.SetData64(0x510c, 0x1000);
+ this->memory_.SetData64(0x5114, 0x1500);
+ this->memory_.SetData64(0x511c, 0x200);
+
+ ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 1);
+ // Augment string.
+ this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'R', 'P', 'L', '\0'});
+ // Code alignment factor.
+ this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x80, 0x00});
+ // Data alignment factor.
+ this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+ // Return address register
+ this->memory_.SetData8(0x5014, 0x84);
+ // Augmentation length
+ this->memory_.SetMemory(0x5015, std::vector<uint8_t>{0x84, 0x00});
+ // R data.
+ this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0);
+ this->memory_.SetData16(0x5108, 0x1500);
+ this->memory_.SetData16(0x510a, 0x200);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+ ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x1500U, info.start);
+ EXPECT_EQ(0x1700U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init_version4) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 4);
+ // Augment string.
+ this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
+ // Address size.
+ this->memory_.SetData8(0x500e, 4);
+ // Segment size.
+ this->memory_.SetData8(0x500f, 0);
+ // Code alignment factor.
+ this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x80, 0x00});
+ // Data alignment factor.
+ this->memory_.SetMemory(0x5012, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+ // Return address register
+ this->memory_.SetMemory(0x5016, std::vector<uint8_t>{0x85, 0x10});
+ // Augmentation length
+ this->memory_.SetMemory(0x5018, std::vector<uint8_t>{0x84, 0x00});
+ // L data.
+ this->memory_.SetData8(0x501a, 0x10);
+ // P data.
+ this->memory_.SetData8(0x501b, DW_EH_PE_udata4);
+ this->memory_.SetData32(0x501c, 0x100);
+ // R data.
+ this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0);
+ this->memory_.SetData16(0x5108, 0x1500);
+ this->memory_.SetData16(0x510a, 0x200);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+ ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x1500U, info.start);
+ EXPECT_EQ(0x1700U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeOffsetFromPc) {
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+ for (size_t i = 0; i < 9; i++) {
+ info.start = 0x1000 * (i + 1);
+ info.end = 0x1000 * (i + 2) - 0x10;
+ info.offset = 0x5000 + i * 0x20;
+ this->debug_frame_->TestPushFdeInfo(info);
+ }
+
+ this->debug_frame_->TestSetFdeCount(0);
+ uint64_t fde_offset;
+ ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+
+ this->debug_frame_->TestSetFdeCount(9);
+ ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+ // Odd number of elements.
+ for (size_t i = 0; i < 9; i++) {
+ TypeParam pc = 0x1000 * (i + 1);
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
+ << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
+ << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+ << "Failed at index " << i;
+ ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+ }
+
+ // Even number of elements.
+ this->debug_frame_->TestSetFdeCount(10);
+ info.start = 0xa000;
+ info.end = 0xaff0;
+ info.offset = 0x5120;
+ this->debug_frame_->TestPushFdeInfo(info);
+
+ for (size_t i = 0; i < 10; i++) {
+ TypeParam pc = 0x1000 * (i + 1);
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
+ << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
+ << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+ << "Failed at index " << i;
+ ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+ }
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde32) {
+ this->debug_frame_->TestSetOffset(0x4000);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0xf000, 0x100);
+ this->memory_.SetData32(0xf004, 0xffffffff);
+ this->memory_.SetData8(0xf008, 0x1);
+ this->memory_.SetData8(0xf009, '\0');
+ this->memory_.SetData8(0xf00a, 4);
+ this->memory_.SetData8(0xf00b, 8);
+ this->memory_.SetData8(0xf00c, 0x20);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x14000, 0x20);
+ this->memory_.SetData32(0x14004, 0xb000);
+ this->memory_.SetData32(0x14008, 0x9000);
+ this->memory_.SetData32(0x1400c, 0x100);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x14000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x9000U, fde->pc_start);
+ EXPECT_EQ(0x9100U, fde->pc_end);
+ EXPECT_EQ(0xf000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde64) {
+ this->debug_frame_->TestSetOffset(0x2000);
+
+ // CIE 64 information.
+ this->memory_.SetData32(0x6000, 0xffffffff);
+ this->memory_.SetData64(0x6004, 0x100);
+ this->memory_.SetData64(0x600c, 0xffffffffffffffffULL);
+ this->memory_.SetData8(0x6014, 0x1);
+ this->memory_.SetData8(0x6015, '\0');
+ this->memory_.SetData8(0x6016, 4);
+ this->memory_.SetData8(0x6017, 8);
+ this->memory_.SetData8(0x6018, 0x20);
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x8000, 0xffffffff);
+ this->memory_.SetData64(0x8004, 0x200);
+ this->memory_.SetData64(0x800c, 0x4000);
+ this->memory_.SetData64(0x8014, 0x5000);
+ this->memory_.SetData64(0x801c, 0x300);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x8000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+ EXPECT_EQ(0x5000U, fde->pc_start);
+ EXPECT_EQ(0x5300U, fde->pc_end);
+ EXPECT_EQ(0x6000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie, Init64,
+ Init64_fde_not_following_cie, Init_version1, Init_version4,
+ GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
new file mode 100644
index 0000000..e9501e3
--- /dev/null
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DwarfEhFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class MockDwarfEhFrame : public DwarfEhFrame<TypeParam> {
+ public:
+ MockDwarfEhFrame(Memory* memory) : DwarfEhFrame<TypeParam>(memory) {}
+ ~MockDwarfEhFrame() = default;
+
+ void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
+ void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
+ void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; }
+ void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; }
+ void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; }
+ void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; }
+
+ void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
+ void TestSetFdeInfo(uint64_t index, const typename DwarfEhFrame<TypeParam>::FdeInfo& info) {
+ this->fde_info_[index] = info;
+ }
+
+ uint8_t TestGetVersion() { return this->version_; }
+ uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; }
+ uint64_t TestGetPtrOffset() { return this->ptr_offset_; }
+ uint8_t TestGetTableEncoding() { return this->table_encoding_; }
+ uint64_t TestGetTableEntrySize() { return this->table_entry_size_; }
+ uint64_t TestGetFdeCount() { return this->fde_count_; }
+ uint64_t TestGetEntriesOffset() { return this->entries_offset_; }
+ uint64_t TestGetEntriesEnd() { return this->entries_end_; }
+ uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; }
+ uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; }
+};
+
+template <typename TypeParam>
+class DwarfEhFrameTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ eh_frame_ = new MockDwarfEhFrame<TypeParam>(&memory_);
+ ResetLogs();
+ }
+
+ void TearDown() override { delete eh_frame_; }
+
+ MemoryFake memory_;
+ MockDwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfEhFrameTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfEhFrameTest, Init) {
+ this->memory_.SetMemory(
+ 0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
+ this->memory_.SetData16(0x1004, 0x500);
+ this->memory_.SetData32(0x1006, 126);
+
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100));
+ EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+ EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
+ EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
+ EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+ EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount());
+ EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
+ EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
+ EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+
+ // Verify an unexpected version will cause a fail.
+ this->memory_.SetData8(0x1000, 0);
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+ ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+ this->memory_.SetData8(0x1000, 2);
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+ ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_expect_cache_fail) {
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+ ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+ ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_pcrel) {
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
+ this->eh_frame_->TestSetEntriesOffset(0x1000);
+ this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x1380U, info->pc);
+ EXPECT_EQ(0x1540U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_datarel) {
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4);
+ this->eh_frame_->TestSetEntriesOffset(0x1000);
+ this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x3340U, info->pc);
+ EXPECT_EQ(0x3500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_cached) {
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+ this->eh_frame_->TestSetEntriesOffset(0x1000);
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x340U, info->pc);
+ EXPECT_EQ(0x500U, info->offset);
+
+ // Clear the memory so that this will fail if it doesn't read cached data.
+ this->memory_.Clear();
+
+ info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x340U, info->pc);
+ EXPECT_EQ(0x500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetBinary_verify) {
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+ this->eh_frame_->TestSetFdeCount(10);
+
+ typename DwarfEhFrame<TypeParam>::FdeInfo info;
+ for (size_t i = 0; i < 10; i++) {
+ info.pc = 0x1000 * (i + 1);
+ info.offset = 0x5000 + i * 0x20;
+ this->eh_frame_->TestSetFdeInfo(i, info);
+ }
+
+ uint64_t fde_offset;
+ EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
+ // Not an error, just not found.
+ ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+ // Even number of elements.
+ for (size_t i = 0; i < 10; i++) {
+ TypeParam pc = 0x1000 * (i + 1);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10)) << "Failed at index "
+ << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10))
+ << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ }
+ // Odd number of elements.
+ for (size_t i = 0; i < 9; i++) {
+ TypeParam pc = 0x1000 * (i + 1);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9)) << "Failed at index "
+ << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9))
+ << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ }
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential) {
+ this->eh_frame_->TestSetFdeCount(10);
+ this->eh_frame_->TestSetEntriesDataOffset(0x100);
+ this->eh_frame_->TestSetEntriesEnd(0x2000);
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ this->memory_.SetData32(0x1048, 0x440);
+ this->memory_.SetData32(0x104c, 0x600);
+
+ // Verify that if entries is zero, that it fails.
+ uint64_t fde_offset;
+ ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset));
+ this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset));
+ EXPECT_EQ(0x500U, fde_offset);
+
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset));
+ EXPECT_EQ(0x600U, fde_offset);
+
+ // Expect that the data is cached so no more memory reads will occur.
+ this->memory_.Clear();
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset));
+ EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_last_element) {
+ this->eh_frame_->TestSetFdeCount(2);
+ this->eh_frame_->TestSetEntriesDataOffset(0x100);
+ this->eh_frame_->TestSetEntriesEnd(0x2000);
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+ this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ this->memory_.SetData32(0x1048, 0x440);
+ this->memory_.SetData32(0x104c, 0x600);
+
+ uint64_t fde_offset;
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+ EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_end_check) {
+ this->eh_frame_->TestSetFdeCount(2);
+ this->eh_frame_->TestSetEntriesDataOffset(0x100);
+ this->eh_frame_->TestSetEntriesEnd(0x1048);
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ this->memory_.SetData32(0x1048, 0x440);
+ this->memory_.SetData32(0x104c, 0x600);
+
+ uint64_t fde_offset;
+ ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_fail_fde_count) {
+ this->eh_frame_->TestSetFdeCount(0);
+
+ uint64_t fde_offset;
+ ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_binary_search) {
+ this->eh_frame_->TestSetTableEntrySize(16);
+ this->eh_frame_->TestSetFdeCount(10);
+
+ typename DwarfEhFrame<TypeParam>::FdeInfo info;
+ info.pc = 0x550;
+ info.offset = 0x10500;
+ this->eh_frame_->TestSetFdeInfo(5, info);
+ info.pc = 0x750;
+ info.offset = 0x10700;
+ this->eh_frame_->TestSetFdeInfo(7, info);
+ info.pc = 0x850;
+ info.offset = 0x10800;
+ this->eh_frame_->TestSetFdeInfo(8, info);
+
+ uint64_t fde_offset;
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x800, &fde_offset));
+ EXPECT_EQ(0x10700U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_sequential_search) {
+ this->eh_frame_->TestSetFdeCount(10);
+ this->eh_frame_->TestSetTableEntrySize(0);
+
+ typename DwarfEhFrame<TypeParam>::FdeInfo info;
+ info.pc = 0x50;
+ info.offset = 0x10000;
+ this->eh_frame_->TestSetFdeInfo(0, info);
+ info.pc = 0x150;
+ info.offset = 0x10100;
+ this->eh_frame_->TestSetFdeInfo(1, info);
+ info.pc = 0x250;
+ info.offset = 0x10200;
+ this->eh_frame_->TestSetFdeInfo(2, info);
+
+ uint64_t fde_offset;
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset));
+ EXPECT_EQ(0x10100U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) {
+ // CIE 32 information.
+ this->memory_.SetData32(0xf000, 0x100);
+ this->memory_.SetData32(0xf004, 0);
+ this->memory_.SetData8(0xf008, 0x1);
+ this->memory_.SetData8(0xf009, '\0');
+ this->memory_.SetData8(0xf00a, 4);
+ this->memory_.SetData8(0xf00b, 8);
+ this->memory_.SetData8(0xf00c, 0x20);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x14000, 0x20);
+ this->memory_.SetData32(0x14004, 0x5004);
+ this->memory_.SetData32(0x14008, 0x9000);
+ this->memory_.SetData32(0x1400c, 0x100);
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x14000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x1d00cU, fde->pc_start);
+ EXPECT_EQ(0x1d10cU, fde->pc_end);
+ EXPECT_EQ(0xf000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) {
+ // CIE 64 information.
+ this->memory_.SetData32(0x6000, 0xffffffff);
+ this->memory_.SetData64(0x6004, 0x100);
+ this->memory_.SetData64(0x600c, 0);
+ this->memory_.SetData8(0x6014, 0x1);
+ this->memory_.SetData8(0x6015, '\0');
+ this->memory_.SetData8(0x6016, 4);
+ this->memory_.SetData8(0x6017, 8);
+ this->memory_.SetData8(0x6018, 0x20);
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x8000, 0xffffffff);
+ this->memory_.SetData64(0x8004, 0x200);
+ this->memory_.SetData64(0x800c, 0x200c);
+ this->memory_.SetData64(0x8014, 0x5000);
+ this->memory_.SetData64(0x801c, 0x300);
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x8000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+ EXPECT_EQ(0xd01cU, fde->pc_start);
+ EXPECT_EQ(0xd31cU, fde->pc_end);
+ EXPECT_EQ(0x6000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init, GetFdeInfoFromIndex_expect_cache_fail,
+ GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel,
+ GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify,
+ GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
+ GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
+ GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
+ GetCieFde32, GetCieFde64);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
index 4877f36..08fe7cf 100644
--- a/libunwindstack/tests/DwarfMemoryTest.cpp
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -21,10 +21,12 @@
#include <gtest/gtest.h>
-#include "DwarfMemory.h"
+#include <unwindstack/DwarfMemory.h>
#include "MemoryFake.h"
+namespace unwindstack {
+
class DwarfMemoryTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -237,9 +239,13 @@
ASSERT_EQ(0U, value);
}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) { ReadEncodedValue_omit<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) {
+ ReadEncodedValue_omit<uint32_t>();
+}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) { ReadEncodedValue_omit<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) {
+ ReadEncodedValue_omit<uint64_t>();
+}
TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint32_t) {
uint64_t value = 100;
@@ -302,9 +308,13 @@
ASSERT_EQ(0xffffffffffffe100ULL, value);
}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) { ReadEncodedValue_leb128<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) {
+ ReadEncodedValue_leb128<uint32_t>();
+}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) { ReadEncodedValue_leb128<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) {
+ ReadEncodedValue_leb128<uint64_t>();
+}
template <typename AddressType>
void DwarfMemoryTest::ReadEncodedValue_data1() {
@@ -319,9 +329,13 @@
ASSERT_EQ(0xffffffffffffffe0ULL, value);
}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) { ReadEncodedValue_data1<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) {
+ ReadEncodedValue_data1<uint32_t>();
+}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) { ReadEncodedValue_data1<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) {
+ ReadEncodedValue_data1<uint64_t>();
+}
template <typename AddressType>
void DwarfMemoryTest::ReadEncodedValue_data2() {
@@ -336,9 +350,13 @@
ASSERT_EQ(0xffffffffffffe000ULL, value);
}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) { ReadEncodedValue_data2<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) {
+ ReadEncodedValue_data2<uint32_t>();
+}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) { ReadEncodedValue_data2<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) {
+ ReadEncodedValue_data2<uint64_t>();
+}
template <typename AddressType>
void DwarfMemoryTest::ReadEncodedValue_data4() {
@@ -353,9 +371,13 @@
ASSERT_EQ(0xffffffffe0000000ULL, value);
}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) { ReadEncodedValue_data4<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) {
+ ReadEncodedValue_data4<uint32_t>();
+}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) { ReadEncodedValue_data4<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) {
+ ReadEncodedValue_data4<uint64_t>();
+}
template <typename AddressType>
void DwarfMemoryTest::ReadEncodedValue_data8() {
@@ -370,9 +392,13 @@
ASSERT_EQ(0xe000000000000000ULL, value);
}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) { ReadEncodedValue_data8<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) {
+ ReadEncodedValue_data8<uint32_t>();
+}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) { ReadEncodedValue_data8<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) {
+ ReadEncodedValue_data8<uint64_t>();
+}
template <typename AddressType>
void DwarfMemoryTest::ReadEncodedValue_non_zero_adjust() {
@@ -470,3 +496,5 @@
ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
ASSERT_EQ(0x14234U, value);
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
index d18aad0..234d1c9 100644
--- a/libunwindstack/tests/DwarfOpLogTest.cpp
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -21,14 +21,17 @@
#include <gtest/gtest.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Regs.h>
+
#include "DwarfError.h"
-#include "DwarfMemory.h"
#include "DwarfOp.h"
-#include "Log.h"
-#include "Regs.h"
#include "MemoryFake.h"
+namespace unwindstack {
+
template <typename TypeParam>
class DwarfOpLogTest : public ::testing::Test {
protected:
@@ -66,3 +69,5 @@
typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 520c545..47a40cf 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -21,25 +21,16 @@
#include <gtest/gtest.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+
#include "DwarfError.h"
-#include "DwarfMemory.h"
#include "DwarfOp.h"
-#include "Log.h"
-#include "Regs.h"
#include "MemoryFake.h"
+#include "RegsFake.h"
-template <typename TypeParam>
-class RegsFake : public RegsTmpl<TypeParam> {
- public:
- RegsFake(uint16_t total_regs, uint16_t sp_reg)
- : RegsTmpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
- virtual ~RegsFake() = default;
-
- uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
- uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
- bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
-};
+namespace unwindstack {
template <typename TypeParam>
class DwarfOpTest : public ::testing::Test {
@@ -1591,3 +1582,5 @@
typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
new file mode 100644
index 0000000..b871539
--- /dev/null
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -0,0 +1,839 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfSection.h>
+
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class MockDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
+ public:
+ MockDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
+ virtual ~MockDwarfSectionImpl() = default;
+
+ MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+
+ MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
+
+ MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
+
+ MOCK_METHOD1(IsCie32, bool(uint32_t));
+
+ MOCK_METHOD1(IsCie64, bool(uint64_t));
+
+ MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
+
+ MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
+
+ MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
+
+ void TestSetCachedCieEntry(uint64_t offset, const DwarfCie& cie) {
+ this->cie_entries_[offset] = cie;
+ }
+ void TestClearCachedCieEntry() { this->cie_entries_.clear(); }
+
+ void TestSetCachedFdeEntry(uint64_t offset, const DwarfFde& fde) {
+ this->fde_entries_[offset] = fde;
+ }
+ void TestClearCachedFdeEntry() { this->fde_entries_.clear(); }
+
+ void TestSetCachedCieLocRegs(uint64_t offset, const dwarf_loc_regs_t& loc_regs) {
+ this->cie_loc_regs_[offset] = loc_regs;
+ }
+ void TestClearCachedCieLocRegs() { this->cie_loc_regs_.clear(); }
+
+ void TestClearError() { this->last_error_ = DWARF_ERROR_NONE; }
+};
+
+template <typename TypeParam>
+class DwarfSectionImplTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ section_ = new MockDwarfSectionImpl<TypeParam>(&memory_);
+ ResetLogs();
+ }
+
+ void TearDown() override { delete section_; }
+
+ MemoryFake memory_;
+ MockDwarfSectionImpl<TypeParam>* section_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfSectionImplTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ TypeParam cfa_value = 0x12345;
+ this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x12345U, regs.sp());
+ EXPECT_EQ(0x20U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x80000000U, regs.sp());
+ EXPECT_EQ(0x20U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
+ DwarfCie cie{.return_address_register = 60};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+ this->section_->TestClearError();
+ loc_regs.erase(CFA_REG);
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+ this->section_->TestClearError();
+ loc_regs.erase(CFA_REG);
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+
+ this->section_->TestClearError();
+ loc_regs.erase(CFA_REG);
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {9, 0}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x20U, regs.pc());
+ EXPECT_EQ(0x2000U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[6] = 0x4000;
+ regs[9] = 0x3000;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {6, 0}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x20U, regs.pc());
+ EXPECT_EQ(0x4000U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 0}};
+ loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {10, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ if (sizeof(TypeParam) == sizeof(uint64_t)) {
+ this->memory_.SetData64(0x2150, 0x12345678abcdef00ULL);
+ } else {
+ this->memory_.SetData32(0x2150, 0x12345678);
+ }
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[3] = 0x234;
+ regs[5] = 0x10;
+ regs[8] = 0x2100;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x100, 0}};
+ loc_regs[2] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x50, 0}};
+ loc_regs[3] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x10U, regs.pc());
+ EXPECT_EQ(0x2100U, regs.sp());
+ EXPECT_EQ(0x2200U, regs[1]);
+ EXPECT_EQ(0x234U, regs[3]);
+ if (sizeof(TypeParam) == sizeof(uint64_t)) {
+ EXPECT_EQ(0x12345678abcdef00ULL, regs[2]);
+ } else {
+ EXPECT_EQ(0x12345678U, regs[2]);
+ }
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[5] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0U, regs.pc());
+ EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x20U, regs.pc());
+ EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ // This should not result in any errors.
+ loc_regs[20] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x20U, regs.pc());
+ EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[8] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ TypeParam cfa_value = 0x12345;
+ this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x3000U, regs.sp());
+ EXPECT_EQ(0x12345U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[8] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x3000U, regs.sp());
+ EXPECT_EQ(0x80000000U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x100;
+ regs[8] = 0x2000;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ EXPECT_EQ(0x2000U, regs.sp());
+ EXPECT_EQ(0x100U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_fail_should_not_cache) {
+ ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+ this->section_->TestClearError();
+ ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_32_version_check) {
+ this->memory_.SetData32(0x5000, 0x100);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 0x1);
+ this->memory_.SetData8(0x5009, '\0');
+ this->memory_.SetData8(0x500a, 4);
+ this->memory_.SetData8(0x500b, 8);
+ this->memory_.SetData8(0x500c, 0x20);
+
+ EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+ const DwarfCie* cie = this->section_->GetCie(0x5000);
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(1U, cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+ EXPECT_EQ(0U, cie->segment_size);
+ EXPECT_EQ(1U, cie->augmentation_string.size());
+ EXPECT_EQ('\0', cie->augmentation_string[0]);
+ EXPECT_EQ(0U, cie->personality_handler);
+ EXPECT_EQ(0x500dU, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(8, cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, cie->return_address_register);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->section_->last_error());
+
+ this->section_->TestClearCachedCieEntry();
+ // Set version to 0, 2, 5 and verify we fail.
+ this->memory_.SetData8(0x5008, 0x0);
+ this->section_->TestClearError();
+ ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+
+ this->memory_.SetData8(0x5008, 0x2);
+ this->section_->TestClearError();
+ ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+
+ this->memory_.SetData8(0x5008, 0x5);
+ this->section_->TestClearError();
+ ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_negative_data_alignment_factor) {
+ this->memory_.SetData32(0x5000, 0x100);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 0x1);
+ this->memory_.SetData8(0x5009, '\0');
+ this->memory_.SetData8(0x500a, 4);
+ this->memory_.SetMemory(0x500b, std::vector<uint8_t>{0xfc, 0xff, 0xff, 0xff, 0x7f});
+ this->memory_.SetData8(0x5010, 0x20);
+
+ EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+ const DwarfCie* cie = this->section_->GetCie(0x5000);
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(1U, cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+ EXPECT_EQ(0U, cie->segment_size);
+ EXPECT_EQ(1U, cie->augmentation_string.size());
+ EXPECT_EQ('\0', cie->augmentation_string[0]);
+ EXPECT_EQ(0U, cie->personality_handler);
+ EXPECT_EQ(0x5011U, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(-4, cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_64_no_augment) {
+ this->memory_.SetData32(0x8000, 0xffffffff);
+ this->memory_.SetData64(0x8004, 0x200);
+ this->memory_.SetData64(0x800c, 0xffffffff);
+ this->memory_.SetData8(0x8014, 0x1);
+ this->memory_.SetData8(0x8015, '\0');
+ this->memory_.SetData8(0x8016, 4);
+ this->memory_.SetData8(0x8017, 8);
+ this->memory_.SetData8(0x8018, 0x20);
+
+ EXPECT_CALL(*this->section_, IsCie64(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+ const DwarfCie* cie = this->section_->GetCie(0x8000);
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(1U, cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+ EXPECT_EQ(0U, cie->segment_size);
+ EXPECT_EQ(1U, cie->augmentation_string.size());
+ EXPECT_EQ('\0', cie->augmentation_string[0]);
+ EXPECT_EQ(0U, cie->personality_handler);
+ EXPECT_EQ(0x8019U, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x820cU, cie->cfa_instructions_end);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(8, cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_augment) {
+ this->memory_.SetData32(0x5000, 0x100);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 0x1);
+ this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
+ this->memory_.SetData8(0x500e, 4);
+ this->memory_.SetData8(0x500f, 8);
+ this->memory_.SetData8(0x5010, 0x10);
+ // Augment length.
+ this->memory_.SetData8(0x5011, 0xf);
+ // L data.
+ this->memory_.SetData8(0x5012, DW_EH_PE_textrel | DW_EH_PE_udata2);
+ // P data.
+ this->memory_.SetData8(0x5013, DW_EH_PE_udata4);
+ this->memory_.SetData32(0x5014, 0x12345678);
+ // R data.
+ this->memory_.SetData8(0x5018, DW_EH_PE_udata2);
+
+ EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+ const DwarfCie* cie = this->section_->GetCie(0x5000);
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(1U, cie->version);
+ EXPECT_EQ(DW_EH_PE_udata2, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_textrel | DW_EH_PE_udata2, cie->lsda_encoding);
+ EXPECT_EQ(0U, cie->segment_size);
+ EXPECT_EQ(5U, cie->augmentation_string.size());
+ EXPECT_EQ('z', cie->augmentation_string[0]);
+ EXPECT_EQ('L', cie->augmentation_string[1]);
+ EXPECT_EQ('P', cie->augmentation_string[2]);
+ EXPECT_EQ('R', cie->augmentation_string[3]);
+ EXPECT_EQ('\0', cie->augmentation_string[4]);
+ EXPECT_EQ(0x12345678U, cie->personality_handler);
+ EXPECT_EQ(0x5021U, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(8, cie->data_alignment_factor);
+ EXPECT_EQ(0x10U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_3) {
+ this->memory_.SetData32(0x5000, 0x100);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 0x3);
+ this->memory_.SetData8(0x5009, '\0');
+ this->memory_.SetData8(0x500a, 4);
+ this->memory_.SetData8(0x500b, 8);
+ this->memory_.SetMemory(0x500c, std::vector<uint8_t>{0x81, 0x03});
+
+ EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+ const DwarfCie* cie = this->section_->GetCie(0x5000);
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(3U, cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+ EXPECT_EQ(0U, cie->segment_size);
+ EXPECT_EQ(1U, cie->augmentation_string.size());
+ EXPECT_EQ('\0', cie->augmentation_string[0]);
+ EXPECT_EQ(0U, cie->personality_handler);
+ EXPECT_EQ(0x500eU, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(8, cie->data_alignment_factor);
+ EXPECT_EQ(0x181U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_4) {
+ this->memory_.SetData32(0x5000, 0x100);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 0x4);
+ this->memory_.SetData8(0x5009, '\0');
+ this->memory_.SetData8(0x500a, 4);
+ this->memory_.SetData8(0x500b, 0x13);
+ this->memory_.SetData8(0x500c, 4);
+ this->memory_.SetData8(0x500d, 8);
+ this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x81, 0x03});
+
+ EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
+
+ const DwarfCie* cie = this->section_->GetCie(0x5000);
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(4U, cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+ EXPECT_EQ(0x13U, cie->segment_size);
+ EXPECT_EQ(1U, cie->augmentation_string.size());
+ EXPECT_EQ('\0', cie->augmentation_string[0]);
+ EXPECT_EQ(0U, cie->personality_handler);
+ EXPECT_EQ(0x5010U, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(8, cie->data_alignment_factor);
+ EXPECT_EQ(0x181U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_fail_should_not_cache) {
+ ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+ this->section_->TestClearError();
+ ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment) {
+ this->memory_.SetData32(0x4000, 0x20);
+ this->memory_.SetData32(0x4004, 0x8000);
+ this->memory_.SetData32(0x4008, 0x5000);
+ this->memory_.SetData32(0x400c, 0x100);
+
+ EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
+ DwarfCie cie{};
+ cie.fde_address_encoding = DW_EH_PE_udata4;
+ this->section_->TestSetCachedCieEntry(0x8000, cie);
+ EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+ const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(0x4010U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x4024U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x5000U, fde->pc_start);
+ EXPECT_EQ(0x5100U, fde->pc_end);
+ EXPECT_EQ(0x8000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment_non_zero_segment_size) {
+ this->memory_.SetData32(0x4000, 0x30);
+ this->memory_.SetData32(0x4004, 0x8000);
+ this->memory_.SetData32(0x4018, 0x5000);
+ this->memory_.SetData32(0x401c, 0x100);
+
+ EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
+ DwarfCie cie{};
+ cie.fde_address_encoding = DW_EH_PE_udata4;
+ cie.segment_size = 0x10;
+ this->section_->TestSetCachedCieEntry(0x8000, cie);
+ EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+ const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(0x4020U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x4034U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x5000U, fde->pc_start);
+ EXPECT_EQ(0x5100U, fde->pc_end);
+ EXPECT_EQ(0x8000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_augment) {
+ this->memory_.SetData32(0x4000, 0x100);
+ this->memory_.SetData32(0x4004, 0x8000);
+ this->memory_.SetData32(0x4008, 0x5000);
+ this->memory_.SetData32(0x400c, 0x100);
+ this->memory_.SetMemory(0x4010, std::vector<uint8_t>{0x82, 0x01});
+ this->memory_.SetData16(0x4012, 0x1234);
+
+ EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
+ DwarfCie cie{};
+ cie.fde_address_encoding = DW_EH_PE_udata4;
+ cie.augmentation_string.push_back('z');
+ cie.lsda_encoding = DW_EH_PE_udata2;
+ this->section_->TestSetCachedCieEntry(0x8000, cie);
+ EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+ const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(0x4094U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x4104U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x5000U, fde->pc_start);
+ EXPECT_EQ(0x5100U, fde->pc_end);
+ EXPECT_EQ(0x8000U, fde->cie_offset);
+ EXPECT_EQ(0x1234U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_64_no_augment) {
+ this->memory_.SetData32(0x4000, 0xffffffff);
+ this->memory_.SetData64(0x4004, 0x100);
+ this->memory_.SetData64(0x400c, 0x12345678);
+ this->memory_.SetData32(0x4014, 0x5000);
+ this->memory_.SetData32(0x4018, 0x100);
+
+ EXPECT_CALL(*this->section_, IsCie64(0x12345678)).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*this->section_, GetCieOffsetFromFde64(0x12345678))
+ .WillOnce(::testing::Return(0x12345678));
+ DwarfCie cie{};
+ cie.fde_address_encoding = DW_EH_PE_udata4;
+ this->section_->TestSetCachedCieEntry(0x12345678, cie);
+ EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
+
+ const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(0x401cU, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x410cU, fde->cfa_instructions_end);
+ EXPECT_EQ(0x5000U, fde->pc_start);
+ EXPECT_EQ(0x5100U, fde->pc_end);
+ EXPECT_EQ(0x12345678U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_cached) {
+ DwarfCie cie{};
+ cie.fde_address_encoding = DW_EH_PE_udata4;
+ cie.augmentation_string.push_back('z');
+ cie.lsda_encoding = DW_EH_PE_udata2;
+
+ DwarfFde fde_cached{};
+ fde_cached.cfa_instructions_offset = 0x1000;
+ fde_cached.cfa_instructions_end = 0x1100;
+ fde_cached.pc_start = 0x9000;
+ fde_cached.pc_end = 0x9400;
+ fde_cached.cie_offset = 0x30000;
+ fde_cached.cie = &cie;
+ this->section_->TestSetCachedFdeEntry(0x6000, fde_cached);
+
+ const DwarfFde* fde = this->section_->GetFdeFromOffset(0x6000);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_EQ(&cie, fde->cie);
+ EXPECT_EQ(0x1000U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x1100U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x9000U, fde->pc_start);
+ EXPECT_EQ(0x9400U, fde->pc_end);
+ EXPECT_EQ(0x30000U, fde->cie_offset);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_not_cached) {
+ DwarfCie cie{};
+ cie.cfa_instructions_offset = 0x3000;
+ cie.cfa_instructions_end = 0x3002;
+ DwarfFde fde{};
+ fde.cie = &cie;
+ fde.cie_offset = 0x8000;
+ fde.cfa_instructions_offset = 0x6000;
+ fde.cfa_instructions_end = 0x6002;
+
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x09, 0x02, 0x01});
+ this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
+
+ dwarf_loc_regs_t loc_regs;
+ ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+ ASSERT_EQ(2U, loc_regs.size());
+
+ auto entry = loc_regs.find(2);
+ ASSERT_NE(entry, loc_regs.end());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+ ASSERT_EQ(1U, entry->second.values[0]);
+
+ entry = loc_regs.find(4);
+ ASSERT_NE(entry, loc_regs.end());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+ ASSERT_EQ(3U, entry->second.values[0]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_cached) {
+ DwarfCie cie{};
+ cie.cfa_instructions_offset = 0x3000;
+ cie.cfa_instructions_end = 0x3002;
+ DwarfFde fde{};
+ fde.cie = &cie;
+ fde.cie_offset = 0x8000;
+ fde.cfa_instructions_offset = 0x6000;
+ fde.cfa_instructions_end = 0x6002;
+
+ dwarf_loc_regs_t cie_loc_regs{{6, {DWARF_LOCATION_REGISTER, {4, 0}}}};
+ this->section_->TestSetCachedCieLocRegs(0x8000, cie_loc_regs);
+ this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
+
+ dwarf_loc_regs_t loc_regs;
+ ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+ ASSERT_EQ(2U, loc_regs.size());
+
+ auto entry = loc_regs.find(6);
+ ASSERT_NE(entry, loc_regs.end());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+ ASSERT_EQ(4U, entry->second.values[0]);
+
+ entry = loc_regs.find(4);
+ ASSERT_NE(entry, loc_regs.end());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+ ASSERT_EQ(3U, entry->second.values[0]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Log) {
+ DwarfCie cie{};
+ cie.cfa_instructions_offset = 0x5000;
+ cie.cfa_instructions_end = 0x5001;
+ DwarfFde fde{};
+ fde.cie = &cie;
+ fde.cfa_instructions_offset = 0x6000;
+ fde.cfa_instructions_end = 0x6001;
+
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
+ this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
+ ASSERT_TRUE(this->section_->Log(2, 0x1000, 0x1000, &fde));
+
+ ASSERT_EQ(
+ "4 unwind DW_CFA_nop\n"
+ "4 unwind Raw Data: 0x00\n"
+ "4 unwind DW_CFA_restore register(2)\n"
+ "4 unwind Raw Data: 0xc2\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_CASE_P(
+ DwarfSectionImplTest, Eval_cfa_expr_eval_fail, Eval_cfa_expr_no_stack,
+ Eval_cfa_expr_is_register, Eval_cfa_expr, Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa,
+ Eval_cfa_bad, Eval_cfa_register_prev, Eval_cfa_register_from_value, Eval_double_indirection,
+ Eval_invalid_register, Eval_different_reg_locations, Eval_return_address_undefined,
+ Eval_return_address, Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+ Eval_same_cfa_same_pc, GetCie_fail_should_not_cache, GetCie_32_version_check,
+ GetCie_negative_data_alignment_factor, GetCie_64_no_augment, GetCie_augment, GetCie_version_3,
+ GetCie_version_4, GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
+ GetFdeFromOffset_32_no_augment_non_zero_segment_size, GetFdeFromOffset_32_augment,
+ GetFdeFromOffset_64_no_augment, GetFdeFromOffset_cached, GetCfaLocationInfo_cie_not_cached,
+ GetCfaLocationInfo_cie_cached, Log);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
new file mode 100644
index 0000000..fc67063
--- /dev/null
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfSection.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MockDwarfSection : public DwarfSection {
+ public:
+ MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
+ virtual ~MockDwarfSection() = default;
+
+ MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
+
+ MOCK_METHOD4(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*));
+
+ MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
+
+ MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+
+ MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
+
+ MOCK_METHOD1(GetFdeFromOffset, const DwarfFde*(uint64_t));
+
+ MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
+
+ MOCK_METHOD1(IsCie32, bool(uint32_t));
+
+ MOCK_METHOD1(IsCie64, bool(uint64_t));
+
+ MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
+
+ MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
+
+ MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
+};
+
+class DwarfSectionTest : public ::testing::Test {
+ protected:
+ MemoryFake memory_;
+};
+
+TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_from_pc) {
+ MockDwarfSection mock_section(&memory_);
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(false));
+
+ // Verify nullptr when GetFdeOffsetFromPc fails.
+ ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
+}
+
+TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_fde_pc_end) {
+ MockDwarfSection mock_section(&memory_);
+
+ DwarfFde fde{};
+ fde.pc_end = 0x500;
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(true));
+ EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+ // Verify nullptr when GetFdeOffsetFromPc fails.
+ ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
+}
+
+TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_pass) {
+ MockDwarfSection mock_section(&memory_);
+
+ DwarfFde fde{};
+ fde.pc_end = 0x2000;
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(true));
+ EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+ // Verify nullptr when GetFdeOffsetFromPc fails.
+ ASSERT_EQ(&fde, mock_section.GetFdeFromPc(0x1000));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_fde) {
+ MockDwarfSection mock_section(&memory_);
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(false));
+
+ ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_cie_null) {
+ MockDwarfSection mock_section(&memory_);
+
+ DwarfFde fde{};
+ fde.pc_end = 0x2000;
+ fde.cie = nullptr;
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(true));
+ EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+ ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
+ MockDwarfSection mock_section(&memory_);
+
+ DwarfCie cie{};
+ DwarfFde fde{};
+ fde.pc_end = 0x2000;
+ fde.cie = &cie;
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(true));
+ EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+ EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ .WillOnce(::testing::Return(false));
+
+ ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+}
+
+TEST_F(DwarfSectionTest, Step_pass) {
+ MockDwarfSection mock_section(&memory_);
+
+ DwarfCie cie{};
+ DwarfFde fde{};
+ fde.pc_end = 0x2000;
+ fde.cie = &cie;
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(true));
+ EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+ EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ .WillOnce(::testing::Return(true));
+
+ MemoryFake process;
+ EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr))
+ .WillOnce(::testing::Return(true));
+
+ ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 67c9a6b..c7ef4a1 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -20,12 +20,15 @@
#include <vector>
+#include <unwindstack/Regs.h>
+
#include "ElfInterfaceArm.h"
#include "Machine.h"
-#include "Regs.h"
#include "MemoryFake.h"
+namespace unwindstack {
+
class ElfInterfaceArmTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -370,3 +373,5 @@
ASSERT_EQ(0x10U, regs.pc());
ASSERT_EQ(0x10U, regs[ARM_REG_PC]);
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index c31903d..acb7320 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -20,7 +20,9 @@
#include <gtest/gtest.h>
-#include "ElfInterface.h"
+#include <unwindstack/ElfInterface.h>
+
+#include "DwarfEncoding.h"
#include "ElfInterfaceArm.h"
#include "MemoryFake.h"
@@ -33,6 +35,8 @@
#define EM_AARCH64 183
#endif
+namespace unwindstack {
+
class ElfInterfaceTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -67,9 +71,49 @@
template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
void SonameSize();
+ template <typename ElfType>
+ void InitHeadersEhFrameTest();
+
+ template <typename ElfType>
+ void InitHeadersDebugFrame();
+
+ template <typename ElfType>
+ void InitHeadersEhFrameFail();
+
+ template <typename ElfType>
+ void InitHeadersDebugFrameFail();
+
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersMalformed();
+
+ template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
+ void InitSectionHeaders(uint64_t entry_size);
+
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersOffsets();
+
+ template <typename Sym>
+ void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
+ uint64_t sym_offset, const char* name);
+
MemoryFake memory_;
};
+template <typename Sym>
+void ElfInterfaceTest::InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
+ uint64_t sym_offset, const char* name) {
+ Sym sym;
+ memset(&sym, 0, sizeof(sym));
+ sym.st_info = STT_FUNC;
+ sym.st_value = value;
+ sym.st_size = size;
+ sym.st_name = name_offset;
+ sym.st_shndx = SHN_COMMON;
+
+ memory_.SetMemory(offset, &sym, sizeof(sym));
+ memory_.SetMemory(sym_offset + name_offset, name, strlen(name) + 1);
+}
+
template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
void ElfInterfaceTest::SinglePtLoad() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
@@ -571,3 +615,315 @@
TEST_F(ElfInterfaceTest, elf64_soname_size) {
SonameSize<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
}
+
+class MockElfInterface32 : public ElfInterface32 {
+ public:
+ MockElfInterface32(Memory* memory) : ElfInterface32(memory) {}
+ virtual ~MockElfInterface32() = default;
+
+ void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+ void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+
+ void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+ void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+class MockElfInterface64 : public ElfInterface64 {
+ public:
+ MockElfInterface64(Memory* memory) : ElfInterface64(memory) {}
+ virtual ~MockElfInterface64() = default;
+
+ void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+ void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+
+ void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+ void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersEhFrameTest() {
+ ElfType elf(&memory_);
+
+ elf.TestSetEhFrameOffset(0x10000);
+ elf.TestSetEhFrameSize(0);
+ elf.TestSetDebugFrameOffset(0);
+ elf.TestSetDebugFrameSize(0);
+
+ memory_.SetMemory(0x10000,
+ std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata2, DW_EH_PE_udata2});
+ memory_.SetData32(0x10004, 0x500);
+ memory_.SetData32(0x10008, 250);
+
+ elf.InitHeaders();
+
+ EXPECT_FALSE(elf.eh_frame() == nullptr);
+ EXPECT_TRUE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame32) {
+ InitHeadersEhFrameTest<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame64) {
+ InitHeadersEhFrameTest<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersDebugFrame() {
+ ElfType elf(&memory_);
+
+ elf.TestSetEhFrameOffset(0);
+ elf.TestSetEhFrameSize(0);
+ elf.TestSetDebugFrameOffset(0x5000);
+ elf.TestSetDebugFrameSize(0x200);
+
+ memory_.SetData32(0x5000, 0xfc);
+ memory_.SetData32(0x5004, 0xffffffff);
+ memory_.SetData8(0x5008, 1);
+ memory_.SetData8(0x5009, '\0');
+
+ memory_.SetData32(0x5100, 0xfc);
+ memory_.SetData32(0x5104, 0);
+ memory_.SetData32(0x5108, 0x1500);
+ memory_.SetData32(0x510c, 0x200);
+
+ elf.InitHeaders();
+
+ EXPECT_TRUE(elf.eh_frame() == nullptr);
+ EXPECT_FALSE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame32) {
+ InitHeadersDebugFrame<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame64) {
+ InitHeadersDebugFrame<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersEhFrameFail() {
+ ElfType elf(&memory_);
+
+ elf.TestSetEhFrameOffset(0x1000);
+ elf.TestSetEhFrameSize(0x100);
+ elf.TestSetDebugFrameOffset(0);
+ elf.TestSetDebugFrameSize(0);
+
+ elf.InitHeaders();
+
+ EXPECT_TRUE(elf.eh_frame() == nullptr);
+ EXPECT_EQ(0U, elf.eh_frame_offset());
+ EXPECT_EQ(static_cast<uint64_t>(-1), elf.eh_frame_size());
+ EXPECT_TRUE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame32_fail) {
+ InitHeadersEhFrameFail<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame64_fail) {
+ InitHeadersEhFrameFail<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersDebugFrameFail() {
+ ElfType elf(&memory_);
+
+ elf.TestSetEhFrameOffset(0);
+ elf.TestSetEhFrameSize(0);
+ elf.TestSetDebugFrameOffset(0x1000);
+ elf.TestSetDebugFrameSize(0x100);
+
+ elf.InitHeaders();
+
+ EXPECT_TRUE(elf.eh_frame() == nullptr);
+ EXPECT_TRUE(elf.debug_frame() == nullptr);
+ EXPECT_EQ(0U, elf.debug_frame_offset());
+ EXPECT_EQ(static_cast<uint64_t>(-1), elf.debug_frame_size());
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame32_fail) {
+ InitHeadersDebugFrameFail<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) {
+ InitHeadersDebugFrameFail<MockElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersMalformed() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_shoff = 0x1000;
+ ehdr.e_shnum = 10;
+ ehdr.e_shentsize = sizeof(Shdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ ASSERT_TRUE(elf->Init());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed32) {
+ InitSectionHeadersMalformed<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed64) {
+ InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x1000;
+
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 10;
+ ehdr.e_shentsize = entry_size;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_SYMTAB;
+ shdr.sh_link = 4;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = sizeof(Sym);
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_DYNSYM;
+ shdr.sh_link = 4;
+ shdr.sh_addr = 0x6000;
+ shdr.sh_offset = 0x6000;
+ shdr.sh_entsize = sizeof(Sym);
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0xa000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for the entries.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ InitSym<Sym>(0x5000, 0x90000, 0x1000, 0x100, 0xf000, "function_one");
+ InitSym<Sym>(0x6000, 0xd0000, 0x1000, 0x300, 0xf000, "function_two");
+
+ ASSERT_TRUE(elf->Init());
+ EXPECT_EQ(0U, elf->debug_frame_offset());
+ EXPECT_EQ(0U, elf->debug_frame_size());
+ EXPECT_EQ(0U, elf->gnu_debugdata_offset());
+ EXPECT_EQ(0U, elf->gnu_debugdata_size());
+
+ // Look in the first symbol table.
+ std::string name;
+ uint64_t name_offset;
+ ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset));
+ EXPECT_EQ("function_one", name);
+ EXPECT_EQ(16U, name_offset);
+ ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset));
+ EXPECT_EQ("function_two", name);
+ EXPECT_EQ(32U, name_offset);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers32) {
+ InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(sizeof(Elf32_Shdr));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers64) {
+ InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(sizeof(Elf64_Shdr));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size32) {
+ InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(0x100);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size64) {
+ InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(0x100);
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersOffsets() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 10;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x200;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x800;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x100;
+ shdr.sh_addr = 0x6000;
+ shdr.sh_offset = 0x6000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x500;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
+ memory_.SetMemory(0xf200, ".gnu_debugdata", sizeof(".gnu_debugdata"));
+
+ ASSERT_TRUE(elf->Init());
+ EXPECT_EQ(0x6000U, elf->debug_frame_offset());
+ EXPECT_EQ(0x500U, elf->debug_frame_size());
+ EXPECT_EQ(0x5000U, elf->gnu_debugdata_offset());
+ EXPECT_EQ(0x800U, elf->gnu_debugdata_size());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets32) {
+ InitSectionHeadersOffsets<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets64) {
+ InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 25fec8e..72ceb85 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -15,20 +15,24 @@
*/
#include <elf.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <gtest/gtest.h>
-#include "Elf.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include "ElfTestUtils.h"
#include "MemoryFake.h"
#if !defined(PT_ARM_EXIDX)
#define PT_ARM_EXIDX 0x70000001
#endif
-#if !defined(EM_AARCH64)
-#define EM_AARCH64 183
-#endif
+namespace unwindstack {
class ElfTest : public ::testing::Test {
protected:
@@ -36,35 +40,16 @@
memory_ = new MemoryFake;
}
- template <typename Ehdr>
- void InitEhdr(Ehdr* ehdr) {
- memset(ehdr, 0, sizeof(Ehdr));
- memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
- ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
- ehdr->e_ident[EI_VERSION] = EV_CURRENT;
- ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
- }
-
- void InitElf32(uint32_t type) {
+ void InitElf32(uint32_t machine_type) {
Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, machine_type);
- InitEhdr<Elf32_Ehdr>(&ehdr);
- ehdr.e_ident[EI_CLASS] = ELFCLASS32;
-
- ehdr.e_type = ET_DYN;
- ehdr.e_machine = type;
- ehdr.e_version = EV_CURRENT;
- ehdr.e_entry = 0;
ehdr.e_phoff = 0x100;
- ehdr.e_shoff = 0;
- ehdr.e_flags = 0;
ehdr.e_ehsize = sizeof(ehdr);
ehdr.e_phentsize = sizeof(Elf32_Phdr);
ehdr.e_phnum = 1;
ehdr.e_shentsize = sizeof(Elf32_Shdr);
- ehdr.e_shnum = 0;
- ehdr.e_shstrndx = 0;
- if (type == EM_ARM) {
+ if (machine_type == EM_ARM) {
ehdr.e_flags = 0x5000200;
ehdr.e_phnum = 2;
}
@@ -73,16 +58,13 @@
Elf32_Phdr phdr;
memset(&phdr, 0, sizeof(phdr));
phdr.p_type = PT_LOAD;
- phdr.p_offset = 0;
- phdr.p_vaddr = 0;
- phdr.p_paddr = 0;
phdr.p_filesz = 0x10000;
phdr.p_memsz = 0x10000;
phdr.p_flags = PF_R | PF_X;
phdr.p_align = 0x1000;
memory_->SetMemory(0x100, &phdr, sizeof(phdr));
- if (type == EM_ARM) {
+ if (machine_type == EM_ARM) {
memset(&phdr, 0, sizeof(phdr));
phdr.p_type = PT_ARM_EXIDX;
phdr.p_offset = 0x30000;
@@ -96,33 +78,21 @@
}
}
- void InitElf64(uint32_t type) {
+ void InitElf64(uint32_t machine_type) {
Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, machine_type);
- InitEhdr<Elf64_Ehdr>(&ehdr);
- ehdr.e_ident[EI_CLASS] = ELFCLASS64;
-
- ehdr.e_type = ET_DYN;
- ehdr.e_machine = type;
- ehdr.e_version = EV_CURRENT;
- ehdr.e_entry = 0;
ehdr.e_phoff = 0x100;
- ehdr.e_shoff = 0;
ehdr.e_flags = 0x5000200;
ehdr.e_ehsize = sizeof(ehdr);
ehdr.e_phentsize = sizeof(Elf64_Phdr);
ehdr.e_phnum = 1;
ehdr.e_shentsize = sizeof(Elf64_Shdr);
- ehdr.e_shnum = 0;
- ehdr.e_shstrndx = 0;
memory_->SetMemory(0, &ehdr, sizeof(ehdr));
Elf64_Phdr phdr;
memset(&phdr, 0, sizeof(phdr));
phdr.p_type = PT_LOAD;
- phdr.p_offset = 0;
- phdr.p_vaddr = 0;
- phdr.p_paddr = 0;
phdr.p_filesz = 0x10000;
phdr.p_memsz = 0x10000;
phdr.p_flags = PF_R | PF_X;
@@ -208,3 +178,95 @@
ASSERT_EQ(ELFCLASS64, elf.class_type());
ASSERT_TRUE(elf.interface() != nullptr);
}
+
+TEST_F(ElfTest, gnu_debugdata_init_fail32) {
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(offset, ptr, size);
+ });
+
+ Elf elf(memory_);
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+ EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
+}
+
+TEST_F(ElfTest, gnu_debugdata_init_fail64) {
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(offset, ptr, size);
+ });
+
+ Elf elf(memory_);
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+ EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
+}
+
+TEST_F(ElfTest, gnu_debugdata_init32) {
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(offset, ptr, size);
+ });
+
+ Elf elf(memory_);
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+ EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x8cU, elf.interface()->gnu_debugdata_size());
+
+ elf.InitGnuDebugdata();
+ ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(ElfTest, gnu_debugdata_init64) {
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(offset, ptr, size);
+ });
+
+ Elf elf(memory_);
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+ EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x90U, elf.interface()->gnu_debugdata_size());
+
+ elf.InitGnuDebugdata();
+ ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
+}
+
+class MockElf : public Elf {
+ public:
+ MockElf(Memory* memory) : Elf(memory) {}
+ virtual ~MockElf() = default;
+
+ void set_valid(bool valid) { valid_ = valid; }
+ void set_elf_interface(ElfInterface* interface) { interface_.reset(interface); }
+};
+
+TEST_F(ElfTest, rel_pc) {
+ MockElf elf(memory_);
+
+ ElfInterface* interface = new ElfInterface32(memory_);
+ elf.set_elf_interface(interface);
+
+ elf.set_valid(true);
+ interface->set_load_bias(0);
+ MapInfo map_info{.start = 0x1000, .end = 0x2000};
+
+ ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+
+ interface->set_load_bias(0x3000);
+ ASSERT_EQ(0x3101U, elf.GetRelPc(0x1101, &map_info));
+
+ elf.set_valid(false);
+ ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.cpp b/libunwindstack/tests/ElfTestUtils.cpp
new file mode 100644
index 0000000..069386b
--- /dev/null
+++ b/libunwindstack/tests/ElfTestUtils.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "ElfTestUtils.h"
+
+namespace unwindstack {
+
+template <typename Ehdr>
+void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type) {
+ memset(ehdr, 0, sizeof(Ehdr));
+ memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
+ ehdr->e_ident[EI_CLASS] = elf_class;
+ ehdr->e_type = ET_DYN;
+ ehdr->e_machine = machine_type;
+ ehdr->e_version = EV_CURRENT;
+ ehdr->e_ehsize = sizeof(Ehdr);
+}
+
+static std::string GetTestFileDirectory() {
+ std::string exec(testing::internal::GetArgvs()[0]);
+ auto const value = exec.find_last_of('/');
+ if (value == std::string::npos) {
+ return "tests/files/";
+ }
+ return exec.substr(0, value + 1) + "tests/files/";
+}
+
+template <typename Ehdr, typename Shdr>
+void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine, bool init_gnu_debugdata,
+ TestCopyFuncType copy_func) {
+ Ehdr ehdr;
+
+ TestInitEhdr(&ehdr, elf_class, machine);
+
+ uint64_t offset = sizeof(Ehdr);
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ copy_func(0, &ehdr, sizeof(ehdr));
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ copy_func(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // Skip this header, it will contain the gnu_debugdata information.
+ uint64_t gnu_offset = offset;
+ offset += ehdr.e_shentsize;
+
+ uint64_t symtab_offset = sizeof(ehdr) + ehdr.e_shnum * ehdr.e_shentsize;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_name = 1;
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_offset = symtab_offset;
+ shdr.sh_size = 0x100;
+ copy_func(offset, &shdr, sizeof(shdr));
+
+ char value = '\0';
+ uint64_t symname_offset = symtab_offset;
+ copy_func(symname_offset, &value, 1);
+ symname_offset++;
+ std::string name(".shstrtab");
+ copy_func(symname_offset, name.c_str(), name.size() + 1);
+ symname_offset += name.size() + 1;
+ name = ".gnu_debugdata";
+ copy_func(symname_offset, name.c_str(), name.size() + 1);
+
+ ssize_t bytes = 0x100;
+ offset = symtab_offset + 0x100;
+ if (init_gnu_debugdata) {
+ // Read in the compressed elf data and copy it in.
+ name = GetTestFileDirectory();
+ if (elf_class == ELFCLASS32) {
+ name += "elf32.xz";
+ } else {
+ name += "elf64.xz";
+ }
+ int fd = TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY));
+ ASSERT_NE(-1, fd) << "Cannot open " + name;
+ // Assumes the file is less than 1024 bytes.
+ std::vector<uint8_t> buf(1024);
+ bytes = TEMP_FAILURE_RETRY(read(fd, buf.data(), buf.size()));
+ ASSERT_GT(bytes, 0);
+ // Make sure the file isn't too big.
+ ASSERT_NE(static_cast<size_t>(bytes), buf.size())
+ << "File " + name + " is too big, increase buffer size.";
+ close(fd);
+ buf.resize(bytes);
+ copy_func(offset, buf.data(), buf.size());
+ }
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = symname_offset - symtab_offset;
+ shdr.sh_addr = offset;
+ shdr.sh_offset = offset;
+ shdr.sh_size = bytes;
+ copy_func(gnu_offset, &shdr, sizeof(shdr));
+}
+
+template void TestInitEhdr<Elf32_Ehdr>(Elf32_Ehdr*, uint32_t, uint32_t);
+template void TestInitEhdr<Elf64_Ehdr>(Elf64_Ehdr*, uint32_t, uint32_t);
+
+template void TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(uint32_t, uint32_t, bool,
+ TestCopyFuncType);
+template void TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(uint32_t, uint32_t, bool,
+ TestCopyFuncType);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h
new file mode 100644
index 0000000..6ef00e1
--- /dev/null
+++ b/libunwindstack/tests/ElfTestUtils.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
+#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
+
+#include <functional>
+
+namespace unwindstack {
+
+typedef std::function<void(uint64_t, const void*, size_t)> TestCopyFuncType;
+
+template <typename Ehdr>
+void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type);
+
+template <typename Ehdr, typename Shdr>
+void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata,
+ TestCopyFuncType copy_func);
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
diff --git a/libunwindstack/tests/GenGnuDebugdata.cpp b/libunwindstack/tests/GenGnuDebugdata.cpp
new file mode 100644
index 0000000..2644582
--- /dev/null
+++ b/libunwindstack/tests/GenGnuDebugdata.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#if !defined(EM_AARCH64)
+#define EM_AARCH64 183
+#endif
+
+template <typename Ehdr>
+void InitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine) {
+ memset(ehdr, 0, sizeof(Ehdr));
+ memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
+ ehdr->e_ident[EI_CLASS] = elf_class;
+ ehdr->e_type = ET_DYN;
+ ehdr->e_machine = machine;
+ ehdr->e_version = EV_CURRENT;
+ ehdr->e_ehsize = sizeof(Ehdr);
+}
+
+template <typename Ehdr, typename Shdr>
+void GenElf(Ehdr* ehdr, int fd) {
+ uint64_t offset = sizeof(Ehdr);
+ ehdr->e_shoff = offset;
+ ehdr->e_shnum = 3;
+ ehdr->e_shentsize = sizeof(Shdr);
+ ehdr->e_shstrndx = 2;
+ TEMP_FAILURE_RETRY(write(fd, ehdr, sizeof(Ehdr)));
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_name = 0;
+ shdr.sh_type = SHT_NULL;
+ TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+ offset += ehdr->e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 11;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x800;
+ TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+ offset += ehdr->e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x200;
+ shdr.sh_size = 24;
+ TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+
+ // Write out the name entries information.
+ lseek(fd, 0x200, SEEK_SET);
+ std::string name;
+ TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+ name = ".shstrtab";
+ TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+ name = ".debug_frame";
+ TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+}
+
+int main() {
+ int elf32_fd = TEMP_FAILURE_RETRY(open("elf32", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+ if (elf32_fd == -1) {
+ printf("Failed to create elf32: %s\n", strerror(errno));
+ return 1;
+ }
+
+ int elf64_fd = TEMP_FAILURE_RETRY(open("elf64", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+ if (elf64_fd == -1) {
+ printf("Failed to create elf64: %s\n", strerror(errno));
+ return 1;
+ }
+
+ Elf32_Ehdr ehdr32;
+ InitEhdr<Elf32_Ehdr>(&ehdr32, ELFCLASS32, EM_ARM);
+ GenElf<Elf32_Ehdr, Elf32_Shdr>(&ehdr32, elf32_fd);
+ close(elf32_fd);
+
+ Elf64_Ehdr ehdr64;
+ InitEhdr<Elf64_Ehdr>(&ehdr64, ELFCLASS64, EM_AARCH64);
+ GenElf<Elf64_Ehdr, Elf64_Shdr>(&ehdr64, elf64_fd);
+ close(elf64_fd);
+}
diff --git a/libunwindstack/tests/LogFake.cpp b/libunwindstack/tests/LogFake.cpp
index 411594a..537ccaf 100644
--- a/libunwindstack/tests/LogFake.cpp
+++ b/libunwindstack/tests/LogFake.cpp
@@ -25,7 +25,6 @@
#include "LogFake.h"
// Forward declarations.
-class Backtrace;
struct EventTagMap;
struct AndroidLogEntry;
@@ -33,6 +32,8 @@
std::string g_fake_log_print;
+namespace unwindstack {
+
void ResetLogs() {
g_fake_log_buf = "";
g_fake_log_print = "";
@@ -46,6 +47,8 @@
return g_fake_log_print;
}
+} // namespace unwindstack
+
extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
g_fake_log_buf += tag;
diff --git a/libunwindstack/tests/LogFake.h b/libunwindstack/tests/LogFake.h
index 006d393..e1dc50d 100644
--- a/libunwindstack/tests/LogFake.h
+++ b/libunwindstack/tests/LogFake.h
@@ -19,8 +19,12 @@
#include <string>
+namespace unwindstack {
+
void ResetLogs();
std::string GetFakeLogBuf();
std::string GetFakeLogPrint();
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
similarity index 83%
rename from libunwindstack/tests/MapInfoTest.cpp
rename to libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index c846ad7..9e45e78 100644
--- a/libunwindstack/tests/MapInfoTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -30,11 +30,13 @@
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
-#include "Elf.h"
-#include "MapInfo.h"
-#include "Memory.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
-class MapInfoTest : public ::testing::Test {
+namespace unwindstack {
+
+class MapInfoCreateMemoryTest : public ::testing::Test {
protected:
static void SetUpTestCase() {
std::vector<uint8_t> buffer(1024);
@@ -58,10 +60,10 @@
static TemporaryFile elf_at_100_;
};
-TemporaryFile MapInfoTest::elf_;
-TemporaryFile MapInfoTest::elf_at_100_;
+TemporaryFile MapInfoCreateMemoryTest::elf_;
+TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
-TEST_F(MapInfoTest, end_le_start) {
+TEST_F(MapInfoCreateMemoryTest, end_le_start) {
MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
std::unique_ptr<Memory> memory;
@@ -75,12 +77,12 @@
// Make sure this test is valid.
info.end = 0x101;
memory.reset(info.CreateMemory(getpid()));
- ASSERT_FALSE(info.CreateMemory(getpid()) == nullptr);
+ ASSERT_TRUE(memory.get() != nullptr);
}
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
-TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_full_file) {
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
@@ -100,7 +102,7 @@
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
-TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_partial_file) {
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
@@ -119,7 +121,7 @@
}
// Verify that device file names will never result in Memory object creation.
-TEST_F(MapInfoTest, create_memory_check_device_maps) {
+TEST_F(MapInfoCreateMemoryTest, check_device_maps) {
// Set up some memory so that a valid local memory object would
// be returned if the file mapping fails, but the device check is incorrect.
std::vector<uint8_t> buffer(1024);
@@ -135,7 +137,7 @@
ASSERT_TRUE(memory.get() == nullptr);
}
-TEST_F(MapInfoTest, create_memory_local_memory) {
+TEST_F(MapInfoCreateMemoryTest, local_memory) {
// Set up some memory for a valid local memory object.
std::vector<uint8_t> buffer(1024);
for (size_t i = 0; i < buffer.size(); i++) {
@@ -160,7 +162,7 @@
ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1));
}
-TEST_F(MapInfoTest, create_memory_remote_memory) {
+TEST_F(MapInfoCreateMemoryTest, remote_memory) {
std::vector<uint8_t> buffer(1024);
memset(buffer.data(), 0xa, buffer.size());
@@ -201,20 +203,7 @@
ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
kill(pid, SIGKILL);
+ ASSERT_EQ(pid, wait(nullptr));
}
-TEST_F(MapInfoTest, get_elf) {
- // Create a map to use as initialization data.
- void* map = mmap(nullptr, 1024, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- ASSERT_NE(MAP_FAILED, map);
-
- uint64_t start = reinterpret_cast<uint64_t>(map);
- MapInfo info{.start = start, .end = start + 1024, .offset = 0, .name = ""};
-
- // The map contains garbage, but this should still produce an elf object.
- Elf* elf = info.GetElf(getpid(), false);
- ASSERT_TRUE(elf != nullptr);
- ASSERT_FALSE(elf->valid());
-
- ASSERT_EQ(0, munmap(map, 1024));
-}
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
new file mode 100644
index 0000000..abfa172
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfTestUtils.h"
+
+namespace unwindstack {
+
+class MapInfoGetElfTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ map_ = mmap(nullptr, kMapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(MAP_FAILED, map_);
+
+ uint64_t start = reinterpret_cast<uint64_t>(map_);
+ info_.reset(new MapInfo{.start = start, .end = start + 1024, .offset = 0, .name = ""});
+ }
+
+ void TearDown() override { munmap(map_, kMapSize); }
+
+ const size_t kMapSize = 4096;
+
+ void* map_ = nullptr;
+ std::unique_ptr<MapInfo> info_;
+};
+
+TEST_F(MapInfoGetElfTest, invalid) {
+ // The map is empty, but this should still create an invalid elf object.
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_FALSE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, valid32) {
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memcpy(map_, &ehdr, sizeof(ehdr));
+
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+ EXPECT_EQ(ELFCLASS32, elf->class_type());
+}
+
+TEST_F(MapInfoGetElfTest, valid64) {
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+ memcpy(map_, &ehdr, sizeof(ehdr));
+
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+ EXPECT_EQ(ELFCLASS64, elf->class_type());
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(
+ ELFCLASS32, EM_ARM, false, [&](uint64_t offset, const void* ptr, size_t size) {
+ memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+ EXPECT_EQ(ELFCLASS32, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(
+ ELFCLASS64, EM_AARCH64, false, [&](uint64_t offset, const void* ptr, size_t size) {
+ memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+ EXPECT_EQ(ELFCLASS64, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(
+ ELFCLASS32, EM_ARM, true, [&](uint64_t offset, const void* ptr, size_t size) {
+ memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+ EXPECT_EQ(ELFCLASS32, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(
+ ELFCLASS64, EM_AARCH64, true, [&](uint64_t offset, const void* ptr, size_t size) {
+ memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+ EXPECT_EQ(ELFCLASS64, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 7eb9bae..9430cf3 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -20,7 +20,9 @@
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
-#include "Maps.h"
+#include <unwindstack/Maps.h>
+
+namespace unwindstack {
TEST(MapsTest, parse_permissions) {
BufferMaps maps(
@@ -235,3 +237,5 @@
ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
ASSERT_EQ("/system/lib/fake5.so", info->name);
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryBuffer.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
similarity index 96%
rename from libunwindstack/tests/MemoryBuffer.cpp
rename to libunwindstack/tests/MemoryBufferTest.cpp
index af3d6b9..50a8a1b 100644
--- a/libunwindstack/tests/MemoryBuffer.cpp
+++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -18,10 +18,12 @@
#include <gtest/gtest.h>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
#include "LogFake.h"
+namespace unwindstack {
+
class MemoryBufferTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -75,3 +77,5 @@
ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200));
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
index afb1029..2026acc 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -21,6 +21,8 @@
#include "MemoryFake.h"
+namespace unwindstack {
+
void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
for (size_t i = 0; i < length; i++, addr++) {
@@ -44,3 +46,5 @@
}
return true;
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index 70ef30a..d374261 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -23,7 +23,9 @@
#include <vector>
#include <unordered_map>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
class MemoryFake : public Memory {
public:
@@ -87,4 +89,6 @@
}
};
+} // namespace unwindstack
+
#endif // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index aa7a23a..a204bae 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -21,7 +21,9 @@
#include <android-base/file.h>
#include <gtest/gtest.h>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
class MemoryFileTest : public ::testing::Test {
protected:
@@ -269,3 +271,5 @@
ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i;
}
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index ab999da..73eebdd 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -21,7 +21,9 @@
#include <gtest/gtest.h>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
TEST(MemoryLocalTest, read) {
std::vector<uint8_t> src(1024);
@@ -64,3 +66,5 @@
uint64_t value;
ASSERT_FALSE(local.Read(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX));
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index ee5ba01..6d1366c 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -22,25 +22,19 @@
#include <gtest/gtest.h>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
#include "MemoryFake.h"
-class MemoryRangeTest : public ::testing::Test {
- protected:
- void SetUp() override {
- memory_ = new MemoryFake;
- }
+namespace unwindstack {
- MemoryFake* memory_;
-};
-
-TEST_F(MemoryRangeTest, read) {
+TEST(MemoryRangeTest, read) {
std::vector<uint8_t> src(1024);
memset(src.data(), 0x4c, 1024);
- memory_->SetMemory(9001, src);
+ MemoryFake* memory = new MemoryFake;
+ memory->SetMemory(9001, src);
- MemoryRange range(memory_, 9001, 9001 + src.size());
+ MemoryRange range(memory, 9001, 9001 + src.size());
std::vector<uint8_t> dst(1024);
ASSERT_TRUE(range.Read(0, dst.data(), src.size()));
@@ -49,12 +43,13 @@
}
}
-TEST_F(MemoryRangeTest, read_near_limit) {
+TEST(MemoryRangeTest, read_near_limit) {
std::vector<uint8_t> src(4096);
memset(src.data(), 0x4c, 4096);
- memory_->SetMemory(1000, src);
+ MemoryFake* memory = new MemoryFake;
+ memory->SetMemory(1000, src);
- MemoryRange range(memory_, 1000, 2024);
+ MemoryRange range(memory, 1000, 2024);
std::vector<uint8_t> dst(1024);
ASSERT_TRUE(range.Read(1020, dst.data(), 4));
@@ -71,9 +66,11 @@
ASSERT_TRUE(range.Read(1020, dst.data(), 4));
}
-TEST_F(MemoryRangeTest, read_overflow) {
+TEST(MemoryRangeTest, read_overflow) {
std::vector<uint8_t> buffer(100);
std::unique_ptr<MemoryRange> overflow(new MemoryRange(new MemoryFakeAlwaysReadZero, 100, 200));
ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100));
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index e48edf7..f8965b2 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -31,10 +31,12 @@
#include <android-base/file.h>
#include <gtest/gtest.h>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
#include "MemoryFake.h"
+namespace unwindstack {
+
class MemoryRemoteTest : public ::testing::Test {
protected:
static uint64_t NanoTime() {
@@ -91,6 +93,7 @@
ASSERT_TRUE(Detach(pid));
kill(pid, SIGKILL);
+ ASSERT_EQ(pid, wait(nullptr));
}
TEST_F(MemoryRemoteTest, read_fail) {
@@ -131,6 +134,7 @@
ASSERT_TRUE(Detach(pid));
kill(pid, SIGKILL);
+ ASSERT_EQ(pid, wait(nullptr));
}
TEST_F(MemoryRemoteTest, read_overflow) {
@@ -160,4 +164,7 @@
ASSERT_TRUE(Detach(pid));
kill(pid, SIGKILL);
+ ASSERT_EQ(pid, wait(nullptr));
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 51b5d7d..4a9ed9f 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -22,10 +22,12 @@
#include <gtest/gtest.h>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
#include "MemoryFake.h"
+namespace unwindstack {
+
TEST(MemoryTest, read32) {
MemoryFakeAlwaysReadZero memory;
@@ -124,3 +126,5 @@
ASSERT_TRUE(memory.ReadString(0, &dst_name));
ASSERT_EQ("short", dst_name);
}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
new file mode 100644
index 0000000..e796c9b
--- /dev/null
+++ b/libunwindstack/tests/RegsFake.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class RegsFake : public RegsImpl<TypeParam> {
+ public:
+ RegsFake(uint16_t total_regs, uint16_t sp_reg)
+ : RegsImpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+ virtual ~RegsFake() = default;
+
+ uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+ void SetFromRaw() override {}
+ bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 0dac278..3613689 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -18,13 +18,15 @@
#include <gtest/gtest.h>
-#include "Elf.h"
-#include "ElfInterface.h"
-#include "MapInfo.h"
-#include "Regs.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Regs.h>
#include "MemoryFake.h"
+namespace unwindstack {
+
class ElfFake : public Elf {
public:
ElfFake(Memory* memory) : Elf(memory) { valid_ = true; }
@@ -48,15 +50,16 @@
};
template <typename TypeParam>
-class RegsTestTmpl : public RegsTmpl<TypeParam> {
+class RegsTestImpl : public RegsImpl<TypeParam> {
public:
- RegsTestTmpl(uint16_t total_regs, uint16_t regs_sp)
- : RegsTmpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
- RegsTestTmpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
- : RegsTmpl<TypeParam>(total_regs, regs_sp, return_loc) {}
- virtual ~RegsTestTmpl() = default;
+ RegsTestImpl(uint16_t total_regs, uint16_t regs_sp)
+ : RegsImpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+ RegsTestImpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
+ : RegsImpl<TypeParam>(total_regs, regs_sp, return_loc) {}
+ virtual ~RegsTestImpl() = default;
- uint64_t GetAdjustedPc(uint64_t, Elf*) { return 0; }
+ uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+ void SetFromRaw() override {}
};
class RegsTest : public ::testing::Test {
@@ -69,9 +72,6 @@
}
template <typename AddressType>
- void regs_rel_pc();
-
- template <typename AddressType>
void regs_return_address_register();
ElfInterfaceFake* elf_interface_;
@@ -80,7 +80,7 @@
};
TEST_F(RegsTest, regs32) {
- RegsTestTmpl<uint32_t> regs32(50, 10);
+ RegsTestImpl<uint32_t> regs32(50, 10);
ASSERT_EQ(50U, regs32.total_regs());
ASSERT_EQ(10U, regs32.sp_reg());
@@ -103,7 +103,7 @@
}
TEST_F(RegsTest, regs64) {
- RegsTestTmpl<uint64_t> regs64(30, 12);
+ RegsTestImpl<uint64_t> regs64(30, 12);
ASSERT_EQ(30U, regs64.total_regs());
ASSERT_EQ(12U, regs64.sp_reg());
@@ -126,28 +126,8 @@
}
template <typename AddressType>
-void RegsTest::regs_rel_pc() {
- RegsTestTmpl<AddressType> regs(30, 12);
-
- elf_interface_->set_load_bias(0);
- MapInfo map_info{.start = 0x1000, .end = 0x2000};
- regs.set_pc(0x1101);
- ASSERT_EQ(0x101U, regs.GetRelPc(elf_.get(), &map_info));
- elf_interface_->set_load_bias(0x3000);
- ASSERT_EQ(0x3101U, regs.GetRelPc(elf_.get(), &map_info));
-}
-
-TEST_F(RegsTest, regs32_rel_pc) {
- regs_rel_pc<uint32_t>();
-}
-
-TEST_F(RegsTest, regs64_rel_pc) {
- regs_rel_pc<uint64_t>();
-}
-
-template <typename AddressType>
void RegsTest::regs_return_address_register() {
- RegsTestTmpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
+ RegsTestImpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
regs[5] = 0x12345;
uint64_t value;
@@ -164,7 +144,7 @@
}
TEST_F(RegsTest, regs32_return_address_sp_offset) {
- RegsTestTmpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
+ RegsTestImpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
regs.set_sp(0x2002);
memory_->SetData32(0x2000, 0x12345678);
@@ -174,7 +154,7 @@
}
TEST_F(RegsTest, regs64_return_address_sp_offset) {
- RegsTestTmpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
+ RegsTestImpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
regs.set_sp(0x2008);
memory_->SetData64(0x2000, 0x12345678aabbccddULL);
@@ -249,18 +229,60 @@
MapInfo map_info{.start = 0x1000, .end = 0x2000};
regs_arm.set_pc(0x1500);
- ASSERT_EQ(0x500U, regs_arm.GetRelPc(&invalid_elf, &map_info));
+ ASSERT_EQ(0x500U, invalid_elf.GetRelPc(regs_arm.pc(), &map_info));
ASSERT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, &invalid_elf));
regs_arm64.set_pc(0x1600);
- ASSERT_EQ(0x600U, regs_arm64.GetRelPc(&invalid_elf, &map_info));
+ ASSERT_EQ(0x600U, invalid_elf.GetRelPc(regs_arm64.pc(), &map_info));
ASSERT_EQ(0x600U, regs_arm64.GetAdjustedPc(0x600U, &invalid_elf));
regs_x86.set_pc(0x1700);
- ASSERT_EQ(0x700U, regs_x86.GetRelPc(&invalid_elf, &map_info));
+ ASSERT_EQ(0x700U, invalid_elf.GetRelPc(regs_x86.pc(), &map_info));
ASSERT_EQ(0x700U, regs_x86.GetAdjustedPc(0x700U, &invalid_elf));
regs_x86_64.set_pc(0x1800);
- ASSERT_EQ(0x800U, regs_x86_64.GetRelPc(&invalid_elf, &map_info));
+ ASSERT_EQ(0x800U, invalid_elf.GetRelPc(regs_x86_64.pc(), &map_info));
ASSERT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, &invalid_elf));
}
+
+TEST_F(RegsTest, arm_set_from_raw) {
+ RegsArm arm;
+ uint32_t* regs = reinterpret_cast<uint32_t*>(arm.RawData());
+ regs[13] = 0x100;
+ regs[15] = 0x200;
+ arm.SetFromRaw();
+ EXPECT_EQ(0x100U, arm.sp());
+ EXPECT_EQ(0x200U, arm.pc());
+}
+
+TEST_F(RegsTest, arm64_set_from_raw) {
+ RegsArm64 arm64;
+ uint64_t* regs = reinterpret_cast<uint64_t*>(arm64.RawData());
+ regs[31] = 0xb100000000ULL;
+ regs[32] = 0xc200000000ULL;
+ arm64.SetFromRaw();
+ EXPECT_EQ(0xb100000000U, arm64.sp());
+ EXPECT_EQ(0xc200000000U, arm64.pc());
+}
+
+TEST_F(RegsTest, x86_set_from_raw) {
+ RegsX86 x86;
+ uint32_t* regs = reinterpret_cast<uint32_t*>(x86.RawData());
+ regs[4] = 0x23450000;
+ regs[8] = 0xabcd0000;
+ x86.SetFromRaw();
+ EXPECT_EQ(0x23450000U, x86.sp());
+ EXPECT_EQ(0xabcd0000U, x86.pc());
+}
+
+TEST_F(RegsTest, x86_64_set_from_raw) {
+ RegsX86_64 x86_64;
+ uint64_t* regs = reinterpret_cast<uint64_t*>(x86_64.RawData());
+ regs[7] = 0x1200000000ULL;
+ regs[16] = 0x4900000000ULL;
+ x86_64.SetFromRaw();
+ EXPECT_EQ(0x1200000000U, x86_64.sp());
+ EXPECT_EQ(0x4900000000U, x86_64.pc());
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index a0a21e6..da258a6 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -32,10 +32,13 @@
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
#include "MemoryFake.h"
#include "Symbols.h"
+namespace unwindstack {
+
template <typename TypeParam>
class SymbolsTest : public ::testing::Test {
protected:
@@ -333,3 +336,5 @@
typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
new file mode 100644
index 0000000..72065c9
--- /dev/null
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <signal.h>
+#include <stdint.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <atomic>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+namespace unwindstack {
+
+static std::atomic_bool g_ready(false);
+static volatile bool g_ready_for_remote = false;
+static std::atomic_bool g_finish(false);
+static std::atomic_uintptr_t g_ucontext;
+
+static void Signal(int, siginfo_t*, void* sigcontext) {
+ g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
+ while (!g_finish.load()) {
+ }
+}
+
+static std::string ErrorMsg(const char** function_names, size_t index,
+ std::stringstream& unwind_stream) {
+ return std::string(
+ "Unwind completed without finding all frames\n"
+ " Looking for function: ") +
+ function_names[index] + "\n" + "Unwind data:\n" + unwind_stream.str();
+}
+
+static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs) {
+ const char* function_names[] = {
+ "InnerFunction", "MiddleFunction", "OuterFunction",
+ };
+ size_t function_name_index = 0;
+
+ std::stringstream unwind_stream;
+ unwind_stream << std::hex;
+ for (size_t frame_num = 0; frame_num < 64; frame_num++) {
+ ASSERT_NE(0U, regs->pc()) << ErrorMsg(function_names, function_name_index, unwind_stream);
+ MapInfo* map_info = maps->Find(regs->pc());
+ ASSERT_TRUE(map_info != nullptr) << ErrorMsg(function_names, function_name_index, unwind_stream);
+
+ Elf* elf = map_info->GetElf(pid, true);
+ uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
+ uint64_t adjusted_rel_pc = rel_pc;
+ if (frame_num != 0) {
+ adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
+ }
+ unwind_stream << " PC: 0x" << regs->pc() << " Rel: 0x" << adjusted_rel_pc;
+ unwind_stream << " Map: ";
+ if (!map_info->name.empty()) {
+ unwind_stream << map_info->name;
+ } else {
+ unwind_stream << " anonymous";
+ }
+ unwind_stream << "<" << map_info->start << "-" << map_info->end << ">";
+
+ std::string name;
+ uint64_t func_offset;
+ if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
+ if (name == function_names[function_name_index]) {
+ function_name_index++;
+ if (function_name_index == sizeof(function_names) / sizeof(const char*)) {
+ return;
+ }
+ }
+ unwind_stream << " " << name;
+ }
+ unwind_stream << "\n";
+ ASSERT_TRUE(elf->Step(rel_pc + map_info->elf_offset, regs, memory))
+ << ErrorMsg(function_names, function_name_index, unwind_stream);
+ }
+ ASSERT_TRUE(false) << ErrorMsg(function_names, function_name_index, unwind_stream);
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void InnerFunction(bool local) {
+ if (local) {
+ LocalMaps maps;
+ ASSERT_TRUE(maps.Parse());
+ std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+ RegsGetLocal(regs.get());
+ MemoryLocal memory;
+
+ VerifyUnwind(getpid(), &memory, &maps, regs.get());
+ } else {
+ g_ready_for_remote = true;
+ g_ready = true;
+ while (!g_finish.load()) {
+ }
+ }
+}
+
+extern "C" void MiddleFunction(bool local) {
+ InnerFunction(local);
+}
+
+extern "C" void OuterFunction(bool local) {
+ MiddleFunction(local);
+}
+
+TEST(UnwindTest, local) {
+ OuterFunction(true);
+}
+
+TEST(UnwindTest, remote) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ OuterFunction(false);
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+
+ bool ready = false;
+ uint64_t addr = reinterpret_cast<uint64_t>(&g_ready_for_remote);
+ for (size_t i = 0; i < 100; i++) {
+ ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0));
+ for (size_t j = 0; j < 100; j++) {
+ siginfo_t si;
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ // Check to see if process is ready to be unwound.
+ MemoryRemote memory(pid);
+ // Read the remote value to see if we are ready.
+ bool value;
+ if (memory.Read(addr, &value, sizeof(value)) && value) {
+ ready = true;
+ break;
+ }
+ }
+ usleep(1000);
+ }
+ if (ready) {
+ break;
+ }
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+ usleep(1000);
+ }
+ ASSERT_TRUE(read) << "Timed out waiting for remote process to be ready.";
+
+ RemoteMaps maps(pid);
+ ASSERT_TRUE(maps.Parse());
+ MemoryRemote memory(pid);
+ uint32_t machine_type;
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(pid, &machine_type));
+ ASSERT_TRUE(regs.get() != nullptr);
+
+ VerifyUnwind(pid, &memory, &maps, regs.get());
+
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(pid, wait(nullptr));
+}
+
+TEST(UnwindTest, from_context) {
+ std::atomic_int tid(0);
+ std::thread thread([&]() {
+ tid = syscall(__NR_gettid);
+ OuterFunction(false);
+ });
+
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = Signal;
+ act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+ // Wait for the tid to get set.
+ for (size_t i = 0; i < 100; i++) {
+ if (tid.load() != 0) {
+ break;
+ }
+ usleep(1000);
+ }
+ ASSERT_NE(0, tid.load());
+ // Portable tgkill method.
+ ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Failed because "
+ << strerror(errno);
+
+ // Wait for context data.
+ void* ucontext;
+ for (size_t i = 0; i < 2000; i++) {
+ ucontext = reinterpret_cast<void*>(g_ucontext.load());
+ if (ucontext != nullptr) {
+ break;
+ }
+ usleep(1000);
+ }
+ ASSERT_TRUE(ucontext != nullptr) << "Timed out waiting for thread to respond to signal.";
+
+ LocalMaps maps;
+ ASSERT_TRUE(maps.Parse());
+ std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::GetMachineType(), ucontext));
+ MemoryLocal memory;
+
+ VerifyUnwind(tid.load(), &memory, &maps, regs.get());
+
+ ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+ g_finish = true;
+ thread.join();
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/files/elf32.xz b/libunwindstack/tests/files/elf32.xz
new file mode 100644
index 0000000..f25d433
--- /dev/null
+++ b/libunwindstack/tests/files/elf32.xz
Binary files differ
diff --git a/libunwindstack/tests/files/elf64.xz b/libunwindstack/tests/files/elf64.xz
new file mode 100644
index 0000000..eb1618e
--- /dev/null
+++ b/libunwindstack/tests/files/elf64.xz
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
new file mode 100644
index 0000000..642105a
--- /dev/null
+++ b/libunwindstack/tools/unwind.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+static bool Attach(pid_t pid) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+ return false;
+ }
+
+ // Allow at least 1 second to attach properly.
+ for (size_t i = 0; i < 1000; i++) {
+ siginfo_t si;
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ return true;
+ }
+ usleep(1000);
+ }
+ printf("%d: Failed to stop.\n", pid);
+ return false;
+}
+
+static bool Detach(pid_t pid) {
+ return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
+}
+
+void DoUnwind(pid_t pid) {
+ unwindstack::RemoteMaps remote_maps(pid);
+ if (!remote_maps.Parse()) {
+ printf("Failed to parse map data.\n");
+ return;
+ }
+
+ uint32_t machine_type;
+ unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid, &machine_type);
+ if (regs == nullptr) {
+ printf("Unable to get remote reg data\n");
+ return;
+ }
+
+ bool bits32 = true;
+ printf("ABI: ");
+ switch (machine_type) {
+ case EM_ARM:
+ printf("arm");
+ break;
+ case EM_386:
+ printf("x86");
+ break;
+ case EM_AARCH64:
+ printf("arm64");
+ bits32 = false;
+ break;
+ case EM_X86_64:
+ printf("x86_64");
+ bits32 = false;
+ break;
+ default:
+ printf("unknown\n");
+ return;
+ }
+ printf("\n");
+
+ unwindstack::MemoryRemote remote_memory(pid);
+ for (size_t frame_num = 0; frame_num < 64; frame_num++) {
+ if (regs->pc() == 0) {
+ break;
+ }
+ unwindstack::MapInfo* map_info = remote_maps.Find(regs->pc());
+ if (map_info == nullptr) {
+ printf("Failed to find map data for the pc\n");
+ break;
+ }
+
+ unwindstack::Elf* elf = map_info->GetElf(pid, true);
+
+ uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
+ uint64_t adjusted_rel_pc = rel_pc;
+ // Don't need to adjust the first frame pc.
+ if (frame_num != 0) {
+ adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
+ }
+
+ std::string name;
+ if (bits32) {
+ printf(" #%02zu pc %08" PRIx64, frame_num, adjusted_rel_pc);
+ } else {
+ printf(" #%02zu pc %016" PRIx64, frame_num, adjusted_rel_pc);
+ }
+ if (!map_info->name.empty()) {
+ printf(" %s", map_info->name.c_str());
+ if (map_info->elf_offset != 0) {
+ printf(" (offset 0x%" PRIx64 ")", map_info->elf_offset);
+ }
+ } else {
+ printf(" <anonymous:%" PRIx64 ">", map_info->offset);
+ }
+ uint64_t func_offset;
+ if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
+ printf(" (%s", name.c_str());
+ if (func_offset != 0) {
+ printf("+%" PRId64, func_offset);
+ }
+ printf(")");
+ }
+ printf("\n");
+
+ if (!elf->Step(rel_pc + map_info->elf_offset, regs, &remote_memory)) {
+ break;
+ }
+ }
+}
+
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ printf("Usage: unwind <PID>\n");
+ return 1;
+ }
+
+ pid_t pid = atoi(argv[1]);
+ if (!Attach(pid)) {
+ printf("Failed to attach to pid %d: %s\n", pid, strerror(errno));
+ return 1;
+ }
+
+ DoUnwind(pid);
+
+ Detach(pid);
+
+ return 0;
+}
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
new file mode 100644
index 0000000..66a9439
--- /dev/null
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+
+#include "ArmExidx.h"
+#include "ElfInterfaceArm.h"
+
+namespace unwindstack {
+
+void DumpArm(ElfInterfaceArm* interface) {
+ if (interface == nullptr) {
+ printf("No ARM Unwind Information.\n\n");
+ return;
+ }
+
+ printf("ARM Unwind Information:\n");
+ for (const auto& entry : interface->pt_loads()) {
+ uint64_t load_bias = entry.second.table_offset;
+ printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
+ entry.second.table_size + load_bias);
+ for (auto addr : *interface) {
+ std::string name;
+ printf(" PC 0x%" PRIx64, addr + load_bias);
+ uint64_t func_offset;
+ uint64_t pc = addr + load_bias;
+ // This might be a thumb function, so set the low bit.
+ if (interface->GetFunctionName(pc | 1, &name, &func_offset) && !name.empty()) {
+ printf(" <%s>", name.c_str());
+ }
+ printf("\n");
+ uint64_t entry;
+ if (!interface->FindEntry(pc, &entry)) {
+ printf(" Cannot find entry for address.\n");
+ continue;
+ }
+ ArmExidx arm(nullptr, interface->memory(), nullptr);
+ arm.set_log(true);
+ arm.set_log_skip_execution(true);
+ arm.set_log_indent(2);
+ if (!arm.ExtractEntryData(entry)) {
+ if (arm.status() != ARM_STATUS_NO_UNWIND) {
+ printf(" Error trying to extract data.\n");
+ }
+ continue;
+ }
+ if (arm.data()->size() > 0) {
+ if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) {
+ printf(" Error trying to evaluate dwarf data.\n");
+ }
+ }
+ }
+ }
+ printf("\n");
+}
+
+void DumpDwarfSection(ElfInterface* interface, DwarfSection* section, uint64_t load_bias) {
+ for (const DwarfFde* fde : *section) {
+ // Sometimes there are entries that have empty length, skip those since
+ // they don't contain any interesting information.
+ if (fde->pc_start == fde->pc_end) {
+ continue;
+ }
+ printf("\n PC 0x%" PRIx64, fde->pc_start + load_bias);
+ std::string name;
+ uint64_t func_offset;
+ if (interface->GetFunctionName(fde->pc_start + load_bias, &name, &func_offset) &&
+ !name.empty()) {
+ printf(" <%s>", name.c_str());
+ }
+ printf("\n");
+ if (!section->Log(2, UINT64_MAX, load_bias, fde)) {
+ printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
+ }
+ }
+}
+
+int GetElfInfo(const char* file) {
+ // Send all log messages to stdout.
+ log_to_stdout(true);
+
+ MemoryFileAtOffset* memory = new MemoryFileAtOffset;
+ if (!memory->Init(file, 0)) {
+ // Initializatation failed.
+ printf("Failed to init\n");
+ return 1;
+ }
+
+ Elf elf(memory);
+ if (!elf.Init() || !elf.valid()) {
+ printf("%s is not a valid elf file.\n", file);
+ return 1;
+ }
+
+ ElfInterface* interface = elf.interface();
+ if (elf.machine_type() == EM_ARM) {
+ DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
+ printf("\n");
+ }
+
+ if (interface->eh_frame() != nullptr) {
+ printf("eh_frame information:\n");
+ DumpDwarfSection(interface, interface->eh_frame(), interface->load_bias());
+ printf("\n");
+ } else {
+ printf("\nno eh_frame information\n");
+ }
+
+ if (interface->debug_frame() != nullptr) {
+ printf("\ndebug_frame information:\n");
+ DumpDwarfSection(interface, interface->debug_frame(), interface->load_bias());
+ printf("\n");
+ } else {
+ printf("\nno debug_frame information\n");
+ }
+
+ // If there is a gnu_debugdata interface, dump the information for that.
+ ElfInterface* gnu_debugdata_interface = elf.gnu_debugdata_interface();
+ if (gnu_debugdata_interface != nullptr) {
+ if (gnu_debugdata_interface->eh_frame() != nullptr) {
+ printf("\ngnu_debugdata (eh_frame):\n");
+ DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->eh_frame(), 0);
+ printf("\n");
+ }
+ if (gnu_debugdata_interface->debug_frame() != nullptr) {
+ printf("\ngnu_debugdata (debug_frame):\n");
+ DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->debug_frame(), 0);
+ printf("\n");
+ }
+ } else {
+ printf("\nno valid gnu_debugdata information\n");
+ }
+
+ return 0;
+}
+
+} // namespace unwindstack
+
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ printf("Need to pass the name of an elf file to the program.\n");
+ return 1;
+ }
+
+ struct stat st;
+ if (stat(argv[1], &st) == -1) {
+ printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ printf("%s is not a regular file.\n", argv[1]);
+ return 1;
+ }
+
+ return unwindstack::GetElfInfo(argv[1]);
+}
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
new file mode 100644
index 0000000..b757c1e
--- /dev/null
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ printf("Need to pass the name of an elf file to the program.\n");
+ return 1;
+ }
+
+ struct stat st;
+ if (stat(argv[1], &st) == -1) {
+ printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ printf("%s is not a regular file.\n", argv[1]);
+ return 1;
+ }
+
+ // Send all log messages to stdout.
+ unwindstack::log_to_stdout(true);
+
+ unwindstack::MemoryFileAtOffset* memory = new unwindstack::MemoryFileAtOffset;
+ if (!memory->Init(argv[1], 0)) {
+ printf("Failed to init\n");
+ return 1;
+ }
+
+ unwindstack::Elf elf(memory);
+ if (!elf.Init() || !elf.valid()) {
+ printf("%s is not a valid elf file.\n", argv[1]);
+ return 1;
+ }
+
+ switch (elf.machine_type()) {
+ case EM_ARM:
+ printf("ABI: arm\n");
+ break;
+ case EM_AARCH64:
+ printf("ABI: arm64\n");
+ break;
+ case EM_386:
+ printf("ABI: x86\n");
+ break;
+ case EM_X86_64:
+ printf("ABI: x86_64\n");
+ break;
+ default:
+ printf("ABI: unknown\n");
+ return 1;
+ }
+
+ // This is a crude way to get the symbols in order.
+ std::string name;
+ uint64_t load_bias = elf.interface()->load_bias();
+ for (const auto& entry : elf.interface()->pt_loads()) {
+ uint64_t start = entry.second.offset + load_bias;
+ uint64_t end = entry.second.table_size + load_bias;
+ for (uint64_t addr = start; addr < end; addr += 4) {
+ std::string cur_name;
+ uint64_t func_offset;
+ if (elf.GetFunctionName(addr, &cur_name, &func_offset)) {
+ if (cur_name != name) {
+ printf("<0x%" PRIx64 "> Function: %s\n", addr - func_offset, cur_name.c_str());
+ }
+ name = cur_name;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/libunwindstack/unwind_info.cpp b/libunwindstack/unwind_info.cpp
deleted file mode 100644
index 6f158b0..0000000
--- a/libunwindstack/unwind_info.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <elf.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "ArmExidx.h"
-#include "Elf.h"
-#include "ElfInterface.h"
-#include "ElfInterfaceArm.h"
-#include "Log.h"
-
-void DumpArm(ElfInterfaceArm* interface) {
- if (interface == nullptr) {
- printf("No ARM Unwind Information.\n\n");
- return;
- }
-
- printf("ARM Unwind Information:\n");
- for (const auto& entry : interface->pt_loads()) {
- uint64_t load_bias = entry.second.table_offset;
- printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
- entry.second.table_size + load_bias);
- for (auto addr : *interface) {
- std::string name;
- printf(" PC 0x%" PRIx64, addr + load_bias);
- uint64_t func_offset;
- if (interface->GetFunctionName(addr + load_bias + 1, &name, &func_offset) && !name.empty()) {
- printf(" <%s>", name.c_str());
- }
- printf("\n");
- uint64_t entry;
- if (!interface->FindEntry(addr + load_bias, &entry)) {
- printf(" Cannot find entry for address.\n");
- continue;
- }
- ArmExidx arm(nullptr, interface->memory(), nullptr);
- arm.set_log(true);
- arm.set_log_skip_execution(true);
- arm.set_log_indent(2);
- if (!arm.ExtractEntryData(entry)) {
- if (arm.status() != ARM_STATUS_NO_UNWIND) {
- printf(" Error trying to extract data.\n");
- }
- continue;
- }
- if (arm.data()->size() > 0) {
- if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) {
- printf(" Error trying to evaluate dwarf data.\n");
- }
- }
- }
- }
- printf("\n");
-}
-
-int main(int argc, char** argv) {
- if (argc != 2) {
- printf("Need to pass the name of an elf file to the program.\n");
- return 1;
- }
-
- struct stat st;
- if (stat(argv[1], &st) == -1) {
- printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
- return 1;
- }
- if (!S_ISREG(st.st_mode)) {
- printf("%s is not a regular file.\n", argv[1]);
- return 1;
- }
- if (S_ISDIR(st.st_mode)) {
- printf("%s is a directory.\n", argv[1]);
- return 1;
- }
-
- // Send all log messages to stdout.
- log_to_stdout(true);
-
- MemoryFileAtOffset* memory = new MemoryFileAtOffset;
- if (!memory->Init(argv[1], 0)) {
- // Initializatation failed.
- printf("Failed to init\n");
- return 1;
- }
-
- Elf elf(memory);
- if (!elf.Init() || !elf.valid()) {
- printf("%s is not a valid elf file.\n", argv[1]);
- return 1;
- }
-
- ElfInterface* interface = elf.interface();
- if (elf.machine_type() == EM_ARM) {
- DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
- printf("\n");
- }
-
- return 0;
-}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 33770ba..0125eac 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -18,10 +18,12 @@
host_supported: true,
header_libs: [
+ "liblog_headers",
"libsystem_headers",
"libcutils_headers"
],
export_header_lib_headers: [
+ "liblog_headers",
"libsystem_headers",
"libcutils_headers"
],
@@ -76,6 +78,10 @@
header_libs: ["libutils_headers"],
export_header_lib_headers: ["libutils_headers"],
+ shared_libs: [
+ "liblog",
+ ],
+
arch: {
mips: {
cflags: ["-DALIGN_DOUBLE"],
@@ -97,7 +103,6 @@
"libbacktrace",
"libcutils",
"libdl",
- "liblog",
],
sanitize: {
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index 98cd2c6..84af293 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -47,9 +47,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/.clang-format b/libziparchive/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libziparchive/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 44daf36..333835c 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -50,17 +50,29 @@
"libbase",
"liblog",
],
-}
+ export_include_dirs: ["include"],
+}
cc_library {
name: "libziparchive",
host_supported: true,
- defaults: ["libziparchive_defaults", "libziparchive_flags"],
- shared_libs: ["liblog", "libbase"],
+ vendor_available: true,
+
+ defaults: [
+ "libziparchive_defaults",
+ "libziparchive_flags",
+ ],
+ shared_libs: [
+ "liblog",
+ "libbase",
+ ],
target: {
android: {
- shared_libs: ["libz", "libutils"],
+ shared_libs: [
+ "libz",
+ "libutils",
+ ],
},
host: {
static_libs: ["libutils"],
@@ -86,7 +98,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"],
}
@@ -122,3 +137,39 @@
},
},
}
+
+// Performance benchmarks.
+cc_benchmark {
+ name: "ziparchive-benchmarks",
+ defaults: ["libziparchive_flags"],
+
+ srcs: [
+ "zip_archive_benchmark.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libziparchive",
+ "libz",
+ "libutils",
+ ],
+
+ target: {
+ host: {
+ cppflags: ["-Wno-unnamed-type-template-args"],
+ },
+ },
+}
+
+cc_binary {
+ name: "unzip",
+ defaults: ["libziparchive_flags"],
+ srcs: ["unzip.cpp"],
+ shared_libs: [
+ "libbase",
+ "libziparchive",
+ ],
+}
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
index ddbc286..5fc2fb4 100644
--- a/libziparchive/entry_name_utils-inl.h
+++ b/libziparchive/entry_name_utils-inl.h
@@ -55,5 +55,4 @@
return true;
}
-
#endif // LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
diff --git a/libziparchive/entry_name_utils_test.cc b/libziparchive/entry_name_utils_test.cc
index 20715bb..d83d854 100644
--- a/libziparchive/entry_name_utils_test.cc
+++ b/libziparchive/entry_name_utils_test.cc
@@ -20,44 +20,43 @@
TEST(entry_name_utils, NullChars) {
// 'A', 'R', '\0', 'S', 'E'
- const uint8_t zeroes[] = { 0x41, 0x52, 0x00, 0x53, 0x45 };
+ const uint8_t zeroes[] = {0x41, 0x52, 0x00, 0x53, 0x45};
ASSERT_FALSE(IsValidEntryName(zeroes, sizeof(zeroes)));
- const uint8_t zeroes_continuation_chars[] = { 0xc2, 0xa1, 0xc2, 0x00 };
- ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars,
- sizeof(zeroes_continuation_chars)));
+ const uint8_t zeroes_continuation_chars[] = {0xc2, 0xa1, 0xc2, 0x00};
+ ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars, sizeof(zeroes_continuation_chars)));
}
TEST(entry_name_utils, InvalidSequence) {
// 0xfe is an invalid start byte
- const uint8_t invalid[] = { 0x41, 0xfe };
+ const uint8_t invalid[] = {0x41, 0xfe};
ASSERT_FALSE(IsValidEntryName(invalid, sizeof(invalid)));
// 0x91 is an invalid start byte (it's a valid continuation byte).
- const uint8_t invalid2[] = { 0x41, 0x91 };
+ const uint8_t invalid2[] = {0x41, 0x91};
ASSERT_FALSE(IsValidEntryName(invalid2, sizeof(invalid2)));
}
TEST(entry_name_utils, TruncatedContinuation) {
// Malayalam script with truncated bytes. There should be 2 bytes
// after 0xe0
- const uint8_t truncated[] = { 0xe0, 0xb4, 0x85, 0xe0, 0xb4 };
+ const uint8_t truncated[] = {0xe0, 0xb4, 0x85, 0xe0, 0xb4};
ASSERT_FALSE(IsValidEntryName(truncated, sizeof(truncated)));
// 0xc2 is the start of a 2 byte sequence that we've subsequently
// dropped.
- const uint8_t truncated2[] = { 0xc2, 0xc2, 0xa1 };
+ const uint8_t truncated2[] = {0xc2, 0xc2, 0xa1};
ASSERT_FALSE(IsValidEntryName(truncated2, sizeof(truncated2)));
}
TEST(entry_name_utils, BadContinuation) {
// 0x41 is an invalid continuation char, since it's MSBs
// aren't "10..." (are 01).
- const uint8_t bad[] = { 0xc2, 0xa1, 0xc2, 0x41 };
+ const uint8_t bad[] = {0xc2, 0xa1, 0xc2, 0x41};
ASSERT_FALSE(IsValidEntryName(bad, sizeof(bad)));
// 0x41 is an invalid continuation char, since it's MSBs
// aren't "10..." (are 11).
- const uint8_t bad2[] = { 0xc2, 0xa1, 0xc2, 0xfe };
+ const uint8_t bad2[] = {0xc2, 0xa1, 0xc2, 0xfe};
ASSERT_FALSE(IsValidEntryName(bad2, sizeof(bad2)));
}
diff --git a/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
similarity index 86%
rename from include/ziparchive/zip_archive.h
rename to libziparchive/include/ziparchive/zip_archive.h
index 31fc2df..73ae68d 100644
--- a/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -28,8 +28,8 @@
/* Zip compression methods we support */
enum {
- kCompressStored = 0, // no compression
- kCompressDeflated = 8, // standard deflate
+ kCompressStored = 0, // no compression
+ kCompressDeflated = 8, // standard deflate
};
struct ZipString {
@@ -44,19 +44,17 @@
explicit ZipString(const char* entry_name);
bool operator==(const ZipString& rhs) const {
- return name && (name_length == rhs.name_length) &&
- (memcmp(name, rhs.name, name_length) == 0);
+ return name && (name_length == rhs.name_length) && (memcmp(name, rhs.name, name_length) == 0);
}
bool StartsWith(const ZipString& prefix) const {
return name && (name_length >= prefix.name_length) &&
- (memcmp(name, prefix.name, prefix.name_length) == 0);
+ (memcmp(name, prefix.name, prefix.name_length) == 0);
}
bool EndsWith(const ZipString& suffix) const {
return name && (name_length >= suffix.name_length) &&
- (memcmp(name + name_length - suffix.name_length, suffix.name,
- suffix.name_length) == 0);
+ (memcmp(name + name_length - suffix.name_length, suffix.name, suffix.name_length) == 0);
}
};
@@ -71,8 +69,17 @@
// Modification time. The zipfile format specifies
// that the first two little endian bytes contain the time
// and the last two little endian bytes contain the date.
+ // See `GetModificationTime`.
+ // TODO: should be overridden by extra time field, if present.
uint32_t mod_time;
+ // Returns `mod_time` as a broken-down struct tm.
+ struct tm GetModificationTime() const;
+
+ // Suggested Unix mode for this entry, from the zip archive if created on
+ // Unix, or a default otherwise.
+ mode_t unix_mode;
+
// 1 if this entry contains a data descriptor segment, 0
// otherwise.
uint8_t has_data_descriptor;
@@ -125,11 +132,11 @@
*
* Returns 0 on success, and negative values on failure.
*/
-int32_t OpenArchiveFd(const int fd, const char* debugFileName,
- ZipArchiveHandle *handle, bool assume_ownership = true);
+int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
+ bool assume_ownership = true);
int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debugFileName,
- ZipArchiveHandle *handle);
+ ZipArchiveHandle* handle);
/*
* Close archive, releasing resources associated with it. This will
* unmap the central directory of the zipfile and free all internal
@@ -155,8 +162,7 @@
* On non-Windows platforms this method does not modify internal state and
* can be called concurrently.
*/
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
- ZipEntry* data);
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data);
/*
* Start iterating over all entries of a zip file. The order of iteration
@@ -171,8 +177,7 @@
*
* Returns 0 on success and negative values on failure.
*/
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
- const ZipString* optional_prefix,
+int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
const ZipString* optional_suffix);
/*
@@ -208,8 +213,7 @@
*
* Returns 0 on success and negative values on failure.
*/
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
- uint8_t* begin, uint32_t size);
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size);
int GetFileDescriptor(const ZipArchiveHandle handle);
@@ -221,9 +225,9 @@
/*
* Stream the uncompressed data through the supplied function,
* passing cookie to it each time it gets called.
-*/
+ */
int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
- ProcessZipEntryFunction func, void* cookie);
+ ProcessZipEntryFunction func, void* cookie);
#endif
#endif // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
similarity index 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/testdata/bad_filename.zip b/libziparchive/testdata/bad_filename.zip
new file mode 100644
index 0000000..294eaf5
--- /dev/null
+++ b/libziparchive/testdata/bad_filename.zip
Binary files differ
diff --git a/libziparchive/testdata/crash.apk b/libziparchive/testdata/crash.apk
new file mode 100644
index 0000000..d6dd52d
--- /dev/null
+++ b/libziparchive/testdata/crash.apk
Binary files differ
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 246575f..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,13 @@
* 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));
+ return -1;
+ }
/*
* Walk through the central directory, adding entries to the hash
@@ -381,22 +325,24 @@
const uint8_t* const cd_end = cd_ptr + cd_length;
const uint8_t* ptr = cd_ptr;
for (uint16_t i = 0; i < num_entries; i++) {
- const CentralDirectoryRecord* cdr =
- reinterpret_cast<const CentralDirectoryRecord*>(ptr);
- if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
- ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
+ if (ptr > cd_end - sizeof(CentralDirectoryRecord)) {
+ ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
+#if defined(__ANDROID__)
+ android_errorWriteLog(0x534e4554, "36392138");
+#endif
return -1;
}
- if (ptr + sizeof(CentralDirectoryRecord) > cd_end) {
- ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
+ 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;
}
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;
}
@@ -405,6 +351,13 @@
const uint16_t comment_length = cdr->comment_length;
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);
+ return -1;
+ }
/* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
if (!IsValidEntryName(file_name, file_name_length)) {
return -1;
@@ -414,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;
@@ -423,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;
}
}
@@ -433,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;
@@ -447,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);
@@ -468,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);
@@ -483,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;
@@ -494,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
@@ -520,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
@@ -549,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 agree on the use of a trailing
- // Data Descriptor.
+ // 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. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
+ 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) {
@@ -613,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;
@@ -622,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;
@@ -646,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);
@@ -674,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);
@@ -688,7 +658,7 @@
cookie->position = 0;
cookie->archive = archive;
- *cookie_ptr = cookie ;
+ *cookie_ptr = cookie;
return 0;
}
@@ -696,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);
@@ -733,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) {
@@ -756,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);
};
@@ -767,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;
}
@@ -793,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
@@ -852,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;
}
@@ -866,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_;
@@ -915,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);
}
@@ -925,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 */
@@ -951,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];
@@ -971,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;
}
@@ -986,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);
@@ -1016,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;
@@ -1037,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;
}
@@ -1053,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;
@@ -1070,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);
@@ -1091,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_);
@@ -1111,7 +1077,7 @@
return ExtractToWriter(handle, entry, &writer);
}
-#endif //!defined(_WIN32)
+#endif //! defined(_WIN32)
int MappedZipFile::GetFileDescriptor() const {
if (!has_fd_) {
@@ -1154,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;
}
@@ -1166,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;
}
@@ -1192,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) {
@@ -1203,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");
@@ -1217,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;
}
@@ -1227,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_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
new file mode 100644
index 0000000..cd3e164
--- /dev/null
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <android-base/test_utils.h>
+#include <benchmark/benchmark.h>
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_archive_stream_entry.h>
+#include <ziparchive/zip_writer.h>
+
+static TemporaryFile* CreateZip() {
+ TemporaryFile* result = new TemporaryFile;
+ FILE* fp = fdopen(result->fd, "w");
+
+ ZipWriter writer(fp);
+ std::string lastName = "file";
+ for (size_t i = 0; i < 1000; i++) {
+ // Make file names longer and longer.
+ lastName = lastName + std::to_string(i);
+ writer.StartEntry(lastName.c_str(), ZipWriter::kCompress);
+ writer.WriteBytes("helo", 4);
+ writer.FinishEntry();
+ }
+ writer.Finish();
+ fclose(fp);
+
+ return result;
+}
+
+static void FindEntry_no_match(benchmark::State& state) {
+ // Create a temporary zip archive.
+ std::unique_ptr<TemporaryFile> temp_file(CreateZip());
+ ZipArchiveHandle handle;
+ ZipEntry data;
+
+ // In order to walk through all file names in the archive, look for a name
+ // that does not exist in the archive.
+ ZipString name("thisFileNameDoesNotExist");
+
+ // Start the benchmark.
+ while (state.KeepRunning()) {
+ OpenArchive(temp_file->path, &handle);
+ FindEntry(handle, name, &data);
+ CloseArchive(handle);
+ }
+}
+BENCHMARK(FindEntry_no_match);
+
+static void Iterate_all_files(benchmark::State& state) {
+ std::unique_ptr<TemporaryFile> temp_file(CreateZip());
+ ZipArchiveHandle handle;
+ void* iteration_cookie;
+ ZipEntry data;
+ ZipString name;
+
+ while (state.KeepRunning()) {
+ OpenArchive(temp_file->path, &handle);
+ StartIteration(handle, &iteration_cookie, nullptr, nullptr);
+ while (Next(iteration_cookie, &data, &name) == 0) {
+ }
+ EndIteration(iteration_cookie);
+ CloseArchive(handle);
+ }
+}
+BENCHMARK(Iterate_all_files);
+
+BENCHMARK_MAIN()
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
index ca42509..8b99bde 100644
--- a/libziparchive/zip_archive_common.h
+++ b/libziparchive/zip_archive_common.h
@@ -57,6 +57,7 @@
uint32_t cd_start_offset;
// Length of the central directory comment.
uint16_t comment_length;
+
private:
EocdRecord() = default;
DISALLOW_COPY_AND_ASSIGN(EocdRecord);
@@ -73,7 +74,7 @@
// The start of record signature. Must be |kSignature|.
uint32_t record_signature;
- // Tool version. Ignored by this implementation.
+ // Source tool version. Top byte gives source OS.
uint16_t version_made_by;
// Tool version. Ignored by this implementation.
uint16_t version_needed;
@@ -106,11 +107,12 @@
uint16_t file_start_disk;
// File attributes. Ignored by this implementation.
uint16_t internal_file_attributes;
- // File attributes. Ignored by this implementation.
+ // File attributes. For archives created on Unix, the top bits are the mode.
uint32_t external_file_attributes;
// The offset to the local file header for this entry, from the
// beginning of this archive.
uint32_t local_file_header_offset;
+
private:
CentralDirectoryRecord() = default;
DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
@@ -149,6 +151,7 @@
// The length of the extra field info (in bytes). This data
// will appear immediately after the entry file name.
uint16_t extra_field_length;
+
private:
LocalFileHeader() = default;
DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
@@ -164,6 +167,7 @@
uint32_t compressed_size;
// Uncompressed size of the entry.
uint32_t uncompressed_size;
+
private:
DataDescriptor() = default;
DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 971db4f..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 9dd6cc0..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>
@@ -38,23 +40,17 @@
static const std::string kValidZip = "valid.zip";
static const std::string kLargeZip = "large.zip";
static const std::string kBadCrcZip = "bad_crc.zip";
+static const std::string kCrashApk = "crash.apk";
+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");
@@ -63,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));
}
@@ -83,7 +77,15 @@
TEST(ziparchive, Open) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+ CloseArchive(handle);
+ ASSERT_EQ(-1, OpenArchiveWrapper(kBadFilenameZip, &handle));
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, OutOfBound) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(-8, OpenArchiveWrapper(kCrashApk, &handle));
CloseArchive(handle);
}
@@ -333,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;
@@ -394,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));
@@ -408,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));
@@ -436,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) {
@@ -453,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)));
@@ -464,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));
@@ -478,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));
@@ -487,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()),
@@ -507,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));
@@ -523,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));
@@ -554,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));
@@ -570,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));
@@ -632,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/logcat/logcat.cpp b/logcat/logcat.cpp
index e9ef9cc..f64196f 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1638,7 +1638,7 @@
logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
break;
}
- logcat_panic(context, HELP_FALSE, "logcat read failure");
+ logcat_panic(context, HELP_FALSE, "logcat read failure\n");
break;
}
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 06cc90d..07040b0 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -61,3 +61,4 @@
user logd
group log
writepid /dev/cpuset/system-background/tasks
+ oom_score_adjust -600
diff --git a/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/platform_tools_tool_version.mk b/platform_tools_tool_version.mk
index 73229e6..eed2ab5 100644
--- a/platform_tools_tool_version.mk
+++ b/platform_tools_tool_version.mk
@@ -17,6 +17,6 @@
# literal instead. Using 0 lets us easily distinguish non-canonical builds.
platform_tools_version := $(shell sed \
's/$${PLATFORM_SDK_VERSION}/0/ ; s/^Pkg.Revision=\(.*\)/\1/p ; d' \
- $(ANDROID_BUILD_TOP)/development/sdk/plat_tools_source.prop_template \
+ development/sdk/plat_tools_source.prop_template \
)
tool_version := $(platform_tools_version)-$(BUILD_NUMBER_FROM_FILE)
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 540e976..9301743 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -7,6 +7,7 @@
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
+import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc
@@ -26,6 +27,18 @@
# Set the security context of /postinstall if present.
restorecon /postinstall
+ # Mount cgroup mount point for cpu accounting
+ mount cgroup none /acct cpuacct
+ mkdir /acct/uid
+
+ # root memory control cgroup, used by lmkd
+ mkdir /dev/memcg 0700 root system
+ mount cgroup none /dev/memcg memory
+ # app mem cgroups, used by activity manager, lmkd and zygote
+ mkdir /dev/memcg/apps/ 0755 system system
+ # cgroup for system_server and surfaceflinger
+ mkdir /dev/memcg/system 0550 system system
+
start ueventd
on init
@@ -42,10 +55,6 @@
# Link /vendor to /system/vendor for devices without a vendor partition.
symlink /system/vendor /vendor
- # Mount cgroup mount point for cpu accounting
- mount cgroup none /acct cpuacct
- mkdir /acct/uid
-
# Create energy-aware scheduler tuning nodes
mkdir /dev/stune
mount cgroup none /dev/stune schedtune
@@ -98,12 +107,6 @@
symlink /storage/self/primary /mnt/sdcard
symlink /mnt/user/0/primary /mnt/runtime/default/self/primary
- # root memory control cgroup, used by lmkd
- mkdir /dev/memcg 0700 root system
- mount cgroup none /dev/memcg memory
- # app mem cgroups, used by activity manager, lmkd and zygote
- mkdir /dev/memcg/apps/ 0755 system system
-
write /proc/sys/kernel/panic_on_oops 1
write /proc/sys/kernel/hung_task_timeout_secs 0
write /proc/cpu/alignment 4
@@ -220,6 +223,8 @@
mount pstore pstore /sys/fs/pstore
chown system log /sys/fs/pstore/console-ramoops
chmod 0440 /sys/fs/pstore/console-ramoops
+ chown system log /sys/fs/pstore/console-ramoops-0
+ chmod 0440 /sys/fs/pstore/console-ramoops-0
chown system log /sys/fs/pstore/pmsg-ramoops-0
chmod 0440 /sys/fs/pstore/pmsg-ramoops-0
@@ -371,6 +376,20 @@
# create basic filesystem structure
mkdir /data/misc 01771 system misc
+ mkdir /data/misc/recovery 0770 system log
+ copy /data/misc/recovery/default.prop /data/misc/recovery/default.prop.1
+ chmod 0440 /data/misc/recovery/default.prop.1
+ chown system log /data/misc/recovery/default.prop.1
+ copy /default.prop /data/misc/recovery/default.prop
+ chmod 0440 /data/misc/recovery/default.prop
+ chown system log /data/misc/recovery/default.prop
+ mkdir /data/misc/recovery/proc 0770 system log
+ copy /data/misc/recovery/proc/version /data/misc/recovery/proc/version.1
+ chmod 0440 /data/misc/recovery/proc/version.1
+ chown system log /data/misc/recovery/proc/version.1
+ copy /proc/version /data/misc/recovery/proc/version
+ chmod 0440 /data/misc/recovery/proc/version
+ chown system log /data/misc/recovery/proc/version
mkdir /data/misc/bluedroid 02770 bluetooth bluetooth
# Fix the access permissions and group ownership for 'bt_config.conf'
chmod 0660 /data/misc/bluedroid/bt_config.conf
@@ -676,12 +695,17 @@
on property:security.perf_harden=1
write /proc/sys/kernel/perf_event_paranoid 3
+# on shutdown
+# In device's init.rc, this trigger can be used to do device-specific actions
+# before shutdown. e.g disable watchdog and mask error handling
+
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
class core
critical
seclabel u:r:ueventd:s0
+ shutdown critical
service healthd /sbin/healthd
class core
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 4f4fc5d..9620d63 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -3,7 +3,7 @@
required: [
"bzip2",
"grep",
- "gzip",
+ "grep_vendor",
"mkshrc",
"mkshrc_vendor",
"reboot",
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 1c9fb20..8db8327 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -28,8 +28,8 @@
}
// We build BSD grep separately, so it can provide egrep and fgrep too.
-cc_binary {
- name: "grep",
+cc_defaults {
+ name: "grep_common",
srcs: [
"upstream-netbsd/usr.bin/grep/fastgrep.c",
"upstream-netbsd/usr.bin/grep/file.c",
@@ -40,5 +40,19 @@
cflags: common_cflags,
local_include_dirs: ["upstream-netbsd/include/"],
symlinks: ["egrep", "fgrep"],
+}
+cc_binary {
+ name: "grep",
+ defaults: ["grep_common"],
+}
+
+// Build vendor grep.
+// TODO: Add vendor_available to "grep" module and remove "grep_vendor" module
+// when vendor_available is fully supported.
+cc_binary {
+ name: "grep_vendor",
+ stem: "grep",
+ vendor: true,
+ defaults: ["grep_common"],
}
diff --git a/toolbox/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;
-}