Merge "Make init builtin command installkey respect property updates"
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index f195b4e..f95a855 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -268,10 +268,6 @@
#undef accept
#define accept ___xxx_accept
-int adb_getsockname(int fd, struct sockaddr* sockaddr, socklen_t* optlen);
-#undef getsockname
-#define getsockname(...) ___xxx_getsockname(__VA__ARGS__)
-
// Returns the local port number of a bound socket, or -1 on failure.
int adb_socket_get_local_port(int fd);
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index f997e6b..5873b2b 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -984,7 +984,7 @@
return -1;
}
- int result = (getsockname)(fh->fh_socket, sockaddr, optlen);
+ int result = getsockname(fh->fh_socket, sockaddr, optlen);
if (result == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd,
@@ -1042,11 +1042,6 @@
int local_port = -1;
std::string error;
- struct sockaddr_storage peer_addr = {};
- struct sockaddr_storage client_addr = {};
- socklen_t peer_socklen = sizeof(peer_addr);
- socklen_t client_socklen = sizeof(client_addr);
-
server = network_loopback_server(0, SOCK_STREAM, &error);
if (server < 0) {
D("adb_socketpair: failed to create server: %s", error.c_str());
@@ -1066,32 +1061,12 @@
goto fail;
}
- // Make sure that the peer that connected to us and the client are the same.
- accepted = adb_socket_accept(server, reinterpret_cast<sockaddr*>(&peer_addr), &peer_socklen);
+ accepted = adb_socket_accept(server, nullptr, nullptr);
if (accepted < 0) {
D("adb_socketpair: failed to accept: %s", strerror(errno));
goto fail;
}
-
- if (adb_getsockname(client, reinterpret_cast<sockaddr*>(&client_addr), &client_socklen) != 0) {
- D("adb_socketpair: failed to getpeername: %s", strerror(errno));
- goto fail;
- }
-
- if (peer_socklen != client_socklen) {
- D("adb_socketpair: client and peer sockaddrs have different lengths");
- errno = EIO;
- goto fail;
- }
-
- if (memcmp(&peer_addr, &client_addr, peer_socklen) != 0) {
- D("adb_socketpair: client and peer sockaddrs don't match");
- errno = EIO;
- goto fail;
- }
-
adb_close(server);
-
sv[0] = client;
sv[1] = accepted;
return 0;
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 024bc3d..4783d6e 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -163,6 +163,7 @@
srcs: [
"client/debuggerd_client_test.cpp",
"debuggerd_test.cpp",
+ "tombstoned_client.cpp",
"util.cpp"
],
},
@@ -176,7 +177,8 @@
],
static_libs: [
- "libdebuggerd"
+ "libdebuggerd",
+ "libc_logging",
],
local_include_dirs: [
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index b9fb512..224444f 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -65,24 +65,25 @@
auto time_left = [timeout_ms, &end]() { return end - std::chrono::steady_clock::now(); };
auto set_timeout = [timeout_ms, &time_left](int sockfd) {
if (timeout_ms <= 0) {
- return true;
+ return -1;
}
auto remaining = time_left();
if (remaining < decltype(remaining)::zero()) {
- return false;
+ LOG(ERROR) << "timeout expired";
+ return -1;
}
struct timeval timeout;
populate_timeval(&timeout, remaining);
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
- return false;
+ return -1;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {
- return false;
+ return -1;
}
- return true;
+ return sockfd;
};
sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
@@ -91,12 +92,7 @@
return false;
}
- if (!set_timeout(sockfd)) {
- PLOG(ERROR) << "libdebugger_client: failed to set timeout";
- return false;
- }
-
- if (socket_local_client_connect(sockfd.get(), kTombstonedInterceptSocketName,
+ if (socket_local_client_connect(set_timeout(sockfd.get()), kTombstonedInterceptSocketName,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET) == -1) {
PLOG(ERROR) << "libdebuggerd_client: failed to connect to tombstoned";
return false;
@@ -115,21 +111,35 @@
return false;
}
- if (send_fd(sockfd.get(), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
+ 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;
}
+ // Check to make sure we've successfully registered.
+ InterceptResponse response;
+ ssize_t rc =
+ TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
+ if (rc == 0) {
+ LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+ return false;
+ } else if (rc != sizeof(response)) {
+ LOG(ERROR)
+ << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
+ << sizeof(response) << ", received " << rc;
+ return false;
+ }
+
+ if (response.status != InterceptStatus::kRegistered) {
+ LOG(ERROR) << "libdebuggerd_client: unexpected registration response: "
+ << static_cast<int>(response.status);
+ return false;
+ }
+
bool backtrace = dump_type == kDebuggerdBacktrace;
send_signal(pid, backtrace);
- if (!set_timeout(sockfd)) {
- PLOG(ERROR) << "libdebuggerd_client: failed to set timeout";
- return false;
- }
-
- InterceptResponse response;
- ssize_t rc = TEMP_FAILURE_RETRY(recv(sockfd.get(), &response, sizeof(response), MSG_TRUNC));
+ rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
if (rc == 0) {
LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
return false;
@@ -140,7 +150,7 @@
return false;
}
- if (response.success != 1) {
+ if (response.status != InterceptStatus::kStarted) {
response.error_message[sizeof(response.error_message) - 1] = '\0';
LOG(ERROR) << "libdebuggerd_client: tombstoned reported failure: " << response.error_message;
return false;
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 1a27f3f..1befcb1 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -36,6 +36,7 @@
#include <cutils/sockets.h>
#include <debuggerd/handler.h>
#include <debuggerd/protocol.h>
+#include <debuggerd/tombstoned.h>
#include <debuggerd/util.h>
#include <gtest/gtest.h>
@@ -77,6 +78,54 @@
} \
} while (0)
+static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd) {
+ 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};
+
+ unique_fd output_pipe_write;
+ if (!Pipe(output_fd, &output_pipe_write)) {
+ FAIL() << "failed to create output pipe: " << strerror(errno);
+ }
+
+ std::string pipe_size_str;
+ int pipe_buffer_size;
+ if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
+ FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
+ }
+
+ pipe_size_str = android::base::Trim(pipe_size_str);
+
+ if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
+ FAIL() << "failed to parse pipe max size";
+ }
+
+ if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
+ FAIL() << "failed to set pipe size: " << strerror(errno);
+ }
+
+ 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);
+ }
+
+ InterceptResponse response;
+ ssize_t rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
+ if (rc == -1) {
+ FAIL() << "failed to read response from tombstoned: " << strerror(errno);
+ } else if (rc == 0) {
+ FAIL() << "failed to read response from tombstoned (EOF)";
+ } else if (rc != sizeof(response)) {
+ FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
+ << ", received " << rc;
+ }
+
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status);
+}
+
class CrasherTest : public ::testing::Test {
public:
pid_t crasher_pid = -1;
@@ -118,38 +167,7 @@
FAIL() << "crasher hasn't been started";
}
- intercept_fd.reset(socket_local_client(kTombstonedInterceptSocketName,
- ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
- if (intercept_fd == -1) {
- FAIL() << "failed to contact tombstoned: " << strerror(errno);
- }
-
- InterceptRequest req = {.pid = crasher_pid };
-
- unique_fd output_pipe_write;
- if (!Pipe(output_fd, &output_pipe_write)) {
- FAIL() << "failed to create output pipe: " << strerror(errno);
- }
-
- std::string pipe_size_str;
- int pipe_buffer_size;
- if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
- FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
- }
-
- pipe_size_str = android::base::Trim(pipe_size_str);
-
- if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
- FAIL() << "failed to parse pipe max size";
- }
-
- if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
- FAIL() << "failed to set pipe size: " << strerror(errno);
- }
-
- 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);
- }
+ tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd);
}
void CrasherTest::FinishIntercept(int* result) {
@@ -165,7 +183,7 @@
FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
<< ", received " << rc;
} else {
- *result = response.success;
+ *result = response.status == InterceptStatus::kStarted ? 1 : 0;
}
}
@@ -508,3 +526,97 @@
ASSERT_EQ(0, WEXITSTATUS(status));
}
}
+
+TEST(tombstoned, no_notify) {
+ // Do this a few times.
+ for (int i = 0; i < 3; ++i) {
+ pid_t pid = 123'456'789 + i;
+
+ unique_fd intercept_fd, output_fd;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd);
+
+ {
+ unique_fd tombstoned_socket, input_fd;
+ ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+ ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
+ }
+
+ pid_t read_pid;
+ ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));
+ ASSERT_EQ(read_pid, pid);
+ }
+}
+
+TEST(tombstoned, stress) {
+ // Spawn threads to simultaneously do a bunch of failing dumps and a bunch of successful dumps.
+ static constexpr int kDumpCount = 100;
+
+ std::atomic<bool> start(false);
+ std::vector<std::thread> threads;
+ threads.emplace_back([&start]() {
+ while (!start) {
+ continue;
+ }
+
+ // Use a way out of range pid, to avoid stomping on an actual process.
+ pid_t pid_base = 1'000'000;
+
+ for (int dump = 0; dump < kDumpCount; ++dump) {
+ pid_t pid = pid_base + dump;
+
+ unique_fd intercept_fd, output_fd;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd);
+
+ // Pretend to crash, and then immediately close the socket.
+ unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+ if (sockfd == -1) {
+ FAIL() << "failed to connect to tombstoned: " << strerror(errno);
+ }
+ TombstonedCrashPacket packet = {};
+ packet.packet_type = CrashPacketType::kDumpRequest;
+ packet.packet.dump_request.pid = pid;
+ if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
+ FAIL() << "failed to write to tombstoned: " << strerror(errno);
+ }
+
+ continue;
+ }
+ });
+
+ threads.emplace_back([&start]() {
+ while (!start) {
+ continue;
+ }
+
+ // Use a way out of range pid, to avoid stomping on an actual process.
+ pid_t pid_base = 2'000'000;
+
+ for (int dump = 0; dump < kDumpCount; ++dump) {
+ pid_t pid = pid_base + dump;
+
+ unique_fd intercept_fd, output_fd;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd);
+
+ {
+ unique_fd tombstoned_socket, input_fd;
+ ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+ ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
+ tombstoned_notify_completion(tombstoned_socket.get());
+ }
+
+ // TODO: Fix the race that requires this sleep.
+ std::this_thread::sleep_for(50ms);
+
+ pid_t read_pid;
+ ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));
+ ASSERT_EQ(read_pid, pid);
+ }
+ });
+
+ start = true;
+
+ for (std::thread& thread : threads) {
+ thread.join();
+ }
+}
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/include/debuggerd/protocol.h
index bb2ab0d..0756876 100644
--- a/debuggerd/include/debuggerd/protocol.h
+++ b/debuggerd/include/debuggerd/protocol.h
@@ -56,8 +56,14 @@
int32_t pid;
};
+enum class InterceptStatus : uint8_t {
+ kFailed,
+ kStarted,
+ kRegistered,
+};
+
// Sent either immediately upon failure, or when the intercept has been used.
struct InterceptResponse {
- uint8_t success; // 0 or 1
+ InterceptStatus status;
char error_message[127]; // always null-terminated
};
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 789260d..dff942c 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -105,6 +105,7 @@
// 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()) {
InterceptResponse response = {};
+ response.status = InterceptStatus::kFailed;
snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
intercept_request.pid);
TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
@@ -113,9 +114,10 @@
intercept->intercept_pid = intercept_request.pid;
- // Register the intercept with the InterceptManager.
+ // Check if it's already registered.
if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
InterceptResponse response = {};
+ response.status = InterceptStatus::kFailed;
snprintf(response.error_message, sizeof(response.error_message),
"pid %" PRId32 " already intercepted", intercept_request.pid);
TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
@@ -123,6 +125,15 @@
goto fail;
}
+ // Let the other side know that the intercept has been registered, now that we know we can't
+ // fail. tombstoned is single threaded, so this isn't racy.
+ InterceptResponse response = {};
+ response.status = InterceptStatus::kRegistered;
+ if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) {
+ PLOG(WARNING) << "failed to notify interceptor of registration";
+ goto fail;
+ }
+
intercept->output_fd = std::move(rcv_fd);
intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
intercept->registered = true;
@@ -174,7 +185,7 @@
LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
InterceptResponse response = {};
- response.success = 1;
+ response.status = InterceptStatus::kStarted;
TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
*out_fd = std::move(intercept->output_fd);
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 6754508..2248a21 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -126,9 +126,7 @@
return result;
}
-static void dequeue_request(Crash* crash) {
- ++num_concurrent_dumps;
-
+static void perform_request(Crash* crash) {
unique_fd output_fd;
if (!intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
output_fd = get_tombstone_fd();
@@ -153,12 +151,22 @@
crash_completed_cb, crash);
event_add(crash->crash_event, &timeout);
}
+
+ ++num_concurrent_dumps;
return;
fail:
delete crash;
}
+static void dequeue_requests() {
+ while (!queued_requests.empty() && num_concurrent_dumps < kMaxConcurrentDumps) {
+ Crash* next_crash = queued_requests.front();
+ queued_requests.pop_front();
+ perform_request(next_crash);
+ }
+}
+
static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
void*) {
event_base* base = evconnlistener_get_base(listener);
@@ -207,7 +215,7 @@
LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
queued_requests.push_back(crash);
} else {
- dequeue_request(crash);
+ perform_request(crash);
}
return;
@@ -247,11 +255,7 @@
delete crash;
// If there's something queued up, let them proceed.
- if (!queued_requests.empty()) {
- Crash* next_crash = queued_requests.front();
- queued_requests.pop_front();
- dequeue_request(next_crash);
- }
+ dequeue_requests();
}
int main(int, char* []) {
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index a762038..7512eb9 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -390,10 +390,12 @@
continue;
}
- // Ensures that hashtree descriptor is either in /vbmeta or in
+ // Ensures that hashtree descriptor is in /vbmeta or /boot or in
// the same partition for verity setup.
std::string vbmeta_partition_name(verify_data.vbmeta_images[i].partition_name);
- if (vbmeta_partition_name != "vbmeta" && vbmeta_partition_name != partition_name) {
+ if (vbmeta_partition_name != "vbmeta" &&
+ vbmeta_partition_name != "boot" && // for legacy device to append top-level vbmeta
+ vbmeta_partition_name != partition_name) {
LWARNING << "Skip vbmeta image at " << verify_data.vbmeta_images[i].partition_name
<< " for partition: " << partition_name.c_str();
continue;
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index 2c14c9b..8e49663 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -39,7 +39,28 @@
#include "fs_mgr_avb_ops.h"
#include "fs_mgr_priv.h"
-static struct fstab* fs_mgr_fstab = nullptr;
+static std::string fstab_by_name_prefix;
+
+static std::string extract_by_name_prefix(struct fstab* fstab) {
+ // In AVB, we can assume that there's an entry for the /misc mount
+ // point in the fstab file and use that to get the device file for
+ // the misc partition. The device needs not to have an actual /misc
+ // partition. Then returns the prefix by removing the trailing "misc":
+ //
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/
+
+ struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
+ if (fstab_entry == nullptr) {
+ LERROR << "/misc mount point not found in fstab";
+ return "";
+ }
+
+ std::string full_path(fstab_entry->blk_device);
+ size_t end_slash = full_path.find_last_of("/");
+
+ return full_path.substr(0, end_slash + 1);
+}
static AvbIOResult read_from_partition(AvbOps* ops ATTRIBUTE_UNUSED, const char* partition,
int64_t offset, size_t num_bytes, void* buffer,
@@ -49,30 +70,14 @@
// for partitions having 'slotselect' optin in fstab file, but it
// won't be appended to the mount point.
//
- // In AVB, we can assume that there's an entry for the /misc mount
- // point and use that to get the device file for the misc partition.
- // From there we'll assume that a by-name scheme is used
- // so we can just replace the trailing "misc" by the given
- // |partition|, e.g.
+ // Appends |partition| to the fstab_by_name_prefix, which is obtained
+ // by removing the trailing "misc" from the device file of /misc mount
+ // point. e.g.,
//
- // - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/ ->
// - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a
- struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fs_mgr_fstab, "/misc");
-
- if (fstab_entry == nullptr) {
- LERROR << "/misc mount point not found in fstab";
- return AVB_IO_RESULT_ERROR_IO;
- }
-
- std::string partition_name(partition);
- std::string path(fstab_entry->blk_device);
- // Replaces the last field of device file if it's not misc.
- if (!android::base::StartsWith(partition_name, "misc")) {
- size_t end_slash = path.find_last_of("/");
- std::string by_name_prefix(path.substr(0, end_slash + 1));
- path = by_name_prefix + partition_name;
- }
+ std::string path = fstab_by_name_prefix + partition;
// Ensures the device path (a symlink created by init) is ready to
// access. fs_mgr_test_access() will test a few iterations if the
@@ -168,8 +173,8 @@
AvbOps* fs_mgr_dummy_avb_ops_new(struct fstab* fstab) {
AvbOps* ops;
- // Assigns the fstab to the static variable for later use.
- fs_mgr_fstab = fstab;
+ fstab_by_name_prefix = extract_by_name_prefix(fstab);
+ if (fstab_by_name_prefix.empty()) return nullptr;
ops = (AvbOps*)calloc(1, sizeof(AvbOps));
if (ops == nullptr) {
diff --git a/init/README.md b/init/README.md
index 0d8f495..e66ade2 100644
--- a/init/README.md
+++ b/init/README.md
@@ -286,11 +286,6 @@
`copy <src> <dst>`
> Copies a file. Similar to write, but useful for binary/large
amounts of data.
- Regarding to the src file, copying from symbol link file and world-writable
- or group-writable files are not allowed.
- Regarding to the dst file, the default mode created is 0600 if it does not
- exist. And it will be truncated if dst file is a normal regular file and
- already exists.
`domainname <name>`
> Set the domain name.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index ccc4fd1..dc2bda6 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -702,11 +702,61 @@
}
static int do_copy(const std::vector<std::string>& args) {
- std::string data;
- if (read_file(args[1].c_str(), &data)) {
- return write_file(args[2].c_str(), data.data()) ? 0 : 1;
+ char *buffer = NULL;
+ int rc = 0;
+ int fd1 = -1, fd2 = -1;
+ struct stat info;
+ int brtw, brtr;
+ char *p;
+
+ if (stat(args[1].c_str(), &info) < 0)
+ return -1;
+
+ if ((fd1 = open(args[1].c_str(), O_RDONLY|O_CLOEXEC)) < 0)
+ goto out_err;
+
+ if ((fd2 = open(args[2].c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
+ goto out_err;
+
+ if (!(buffer = (char*) malloc(info.st_size)))
+ goto out_err;
+
+ p = buffer;
+ brtr = info.st_size;
+ while(brtr) {
+ rc = read(fd1, p, brtr);
+ if (rc < 0)
+ goto out_err;
+ if (rc == 0)
+ break;
+ p += rc;
+ brtr -= rc;
}
- return 1;
+
+ p = buffer;
+ brtw = info.st_size;
+ while(brtw) {
+ rc = write(fd2, p, brtw);
+ if (rc < 0)
+ goto out_err;
+ if (rc == 0)
+ break;
+ p += rc;
+ brtw -= rc;
+ }
+
+ rc = 0;
+ goto out;
+out_err:
+ rc = -1;
+out:
+ if (buffer)
+ free(buffer);
+ if (fd1 >= 0)
+ close(fd1);
+ if (fd2 >= 0)
+ close(fd2);
+ return rc;
}
static int do_chown(const std::vector<std::string>& args) {
diff --git a/init/init.cpp b/init/init.cpp
index 4249115..6f3b3a6 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -875,6 +875,24 @@
}
}
+// The files and directories that were created before initial sepolicy load
+// need to have their security context restored to the proper value.
+// This must happen before /dev is populated by ueventd.
+static void selinux_restore_context() {
+ LOG(INFO) << "Running restorecon...";
+ restorecon("/dev");
+ restorecon("/dev/kmsg");
+ restorecon("/dev/socket");
+ restorecon("/dev/random");
+ restorecon("/dev/urandom");
+ restorecon("/dev/__properties__");
+ restorecon("/plat_property_contexts");
+ restorecon("/nonplat_property_contexts");
+ restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
+ restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+ restorecon("/dev/device-mapper");
+}
+
// Set the UDC controller for the ConfigFS USB Gadgets.
// Read the UDC controller in use from "/sys/class/udc".
// In case of multiple UDC controllers select the first one.
@@ -1213,22 +1231,7 @@
// Now set up SELinux for second stage.
selinux_initialize(false);
-
- // These directories were necessarily created before initial policy load
- // and therefore need their security context restored to the proper value.
- // This must happen before /dev is populated by ueventd.
- LOG(INFO) << "Running restorecon...";
- restorecon("/dev");
- restorecon("/dev/kmsg");
- restorecon("/dev/socket");
- restorecon("/dev/random");
- restorecon("/dev/urandom");
- restorecon("/dev/__properties__");
- restorecon("/plat_property_contexts");
- restorecon("/nonplat_property_contexts");
- restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
- restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
- restorecon("/dev/device-mapper");
+ selinux_restore_context();
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
diff --git a/init/service.cpp b/init/service.cpp
index ede6364..3db34db 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -211,7 +211,13 @@
LOG(INFO) << "Sending signal " << signal
<< " to service '" << name_
<< "' (pid " << pid_ << ") process group...";
- if (killProcessGroup(uid_, pid_, signal) == -1) {
+ int r;
+ if (signal == SIGTERM) {
+ r = killProcessGroupOnce(uid_, pid_, signal);
+ } else {
+ r = killProcessGroup(uid_, pid_, signal);
+ }
+ if (r == -1) {
PLOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
}
if (kill(-pid_, signal) == -1) {
diff --git a/init/util.cpp b/init/util.cpp
index 8a19939..73d97ed 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -185,8 +185,8 @@
}
bool write_file(const char* path, const char* content) {
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(
- open(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, 0600)));
if (fd == -1) {
PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
return false;
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 4e82e76..24c75c4 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -17,15 +17,9 @@
#include "util.h"
#include <errno.h>
-#include <fcntl.h>
-
-#include <sys/stat.h>
#include <gtest/gtest.h>
-#include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
-
TEST(util, read_file_ENOENT) {
std::string s("hello");
errno = 0;
@@ -34,35 +28,6 @@
EXPECT_EQ("", s); // s was cleared.
}
-TEST(util, read_file_group_writeable) {
- std::string s("hello");
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
- EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
- EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
- EXPECT_EQ("", s); // s was cleared.
-}
-
-TEST(util, read_file_world_writeable) {
- std::string s("hello");
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
- EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
- EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
- EXPECT_EQ("", s); // s was cleared.
-}
-
-TEST(util, read_file_symbol_link) {
- std::string s("hello");
- errno = 0;
- // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
- EXPECT_FALSE(read_file("/charger", &s));
- EXPECT_EQ(ELOOP, errno);
- EXPECT_EQ("", s); // s was cleared.
-}
-
TEST(util, read_file_success) {
std::string s("hello");
EXPECT_TRUE(read_file("/proc/version", &s));
@@ -72,42 +37,6 @@
EXPECT_STREQ("Linux", s.c_str());
}
-TEST(util, write_file_not_exist) {
- std::string s("hello");
- std::string s2("hello");
- TemporaryDir test_dir;
- std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
- EXPECT_TRUE(write_file(path.c_str(), s.c_str()));
- EXPECT_TRUE(read_file(path.c_str(), &s2));
- EXPECT_EQ(s, s2);
- struct stat sb;
- int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
- EXPECT_NE(-1, fd);
- EXPECT_EQ(0, fstat(fd, &sb));
- EXPECT_NE(0u, sb.st_mode & S_IRUSR);
- EXPECT_NE(0u, sb.st_mode & S_IWUSR);
- EXPECT_EQ(0u, sb.st_mode & S_IXUSR);
- EXPECT_EQ(0u, sb.st_mode & S_IRGRP);
- EXPECT_EQ(0u, sb.st_mode & S_IWGRP);
- EXPECT_EQ(0u, sb.st_mode & S_IXGRP);
- EXPECT_EQ(0u, sb.st_mode & S_IROTH);
- EXPECT_EQ(0u, sb.st_mode & S_IWOTH);
- EXPECT_EQ(0u, sb.st_mode & S_IXOTH);
- EXPECT_EQ(0, unlink(path.c_str()));
-}
-
-TEST(util, write_file_exist) {
- std::string s2("");
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, "1hello1")) << strerror(errno);
- EXPECT_TRUE(read_file(tf.path, &s2));
- EXPECT_STREQ("1hello1", s2.c_str());
- EXPECT_TRUE(write_file(tf.path, "2hello2"));
- EXPECT_TRUE(read_file(tf.path, &s2));
- EXPECT_STREQ("2hello2", s2.c_str());
-}
-
TEST(util, decode_uid) {
EXPECT_EQ(0U, decode_uid("root"));
EXPECT_EQ(UINT_MAX, decode_uid("toot"));
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index 718d76b..c663a5d 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -69,6 +69,7 @@
cc_test {
name: "libcutils_test_static",
+ test_suites: ["device-tests"],
defaults: ["libcutils_test_default"],
static_libs: ["libc"] + test_libraries,
stl: "libc++_static",
diff --git a/libcutils/tests/AndroidTest.xml b/libcutils/tests/AndroidTest.xml
new file mode 100644
index 0000000..c945f4d
--- /dev/null
+++ b/libcutils/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for libcutils_test_static">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="libcutils_test_static->/data/local/tmp/libcutils_test_static" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="libcutils_test_static" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/libnativebridge/.clang-format b/libnativebridge/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libnativebridge/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libnativeloader/.clang-format b/libnativeloader/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libnativeloader/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 11bd8cc..47f6ff3 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -24,6 +24,8 @@
int killProcessGroup(uid_t uid, int initialPid, int signal);
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
+
int createProcessGroup(uid_t uid, int initialPid);
void removeAllProcessGroups(void);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index eb66727..1572cb3 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -252,8 +252,7 @@
}
}
-static int killProcessGroupOnce(uid_t uid, int initialPid, int signal)
-{
+static int doKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
int processes = 0;
struct ctx ctx;
pid_t pid;
@@ -282,13 +281,11 @@
return processes;
}
-int killProcessGroup(uid_t uid, int initialPid, int signal)
-{
+static int killProcessGroup(uid_t uid, int initialPid, int signal, int retry) {
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
- int retry = 40;
int processes;
- while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) {
+ while ((processes = doKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
LOG(VERBOSE) << "killed " << processes << " processes for processgroup " << initialPid;
if (retry > 0) {
std::this_thread::sleep_for(5ms);
@@ -313,6 +310,14 @@
}
}
+int killProcessGroup(uid_t uid, int initialPid, int signal) {
+ return killProcessGroup(uid, initialPid, signal, 40 /*maxRetry*/);
+}
+
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
+ return killProcessGroup(uid, initialPid, signal, 0 /*maxRetry*/);
+}
+
static bool mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
{
if (mkdir(path, mode) == -1 && errno != EEXIST) {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ece623b..dabeac1 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -47,6 +47,7 @@
srcs: [
"ArmExidx.cpp",
+ "DwarfMemory.cpp",
"Elf.cpp",
"ElfInterface.cpp",
"ElfInterfaceArm.cpp",
@@ -87,6 +88,7 @@
srcs: [
"tests/ArmExidxDecodeTest.cpp",
"tests/ArmExidxExtractTest.cpp",
+ "tests/DwarfMemoryTest.cpp",
"tests/ElfInterfaceArmTest.cpp",
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h
new file mode 100644
index 0000000..0ff3b8c
--- /dev/null
+++ b/libunwindstack/DwarfEncoding.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_ENCODING_H
+#define _LIBUNWINDSTACK_DWARF_ENCODING_H
+
+#include <stdint.h>
+
+enum DwarfEncoding : uint8_t {
+ DW_EH_PE_omit = 0xff,
+
+ DW_EH_PE_absptr = 0x00,
+ DW_EH_PE_uleb128 = 0x01,
+ DW_EH_PE_udata2 = 0x02,
+ DW_EH_PE_udata4 = 0x03,
+ DW_EH_PE_udata8 = 0x04,
+ DW_EH_PE_sleb128 = 0x09,
+ DW_EH_PE_sdata2 = 0x0a,
+ DW_EH_PE_sdata4 = 0x0b,
+ DW_EH_PE_sdata8 = 0x0c,
+
+ DW_EH_PE_pcrel = 0x10,
+ DW_EH_PE_textrel = 0x20,
+ DW_EH_PE_datarel = 0x30,
+ DW_EH_PE_funcrel = 0x40,
+ DW_EH_PE_aligned = 0x50,
+
+ // The following are special values used to encode CFA and OP operands.
+ DW_EH_PE_udata1 = 0x0d,
+ DW_EH_PE_sdata1 = 0x0e,
+ DW_EH_PE_block = 0x0f,
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
new file mode 100644
index 0000000..11806ea
--- /dev/null
+++ b/libunwindstack/DwarfMemory.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "DwarfEncoding.h"
+#include "DwarfMemory.h"
+#include "Memory.h"
+
+bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) {
+ if (!memory_->Read(cur_offset_, dst, num_bytes)) {
+ return false;
+ }
+ cur_offset_ += num_bytes;
+ return true;
+}
+
+template <typename SignedType>
+bool DwarfMemory::ReadSigned(uint64_t* value) {
+ SignedType signed_value;
+ if (!ReadBytes(&signed_value, sizeof(SignedType))) {
+ return false;
+ }
+ *value = static_cast<int64_t>(signed_value);
+ return true;
+}
+
+bool DwarfMemory::ReadULEB128(uint64_t* value) {
+ uint64_t cur_value = 0;
+ uint64_t shift = 0;
+ uint8_t byte;
+ do {
+ if (!ReadBytes(&byte, 1)) {
+ return false;
+ }
+ cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+ *value = cur_value;
+ return true;
+}
+
+bool DwarfMemory::ReadSLEB128(int64_t* value) {
+ uint64_t cur_value = 0;
+ uint64_t shift = 0;
+ uint8_t byte;
+ do {
+ if (!ReadBytes(&byte, 1)) {
+ return false;
+ }
+ cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+ if (byte & 0x40) {
+ // Negative value, need to sign extend.
+ cur_value |= static_cast<uint64_t>(-1) << shift;
+ }
+ *value = static_cast<int64_t>(cur_value);
+ return true;
+}
+
+template <typename AddressType>
+size_t DwarfMemory::GetEncodedSize(uint8_t encoding) {
+ switch (encoding & 0x0f) {
+ case DW_EH_PE_absptr:
+ return sizeof(AddressType);
+ case DW_EH_PE_udata1:
+ case DW_EH_PE_sdata1:
+ return 1;
+ case DW_EH_PE_udata2:
+ case DW_EH_PE_sdata2:
+ return 2;
+ case DW_EH_PE_udata4:
+ case DW_EH_PE_sdata4:
+ return 4;
+ case DW_EH_PE_udata8:
+ case DW_EH_PE_sdata8:
+ return 8;
+ case DW_EH_PE_uleb128:
+ case DW_EH_PE_sleb128:
+ default:
+ return 0;
+ }
+}
+
+bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) {
+ assert((encoding & 0x0f) == 0);
+ assert(encoding != DW_EH_PE_aligned);
+
+ // Handle the encoding.
+ switch (encoding) {
+ case DW_EH_PE_absptr:
+ // Nothing to do.
+ break;
+ case DW_EH_PE_pcrel:
+ if (pc_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += pc_offset_;
+ break;
+ case DW_EH_PE_textrel:
+ if (text_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += text_offset_;
+ break;
+ case DW_EH_PE_datarel:
+ if (data_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += data_offset_;
+ break;
+ case DW_EH_PE_funcrel:
+ if (func_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += func_offset_;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfMemory::ReadEncodedValue(uint8_t encoding, uint64_t* value) {
+ if (encoding == DW_EH_PE_omit) {
+ *value = 0;
+ return true;
+ } else if (encoding == DW_EH_PE_aligned) {
+ if (__builtin_add_overflow(cur_offset_, sizeof(AddressType) - 1, &cur_offset_)) {
+ return false;
+ }
+ cur_offset_ &= -sizeof(AddressType);
+
+ if (sizeof(AddressType) != sizeof(uint64_t)) {
+ *value = 0;
+ }
+ return ReadBytes(value, sizeof(AddressType));
+ }
+
+ // Get the data.
+ switch (encoding & 0x0f) {
+ case DW_EH_PE_absptr:
+ if (sizeof(AddressType) != sizeof(uint64_t)) {
+ *value = 0;
+ }
+ if (!ReadBytes(value, sizeof(AddressType))) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_uleb128:
+ if (!ReadULEB128(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_sleb128:
+ int64_t signed_value;
+ if (!ReadSLEB128(&signed_value)) {
+ return false;
+ }
+ *value = static_cast<uint64_t>(signed_value);
+ break;
+ case DW_EH_PE_udata1: {
+ uint8_t value8;
+ if (!ReadBytes(&value8, 1)) {
+ return false;
+ }
+ *value = value8;
+ } break;
+ case DW_EH_PE_sdata1:
+ if (!ReadSigned<int8_t>(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_udata2: {
+ uint16_t value16;
+ if (!ReadBytes(&value16, 2)) {
+ return false;
+ }
+ *value = value16;
+ } break;
+ case DW_EH_PE_sdata2:
+ if (!ReadSigned<int16_t>(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_udata4: {
+ uint32_t value32;
+ if (!ReadBytes(&value32, 4)) {
+ return false;
+ }
+ *value = value32;
+ } break;
+ case DW_EH_PE_sdata4:
+ if (!ReadSigned<int32_t>(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_udata8:
+ if (!ReadBytes(value, sizeof(uint64_t))) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_sdata8:
+ if (!ReadSigned<int64_t>(value)) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return AdjustEncodedValue(encoding & 0xf0, value);
+}
+
+// Instantiate all of the needed template functions.
+template bool DwarfMemory::ReadSigned<int8_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int16_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int32_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int64_t>(uint64_t*);
+
+template size_t DwarfMemory::GetEncodedSize<uint32_t>(uint8_t);
+template size_t DwarfMemory::GetEncodedSize<uint64_t>(uint8_t);
+
+template bool DwarfMemory::ReadEncodedValue<uint32_t>(uint8_t, uint64_t*);
+template bool DwarfMemory::ReadEncodedValue<uint64_t>(uint8_t, uint64_t*);
diff --git a/libunwindstack/DwarfMemory.h b/libunwindstack/DwarfMemory.h
new file mode 100644
index 0000000..a304dd9
--- /dev/null
+++ b/libunwindstack/DwarfMemory.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_MEMORY_H
+#define _LIBUNWINDSTACK_DWARF_MEMORY_H
+
+#include <stdint.h>
+
+// Forward declarations.
+class Memory;
+
+class DwarfMemory {
+ public:
+ DwarfMemory(Memory* memory) : memory_(memory) {}
+ virtual ~DwarfMemory() = default;
+
+ bool ReadBytes(void* dst, size_t num_bytes);
+
+ template <typename SignedType>
+ bool ReadSigned(uint64_t* value);
+
+ bool ReadULEB128(uint64_t* value);
+
+ bool ReadSLEB128(int64_t* value);
+
+ template <typename AddressType>
+ size_t GetEncodedSize(uint8_t encoding);
+
+ bool AdjustEncodedValue(uint8_t encoding, uint64_t* value);
+
+ template <typename AddressType>
+ bool ReadEncodedValue(uint8_t encoding, uint64_t* value);
+
+ uint64_t cur_offset() { return cur_offset_; }
+ void set_cur_offset(uint64_t cur_offset) { cur_offset_ = cur_offset; }
+
+ void set_pc_offset(uint64_t offset) { pc_offset_ = offset; }
+ void clear_pc_offset() { pc_offset_ = static_cast<uint64_t>(-1); }
+
+ void set_data_offset(uint64_t offset) { data_offset_ = offset; }
+ void clear_data_offset() { data_offset_ = static_cast<uint64_t>(-1); }
+
+ void set_func_offset(uint64_t offset) { func_offset_ = offset; }
+ void clear_func_offset() { func_offset_ = static_cast<uint64_t>(-1); }
+
+ void set_text_offset(uint64_t offset) { text_offset_ = offset; }
+ void clear_text_offset() { text_offset_ = static_cast<uint64_t>(-1); }
+
+ private:
+ Memory* memory_;
+ uint64_t cur_offset_ = 0;
+
+ uint64_t pc_offset_ = static_cast<uint64_t>(-1);
+ uint64_t data_offset_ = static_cast<uint64_t>(-1);
+ uint64_t func_offset_ = static_cast<uint64_t>(-1);
+ uint64_t text_offset_ = static_cast<uint64_t>(-1);
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
new file mode 100644
index 0000000..4877f36
--- /dev/null
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "DwarfMemory.h"
+
+#include "MemoryFake.h"
+
+class DwarfMemoryTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ dwarf_mem_.reset(new DwarfMemory(&memory_));
+ }
+
+ template <typename AddressType>
+ void GetEncodedSizeTest(uint8_t value, size_t expected);
+ template <typename AddressType>
+ void ReadEncodedValue_omit();
+ template <typename AddressType>
+ void ReadEncodedValue_leb128();
+ template <typename AddressType>
+ void ReadEncodedValue_data1();
+ template <typename AddressType>
+ void ReadEncodedValue_data2();
+ template <typename AddressType>
+ void ReadEncodedValue_data4();
+ template <typename AddressType>
+ void ReadEncodedValue_data8();
+ template <typename AddressType>
+ void ReadEncodedValue_non_zero_adjust();
+ template <typename AddressType>
+ void ReadEncodedValue_overflow();
+
+ MemoryFake memory_;
+ std::unique_ptr<DwarfMemory> dwarf_mem_;
+};
+
+TEST_F(DwarfMemoryTest, ReadBytes) {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x10, 0x18, 0xff, 0xfe});
+
+ uint8_t byte;
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0x10U, byte);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0x18U, byte);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0xffU, byte);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0xfeU, byte);
+ ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+
+ dwarf_mem_->set_cur_offset(2);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0xffU, byte);
+ ASSERT_EQ(3U, dwarf_mem_->cur_offset());
+}
+
+TEST_F(DwarfMemoryTest, ReadSigned_check) {
+ uint64_t value;
+
+ // Signed 8 byte reads.
+ memory_.SetData8(0, static_cast<uint8_t>(-10));
+ memory_.SetData8(1, 200);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int8_t>(&value));
+ ASSERT_EQ(static_cast<int8_t>(-10), static_cast<int8_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int8_t>(&value));
+ ASSERT_EQ(static_cast<int8_t>(200), static_cast<int8_t>(value));
+
+ // Signed 16 byte reads.
+ memory_.SetData16(0x10, static_cast<uint16_t>(-1000));
+ memory_.SetData16(0x12, 50100);
+ dwarf_mem_->set_cur_offset(0x10);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int16_t>(&value));
+ ASSERT_EQ(static_cast<int16_t>(-1000), static_cast<int16_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int16_t>(&value));
+ ASSERT_EQ(static_cast<int16_t>(50100), static_cast<int16_t>(value));
+
+ // Signed 32 byte reads.
+ memory_.SetData32(0x100, static_cast<uint32_t>(-1000000000));
+ memory_.SetData32(0x104, 3000000000);
+ dwarf_mem_->set_cur_offset(0x100);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int32_t>(&value));
+ ASSERT_EQ(static_cast<int32_t>(-1000000000), static_cast<int32_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int32_t>(&value));
+ ASSERT_EQ(static_cast<int32_t>(3000000000), static_cast<int32_t>(value));
+
+ // Signed 64 byte reads.
+ memory_.SetData64(0x200, static_cast<uint64_t>(-2000000000000LL));
+ memory_.SetData64(0x208, 5000000000000LL);
+ dwarf_mem_->set_cur_offset(0x200);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int64_t>(&value));
+ ASSERT_EQ(static_cast<int64_t>(-2000000000000), static_cast<int64_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int64_t>(&value));
+ ASSERT_EQ(static_cast<int64_t>(5000000000000), static_cast<int64_t>(value));
+}
+
+TEST_F(DwarfMemoryTest, ReadULEB128) {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x01, 0x80, 0x24, 0xff, 0xc3, 0xff, 0x7f});
+
+ uint64_t value;
+ ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+ ASSERT_EQ(1U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(1U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+ ASSERT_EQ(3U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x1200U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+ ASSERT_EQ(7U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xfffe1ffU, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadSLEB128) {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x06, 0x40, 0x82, 0x34, 0x89, 0x64, 0xf9, 0xc3, 0x8f,
+ 0x2f, 0xbf, 0xc3, 0xf7, 0x5f});
+
+ int64_t value;
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(1U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(6U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(2U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xffffffffffffffc0ULL, static_cast<uint64_t>(value));
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x1a02U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(6U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xfffffffffffff209ULL, static_cast<uint64_t>(value));
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(10U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x5e3e1f9U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(14U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xfffffffffbfde1bfULL, static_cast<uint64_t>(value));
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::GetEncodedSizeTest(uint8_t value, size_t expected) {
+ for (size_t i = 0; i < 16; i++) {
+ uint8_t encoding = (i << 4) | value;
+ ASSERT_EQ(expected, dwarf_mem_->GetEncodedSize<AddressType>(encoding))
+ << "encoding 0x" << std::hex << static_cast<uint32_t>(encoding) << " test value 0x"
+ << static_cast<size_t>(value);
+ }
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint32_t) {
+ GetEncodedSizeTest<uint32_t>(0, sizeof(uint32_t));
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint64_t) {
+ GetEncodedSizeTest<uint64_t>(0, sizeof(uint64_t));
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data1) {
+ // udata1
+ GetEncodedSizeTest<uint32_t>(0x0d, 1);
+ GetEncodedSizeTest<uint64_t>(0x0d, 1);
+
+ // sdata1
+ GetEncodedSizeTest<uint32_t>(0x0e, 1);
+ GetEncodedSizeTest<uint64_t>(0x0e, 1);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data2) {
+ // udata2
+ GetEncodedSizeTest<uint32_t>(0x02, 2);
+ GetEncodedSizeTest<uint64_t>(0x02, 2);
+
+ // sdata2
+ GetEncodedSizeTest<uint32_t>(0x0a, 2);
+ GetEncodedSizeTest<uint64_t>(0x0a, 2);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data4) {
+ // udata4
+ GetEncodedSizeTest<uint32_t>(0x03, 4);
+ GetEncodedSizeTest<uint64_t>(0x03, 4);
+
+ // sdata4
+ GetEncodedSizeTest<uint32_t>(0x0b, 4);
+ GetEncodedSizeTest<uint64_t>(0x0b, 4);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data8) {
+ // udata8
+ GetEncodedSizeTest<uint32_t>(0x04, 8);
+ GetEncodedSizeTest<uint64_t>(0x04, 8);
+
+ // sdata8
+ GetEncodedSizeTest<uint32_t>(0x0c, 8);
+ GetEncodedSizeTest<uint64_t>(0x0c, 8);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_unknown) {
+ GetEncodedSizeTest<uint32_t>(0x01, 0);
+ GetEncodedSizeTest<uint64_t>(0x01, 0);
+
+ GetEncodedSizeTest<uint32_t>(0x09, 0);
+ GetEncodedSizeTest<uint64_t>(0x09, 0);
+
+ GetEncodedSizeTest<uint32_t>(0x0f, 0);
+ GetEncodedSizeTest<uint64_t>(0x0f, 0);
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_omit() {
+ uint64_t value = 123;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0xff, &value));
+ ASSERT_EQ(0U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) { ReadEncodedValue_omit<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) { ReadEncodedValue_omit<uint64_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint32_t) {
+ uint64_t value = 100;
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x00, &value));
+
+ memory_.SetData32(0, 0x12345678);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x00, &value));
+ ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint64_t) {
+ uint64_t value = 100;
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x00, &value));
+
+ memory_.SetData64(0, 0x12345678f1f2f3f4ULL);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x00, &value));
+ ASSERT_EQ(8U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678f1f2f3f4ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint32_t) {
+ uint64_t value = 100;
+ dwarf_mem_->set_cur_offset(1);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x50, &value));
+
+ memory_.SetData32(4, 0x12345678);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x50, &value));
+ ASSERT_EQ(8U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint64_t) {
+ uint64_t value = 100;
+ dwarf_mem_->set_cur_offset(1);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x50, &value));
+
+ memory_.SetData64(8, 0x12345678f1f2f3f4ULL);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x50, &value));
+ ASSERT_EQ(16U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678f1f2f3f4ULL, value);
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_leb128() {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x80, 0x42});
+
+ uint64_t value = 100;
+ // uleb128
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x01, &value));
+ ASSERT_EQ(0x2100U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ // sleb128
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x09, &value));
+ ASSERT_EQ(0xffffffffffffe100ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) { ReadEncodedValue_leb128<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) { ReadEncodedValue_leb128<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data1() {
+ memory_.SetData8(0, 0xe0);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0d, &value));
+ ASSERT_EQ(0xe0U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0e, &value));
+ ASSERT_EQ(0xffffffffffffffe0ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) { ReadEncodedValue_data1<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) { ReadEncodedValue_data1<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data2() {
+ memory_.SetData16(0, 0xe000);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x02, &value));
+ ASSERT_EQ(0xe000U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0a, &value));
+ ASSERT_EQ(0xffffffffffffe000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) { ReadEncodedValue_data2<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) { ReadEncodedValue_data2<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data4() {
+ memory_.SetData32(0, 0xe0000000);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x03, &value));
+ ASSERT_EQ(0xe0000000U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0b, &value));
+ ASSERT_EQ(0xffffffffe0000000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) { ReadEncodedValue_data4<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) { ReadEncodedValue_data4<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data8() {
+ memory_.SetData64(0, 0xe000000000000000ULL);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x04, &value));
+ ASSERT_EQ(0xe000000000000000ULL, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0c, &value));
+ ASSERT_EQ(0xe000000000000000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) { ReadEncodedValue_data8<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) { ReadEncodedValue_data8<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_non_zero_adjust() {
+ memory_.SetData64(0, 0xe000000000000000ULL);
+
+ uint64_t value = 0;
+ dwarf_mem_->set_pc_offset(0x2000);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x14, &value));
+ ASSERT_EQ(0xe000000000002000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint32_t) {
+ ReadEncodedValue_non_zero_adjust<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint64_t) {
+ ReadEncodedValue_non_zero_adjust<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_overflow() {
+ memory_.SetData64(0, 0);
+
+ uint64_t value = 0;
+ dwarf_mem_->set_cur_offset(UINT64_MAX);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<AddressType>(0x50, &value));
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint32_t) {
+ ReadEncodedValue_overflow<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint64_t) {
+ ReadEncodedValue_overflow<uint64_t>();
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_absptr) {
+ uint64_t value = 0x1234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x00, &value));
+ ASSERT_EQ(0x1234U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_pcrel) {
+ uint64_t value = 0x1234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+
+ dwarf_mem_->set_pc_offset(0x2000);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+ ASSERT_EQ(0x3234U, value);
+
+ dwarf_mem_->set_pc_offset(static_cast<uint64_t>(-4));
+ value = 0x1234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+ ASSERT_EQ(0x1230U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_textrel) {
+ uint64_t value = 0x8234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+
+ dwarf_mem_->set_text_offset(0x1000);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+ ASSERT_EQ(0x9234U, value);
+
+ dwarf_mem_->set_text_offset(static_cast<uint64_t>(-16));
+ value = 0x8234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+ ASSERT_EQ(0x8224U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_datarel) {
+ uint64_t value = 0xb234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+
+ dwarf_mem_->set_data_offset(0x1200);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+ ASSERT_EQ(0xc434U, value);
+
+ dwarf_mem_->set_data_offset(static_cast<uint64_t>(-256));
+ value = 0xb234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+ ASSERT_EQ(0xb134U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_funcrel) {
+ uint64_t value = 0x15234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+
+ dwarf_mem_->set_func_offset(0x60000);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+ ASSERT_EQ(0x75234U, value);
+
+ dwarf_mem_->set_func_offset(static_cast<uint64_t>(-4096));
+ value = 0x15234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+ ASSERT_EQ(0x14234U, value);
+}