Merge "libcutils: Clarify schedboost_enabled()"
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/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/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index da8ad37..4660f3d 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -88,6 +88,10 @@
} \
} while (0)
+#define ASSERT_BACKTRACE_FRAME(result, frame_name) \
+ ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX \
+ R"(/libc.so \()" frame_name R"(\+)")
+
static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
InterceptStatus* status, DebuggerdDumpType intercept_type) {
intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
@@ -307,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, "tgkill");
}
TEST_F(CrasherTest, signal) {
@@ -443,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));
@@ -454,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, "tgkill");
}
TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
@@ -474,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, "tgkill");
}
TEST_F(CrasherTest, capabilities) {
@@ -531,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) {
@@ -562,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) {
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/init/init.cpp b/init/init.cpp
index 4e462d7..999fada 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -880,7 +880,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);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 3490544..b43da4e 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,6 +39,7 @@
#include <sys/_system_properties.h>
#include <memory>
+#include <queue>
#include <vector>
#include <android-base/file.h>
@@ -162,7 +163,7 @@
return true;
}
-uint32_t property_set(const std::string& name, const std::string& value) {
+static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
size_t valuelen = value.size();
if (!is_legal_property_name(name)) {
@@ -176,12 +177,6 @@
return PROP_ERROR_INVALID_VALUE;
}
- if (name == "selinux.restorecon_recursive" && valuelen > 0) {
- if (selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
- LOG(ERROR) << "Failed to restorecon_recursive " << value;
- }
- }
-
prop_info* pi = (prop_info*) __system_property_find(name.c_str());
if (pi != nullptr) {
// ro.* properties are actually "write-once".
@@ -210,6 +205,85 @@
return PROP_SUCCESS;
}
+typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&);
+
+struct PropertyChildInfo {
+ pid_t pid;
+ PropertyAsyncFunc func;
+ std::string name;
+ std::string value;
+};
+
+static std::queue<PropertyChildInfo> property_children;
+
+static void PropertyChildLaunch() {
+ auto& info = property_children.front();
+ pid_t pid = fork();
+ if (pid < 0) {
+ LOG(ERROR) << "Failed to fork for property_set_async";
+ while (!property_children.empty()) {
+ property_children.pop();
+ }
+ return;
+ }
+ if (pid != 0) {
+ info.pid = pid;
+ } else {
+ if (info.func(info.name, info.value) != 0) {
+ LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
+ << "\") failed";
+ }
+ exit(0);
+ }
+}
+
+bool PropertyChildReap(pid_t pid) {
+ if (property_children.empty()) {
+ return false;
+ }
+ auto& info = property_children.front();
+ if (info.pid != pid) {
+ return false;
+ }
+ if (PropertySetImpl(info.name, info.value) != PROP_SUCCESS) {
+ LOG(ERROR) << "Failed to set async property " << info.name;
+ }
+ property_children.pop();
+ if (!property_children.empty()) {
+ PropertyChildLaunch();
+ }
+ return true;
+}
+
+static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
+ PropertyAsyncFunc func) {
+ if (value.empty()) {
+ return PropertySetImpl(name, value);
+ }
+
+ PropertyChildInfo info;
+ info.func = func;
+ info.name = name;
+ info.value = value;
+ property_children.push(info);
+ if (property_children.size() == 1) {
+ PropertyChildLaunch();
+ }
+ return PROP_SUCCESS;
+}
+
+static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) {
+ return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+}
+
+uint32_t property_set(const std::string& name, const std::string& value) {
+ if (name == "selinux.restorecon_recursive") {
+ return PropertySetAsync(name, value, RestoreconRecursiveAsync);
+ }
+
+ return PropertySetImpl(name, value);
+}
+
class SocketConnection {
public:
SocketConnection(int socket, const struct ucred& cred)
diff --git a/init/property_service.h b/init/property_service.h
index 9a5b6f6..9251722 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -26,6 +26,8 @@
const char* name;
};
+extern bool PropertyChildReap(pid_t pid);
+
void property_init(void);
void property_load_boot_defaults(void);
void load_persist_props(void);
diff --git a/init/service.cpp b/init/service.cpp
index 1a6474b..b73ddfb 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -1085,6 +1085,8 @@
} else if (pid == -1) {
PLOG(ERROR) << "waitpid failed";
return false;
+ } else if (PropertyChildReap(pid)) {
+ return true;
}
Service* svc = FindServiceByPid(pid);
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 01b8250..27c5d23 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -165,7 +165,7 @@
return RegenerateUeventsForDir(d.get(), callback);
}
-const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
+static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
void UeventListener::RegenerateUevents(RegenerateCallback callback) const {
for (const auto path : kRegenerationPaths) {
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
index 8e6f3b4..ba31aaa 100644
--- a/init/uevent_listener.h
+++ b/init/uevent_listener.h
@@ -35,8 +35,6 @@
using RegenerateCallback = std::function<RegenerationAction(const Uevent&)>;
using PollCallback = std::function<void(const Uevent&)>;
-extern const char* kRegenerationPaths[3];
-
class UeventListener {
public:
UeventListener();
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 31e4106..d121647 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -174,9 +174,7 @@
}
void ColdBoot::DoRestoreCon() {
- for (const char* path : kRegenerationPaths) {
- selinux_android_restorecon(path, SELINUX_ANDROID_RESTORECON_RECURSE);
- }
+ selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
device_handler_.set_skip_restorecon(false);
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index eb2b902..d7e949b 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -48,6 +48,8 @@
srcs: [
"ArmExidx.cpp",
"DwarfCfa.cpp",
+ "DwarfDebugFrame.cpp",
+ "DwarfEhFrame.cpp",
"DwarfMemory.cpp",
"DwarfOp.cpp",
"DwarfSection.cpp",
@@ -96,6 +98,8 @@
"tests/ArmExidxExtractTest.cpp",
"tests/DwarfCfaLogTest.cpp",
"tests/DwarfCfaTest.cpp",
+ "tests/DwarfDebugFrameTest.cpp",
+ "tests/DwarfEhFrameTest.cpp",
"tests/DwarfMemoryTest.cpp",
"tests/DwarfOpLogTest.cpp",
"tests/DwarfOpTest.cpp",
diff --git a/libunwindstack/DwarfDebugFrame.cpp b/libunwindstack/DwarfDebugFrame.cpp
new file mode 100644
index 0000000..3ac02fc
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfMemory.h"
+#include "DwarfSection.h"
+#include "DwarfStructs.h"
+#include "Memory.h"
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
+ offset_ = offset;
+ end_offset_ = offset + size;
+
+ memory_.clear_func_offset();
+ memory_.clear_text_offset();
+ memory_.set_data_offset(offset);
+ memory_.set_cur_offset(offset);
+
+ return CreateSortedFdeList();
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
+ uint8_t version;
+ if (!memory_.ReadBytes(&version, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ // Read the augmentation string.
+ std::vector<char> aug_string;
+ char aug_value;
+ bool get_encoding = false;
+ do {
+ if (!memory_.ReadBytes(&aug_value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (aug_value == 'R') {
+ get_encoding = true;
+ }
+ aug_string.push_back(aug_value);
+ } while (aug_value != '\0');
+
+ if (version == 4) {
+ // Skip the Address Size field.
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+
+ // Read the segment size.
+ if (!memory_.ReadBytes(segment_size, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } else {
+ *segment_size = 0;
+ }
+
+ if (aug_string[0] != 'z' || !get_encoding) {
+ // No encoding
+ return true;
+ }
+
+ // Skip code alignment factor
+ uint8_t value;
+ do {
+ if (!memory_.ReadBytes(&value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } while (value & 0x80);
+
+ // Skip data alignment factor
+ do {
+ if (!memory_.ReadBytes(&value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } while (value & 0x80);
+
+ if (version == 1) {
+ // Skip return address register.
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+ } else {
+ // Skip return address register.
+ do {
+ if (!memory_.ReadBytes(&value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } while (value & 0x80);
+ }
+
+ // Skip the augmentation length.
+ do {
+ if (!memory_.ReadBytes(&value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } while (value & 0x80);
+
+ for (size_t i = 1; i < aug_string.size(); i++) {
+ if (aug_string[i] == 'R') {
+ if (!memory_.ReadBytes(encoding, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ // Got the encoding, that's all we are looking for.
+ return true;
+ } else if (aug_string[i] == 'L') {
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+ } else if (aug_string[i] == 'P') {
+ uint8_t encoding;
+ if (!memory_.ReadBytes(&encoding, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ uint64_t value;
+ if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ }
+ }
+
+ // It should be impossible to get here.
+ abort();
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::AddFdeInfo(uint64_t entry_offset, uint8_t segment_size,
+ uint8_t encoding) {
+ if (segment_size != 0) {
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+ }
+
+ uint64_t start;
+ if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ uint64_t length;
+ if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (length != 0) {
+ fdes_.emplace_back(entry_offset, start, length);
+ }
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::CreateSortedFdeList() {
+ memory_.set_cur_offset(offset_);
+
+ // Loop through all of the entries and read just enough to create
+ // a sorted list of pcs.
+ // This code assumes that first comes the cie, then the fdes that
+ // it applies to.
+ uint64_t cie_offset = 0;
+ uint8_t address_encoding;
+ uint8_t segment_size;
+ while (memory_.cur_offset() < end_offset_) {
+ uint64_t cur_entry_offset = memory_.cur_offset();
+
+ // Figure out the entry length and type.
+ uint32_t value32;
+ if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ uint64_t next_entry_offset;
+ if (value32 == static_cast<uint32_t>(-1)) {
+ uint64_t value64;
+ if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ next_entry_offset = memory_.cur_offset() + value64;
+
+ // Read the Cie Id of a Cie or the pointer of the Fde.
+ if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (value64 == static_cast<uint64_t>(-1)) {
+ // Cie 64 bit
+ address_encoding = DW_EH_PE_sdata8;
+ if (!GetCieInfo(&segment_size, &address_encoding)) {
+ return false;
+ }
+ cie_offset = cur_entry_offset;
+ } else {
+ if (offset_ + value64 != cie_offset) {
+ // This means that this Fde is not following the Cie.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Fde 64 bit
+ if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
+ return false;
+ }
+ }
+ } else {
+ next_entry_offset = memory_.cur_offset() + value32;
+
+ // Read the Cie Id of a Cie or the pointer of the Fde.
+ if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ if (value32 == static_cast<uint32_t>(-1)) {
+ // Cie 32 bit
+ address_encoding = DW_EH_PE_sdata4;
+ if (!GetCieInfo(&segment_size, &address_encoding)) {
+ return false;
+ }
+ cie_offset = cur_entry_offset;
+ } else {
+ if (offset_ + value32 != cie_offset) {
+ // This means that this Fde is not following the Cie.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Fde 32 bit
+ if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
+ return false;
+ }
+ }
+ }
+
+ if (next_entry_offset < memory_.cur_offset()) {
+ // This indicates some kind of corruption, or malformed section data.
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ memory_.set_cur_offset(next_entry_offset);
+ }
+
+ // Sort the entries.
+ std::sort(fdes_.begin(), fdes_.end(), [](const FdeInfo& a, const FdeInfo& b) {
+ if (a.start == b.start) return a.end < b.end;
+ return a.start < b.start;
+ });
+
+ fde_count_ = fdes_.size();
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+ if (fde_count_ == 0) {
+ return false;
+ }
+
+ size_t first = 0;
+ size_t last = fde_count_;
+ while (first < last) {
+ size_t current = (first + last) / 2;
+ const FdeInfo* info = &fdes_[current];
+ if (pc >= info->start && pc <= info->end) {
+ *fde_offset = info->offset;
+ return true;
+ }
+
+ if (pc < info->start) {
+ last = current;
+ } else {
+ first = current + 1;
+ }
+ }
+ return false;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfDebugFrame<AddressType>::GetFdeFromIndex(size_t index) {
+ if (index >= fdes_.size()) {
+ return nullptr;
+ }
+ return this->GetFdeFromOffset(fdes_[index].offset);
+}
+
+// Explicitly instantiate DwarfDebugFrame.
+template class DwarfDebugFrame<uint32_t>;
+template class DwarfDebugFrame<uint64_t>;
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
new file mode 100644
index 0000000..320368c
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+#define _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "DwarfSection.h"
+
+template <typename AddressType>
+class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
+ public:
+ // Add these so that the protected members of DwarfSectionImpl
+ // can be accessed without needing a this->.
+ using DwarfSectionImpl<AddressType>::memory_;
+ using DwarfSectionImpl<AddressType>::fde_count_;
+ using DwarfSectionImpl<AddressType>::last_error_;
+
+ struct FdeInfo {
+ FdeInfo(uint64_t offset, uint64_t start, uint64_t length)
+ : offset(offset), start(start), end(start + length) {}
+
+ uint64_t offset;
+ AddressType start;
+ AddressType end;
+ };
+
+ DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+ virtual ~DwarfDebugFrame() = default;
+
+ bool Init(uint64_t offset, uint64_t size) override;
+
+ bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+
+ const DwarfFde* GetFdeFromIndex(size_t index) override;
+
+ bool IsCie32(uint32_t value32) override { return value32 == static_cast<uint32_t>(-1); }
+
+ bool IsCie64(uint64_t value64) override { return value64 == static_cast<uint64_t>(-1); }
+
+ uint64_t GetCieOffsetFromFde32(uint32_t pointer) override { return offset_ + pointer; }
+
+ uint64_t GetCieOffsetFromFde64(uint64_t pointer) override { return offset_ + pointer; }
+
+ uint64_t AdjustPcFromFde(uint64_t pc) override { return pc; }
+
+ bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);
+
+ bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding);
+
+ bool CreateSortedFdeList();
+
+ protected:
+ uint64_t offset_;
+ uint64_t end_offset_;
+
+ std::vector<FdeInfo> fdes_;
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrame.cpp
new file mode 100644
index 0000000..045fb36
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "DwarfEhFrame.h"
+#include "DwarfMemory.h"
+#include "DwarfSection.h"
+#include "DwarfStructs.h"
+#include "Memory.h"
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
+ uint8_t data[4];
+
+ memory_.clear_func_offset();
+ memory_.clear_text_offset();
+ memory_.set_data_offset(offset);
+ memory_.set_cur_offset(offset);
+
+ // Read the first four bytes all at once.
+ if (!memory_.ReadBytes(data, 4)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ version_ = data[0];
+ if (version_ != 1) {
+ // Unknown version.
+ last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+ return false;
+ }
+
+ ptr_encoding_ = data[1];
+ uint8_t fde_count_encoding = data[2];
+ table_encoding_ = data[3];
+ table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
+
+ memory_.set_pc_offset(memory_.cur_offset());
+ if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ memory_.set_pc_offset(memory_.cur_offset());
+ if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ entries_offset_ = memory_.cur_offset();
+ entries_end_ = offset + size;
+ entries_data_offset_ = offset;
+ cur_entries_offset_ = entries_offset_;
+
+ return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfEhFrame<AddressType>::GetFdeFromIndex(size_t index) {
+ const FdeInfo* info = GetFdeInfoFromIndex(index);
+ if (info == nullptr) {
+ return nullptr;
+ }
+ return this->GetFdeFromOffset(info->offset);
+}
+
+template <typename AddressType>
+const typename DwarfEhFrame<AddressType>::FdeInfo* DwarfEhFrame<AddressType>::GetFdeInfoFromIndex(
+ size_t index) {
+ auto entry = fde_info_.find(index);
+ if (entry != fde_info_.end()) {
+ return &fde_info_[index];
+ }
+ FdeInfo* info = &fde_info_[index];
+
+ memory_.set_data_offset(entries_data_offset_);
+ memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
+ memory_.set_pc_offset(memory_.cur_offset());
+ uint64_t value;
+ if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
+ !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ fde_info_.erase(index);
+ return nullptr;
+ }
+ info->pc = value;
+ return info;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
+ uint64_t total_entries) {
+ assert(fde_count_ > 0);
+ assert(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) {
+ assert(fde_count_ != 0);
+ last_error_ = DWARF_ERROR_NONE;
+ // We can do a binary search if the pc is in the range of the elements
+ // that have already been cached.
+ if (!fde_info_.empty()) {
+ const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
+ if (pc >= info->pc) {
+ *fde_offset = info->offset;
+ return true;
+ }
+ if (pc < info->pc) {
+ return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
+ }
+ }
+
+ if (cur_entries_offset_ == 0) {
+ // All entries read, or error encountered.
+ return false;
+ }
+
+ memory_.set_data_offset(entries_data_offset_);
+ memory_.set_cur_offset(cur_entries_offset_);
+ cur_entries_offset_ = 0;
+
+ FdeInfo* prev_info = nullptr;
+ for (size_t current = fde_info_.size();
+ current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
+ memory_.set_pc_offset(memory_.cur_offset());
+ uint64_t value;
+ if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ FdeInfo* info = &fde_info_[current];
+ if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+ fde_info_.erase(current);
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ info->pc = value;
+
+ if (pc < info->pc) {
+ if (prev_info == nullptr) {
+ return false;
+ }
+ cur_entries_offset_ = memory_.cur_offset();
+ *fde_offset = prev_info->offset;
+ return true;
+ }
+ prev_info = info;
+ }
+
+ if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
+ *fde_offset = prev_info->offset;
+ return true;
+ }
+ return false;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+ if (fde_count_ == 0) {
+ return false;
+ }
+
+ if (table_entry_size_ > 0) {
+ // Do a binary search since the size of each table entry is fixed.
+ return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
+ } else {
+ // Do a sequential search since each table entry size is variable.
+ return GetFdeOffsetSequential(pc, fde_offset);
+ }
+}
+
+// Explicitly instantiate DwarfEhFrame.
+template class DwarfEhFrame<uint32_t>;
+template class DwarfEhFrame<uint64_t>;
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
new file mode 100644
index 0000000..e6909ed
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+#define _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+
+#include <stdint.h>
+
+#include "DwarfSection.h"
+
+// Forward declarations.
+class Memory;
+
+template <typename AddressType>
+class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
+ public:
+ // Add these so that the protected members of DwarfSectionImpl
+ // can be accessed without needing a this->.
+ using DwarfSectionImpl<AddressType>::memory_;
+ using DwarfSectionImpl<AddressType>::fde_count_;
+ using DwarfSectionImpl<AddressType>::last_error_;
+
+ struct FdeInfo {
+ AddressType pc;
+ uint64_t offset;
+ };
+
+ DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+ virtual ~DwarfEhFrame() = default;
+
+ bool Init(uint64_t offset, uint64_t size) override;
+
+ bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+
+ const DwarfFde* GetFdeFromIndex(size_t index) override;
+
+ bool IsCie32(uint32_t value32) override { return value32 == 0; }
+
+ bool IsCie64(uint64_t value64) override { return value64 == 0; }
+
+ uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+ return memory_.cur_offset() - pointer - 4;
+ }
+
+ uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+ return memory_.cur_offset() - pointer - 8;
+ }
+
+ uint64_t AdjustPcFromFde(uint64_t pc) override {
+ // The eh_frame uses relative pcs.
+ return pc + memory_.cur_offset();
+ }
+
+ const FdeInfo* GetFdeInfoFromIndex(size_t index);
+
+ bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset);
+
+ bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
+
+ protected:
+ uint8_t version_;
+ uint8_t ptr_encoding_;
+ uint8_t table_encoding_;
+ size_t table_entry_size_;
+
+ uint64_t ptr_offset_;
+
+ uint64_t entries_offset_;
+ uint64_t entries_end_;
+ uint64_t entries_data_offset_;
+ uint64_t cur_entries_offset_ = 0;
+
+ std::unordered_map<uint64_t, FdeInfo> fde_info_;
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 087457c..bfa7944 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -20,10 +20,33 @@
#include <memory>
#include <string>
+#include "DwarfDebugFrame.h"
+#include "DwarfEhFrame.h"
#include "ElfInterface.h"
#include "Memory.h"
#include "Regs.h"
+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() {
EhdrType ehdr;
@@ -210,6 +233,9 @@
}
// 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>();
diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/ElfInterface.h
index 944146c..1cc8aa0 100644
--- a/libunwindstack/ElfInterface.h
+++ b/libunwindstack/ElfInterface.h
@@ -25,6 +25,8 @@
#include <unordered_map>
#include <vector>
+#include "DwarfSection.h"
+
// Forward declarations.
class Memory;
class Regs;
@@ -68,10 +70,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();
@@ -105,6 +115,9 @@
uint8_t soname_type_ = SONAME_UNKNOWN;
std::string soname_;
+
+ std::unique_ptr<DwarfSection> eh_frame_;
+ std::unique_ptr<DwarfSection> debug_frame_;
};
class ElfInterface32 : public ElfInterface {
@@ -116,8 +129,7 @@
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);
@@ -137,8 +149,7 @@
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);
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
new file mode 100644
index 0000000..94a9974
--- /dev/null
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEncoding.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+template <typename TypeParam>
+class MockDwarfDebugFrame : public DwarfDebugFrame<TypeParam> {
+ public:
+ MockDwarfDebugFrame(Memory* memory) : DwarfDebugFrame<TypeParam>(memory) {}
+ ~MockDwarfDebugFrame() = default;
+
+ void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
+ void TestSetOffset(uint64_t offset) { this->offset_ = offset; }
+ void TestSetEndOffset(uint64_t offset) { this->end_offset_ = offset; }
+ void TestPushFdeInfo(const typename DwarfDebugFrame<TypeParam>::FdeInfo& info) {
+ this->fdes_.push_back(info);
+ }
+
+ uint64_t TestGetFdeCount() { return this->fde_count_; }
+ uint8_t TestGetOffset() { return this->offset_; }
+ uint8_t TestGetEndOffset() { return this->end_offset_; }
+ void TestGetFdeInfo(size_t index, typename DwarfDebugFrame<TypeParam>::FdeInfo* info) {
+ *info = this->fdes_[index];
+ }
+};
+
+template <typename TypeParam>
+class DwarfDebugFrameTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ debug_frame_ = new MockDwarfDebugFrame<TypeParam>(&memory_);
+ ResetLogs();
+ }
+
+ void TearDown() override { delete debug_frame_; }
+
+ MemoryFake memory_;
+ MockDwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfDebugFrameTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init32) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 1);
+ this->memory_.SetData8(0x5009, '\0');
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0);
+ this->memory_.SetData32(0x5108, 0x1500);
+ this->memory_.SetData32(0x510c, 0x200);
+
+ this->memory_.SetData32(0x5200, 0xfc);
+ this->memory_.SetData32(0x5204, 0);
+ this->memory_.SetData32(0x5208, 0x2500);
+ this->memory_.SetData32(0x520c, 0x300);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0x5300, 0xfc);
+ this->memory_.SetData32(0x5304, 0xffffffff);
+ this->memory_.SetData8(0x5308, 1);
+ this->memory_.SetData8(0x5309, '\0');
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5400, 0xfc);
+ this->memory_.SetData32(0x5404, 0x300);
+ this->memory_.SetData32(0x5408, 0x3500);
+ this->memory_.SetData32(0x540c, 0x400);
+
+ this->memory_.SetData32(0x5500, 0xfc);
+ this->memory_.SetData32(0x5504, 0x300);
+ this->memory_.SetData32(0x5508, 0x4500);
+ this->memory_.SetData32(0x550c, 0x500);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x1500U, info.start);
+ EXPECT_EQ(0x1700U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(1, &info);
+ EXPECT_EQ(0x5200U, info.offset);
+ EXPECT_EQ(0x2500U, info.start);
+ EXPECT_EQ(0x2800U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(2, &info);
+ EXPECT_EQ(0x5400U, info.offset);
+ EXPECT_EQ(0x3500U, info.start);
+ EXPECT_EQ(0x3900U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(3, &info);
+ EXPECT_EQ(0x5500U, info.offset);
+ EXPECT_EQ(0x4500U, info.start);
+ EXPECT_EQ(0x4a00U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init32_fde_not_following_cie) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 1);
+ this->memory_.SetData8(0x5009, '\0');
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0x1000);
+ this->memory_.SetData32(0x5108, 0x1500);
+ this->memory_.SetData32(0x510c, 0x200);
+
+ ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init64) {
+ // CIE 64 information.
+ this->memory_.SetData32(0x5000, 0xffffffff);
+ this->memory_.SetData64(0x5004, 0xf4);
+ this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
+ this->memory_.SetData8(0x5014, 1);
+ this->memory_.SetData8(0x5015, '\0');
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x5100, 0xffffffff);
+ this->memory_.SetData64(0x5104, 0xf4);
+ this->memory_.SetData64(0x510c, 0);
+ this->memory_.SetData64(0x5114, 0x1500);
+ this->memory_.SetData64(0x511c, 0x200);
+
+ this->memory_.SetData32(0x5200, 0xffffffff);
+ this->memory_.SetData64(0x5204, 0xf4);
+ this->memory_.SetData64(0x520c, 0);
+ this->memory_.SetData64(0x5214, 0x2500);
+ this->memory_.SetData64(0x521c, 0x300);
+
+ // CIE 64 information.
+ this->memory_.SetData32(0x5300, 0xffffffff);
+ this->memory_.SetData64(0x5304, 0xf4);
+ this->memory_.SetData64(0x530c, 0xffffffffffffffffULL);
+ this->memory_.SetData8(0x5314, 1);
+ this->memory_.SetData8(0x5315, '\0');
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x5400, 0xffffffff);
+ this->memory_.SetData64(0x5404, 0xf4);
+ this->memory_.SetData64(0x540c, 0x300);
+ this->memory_.SetData64(0x5414, 0x3500);
+ this->memory_.SetData64(0x541c, 0x400);
+
+ this->memory_.SetData32(0x5500, 0xffffffff);
+ this->memory_.SetData64(0x5504, 0xf4);
+ this->memory_.SetData64(0x550c, 0x300);
+ this->memory_.SetData64(0x5514, 0x4500);
+ this->memory_.SetData64(0x551c, 0x500);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x1500U, info.start);
+ EXPECT_EQ(0x1700U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(1, &info);
+ EXPECT_EQ(0x5200U, info.offset);
+ EXPECT_EQ(0x2500U, info.start);
+ EXPECT_EQ(0x2800U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(2, &info);
+ EXPECT_EQ(0x5400U, info.offset);
+ EXPECT_EQ(0x3500U, info.start);
+ EXPECT_EQ(0x3900U, info.end);
+
+ this->debug_frame_->TestGetFdeInfo(3, &info);
+ EXPECT_EQ(0x5500U, info.offset);
+ EXPECT_EQ(0x4500U, info.start);
+ EXPECT_EQ(0x4a00U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init64_fde_not_following_cie) {
+ // CIE 64 information.
+ this->memory_.SetData32(0x5000, 0xffffffff);
+ this->memory_.SetData64(0x5004, 0xf4);
+ this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
+ this->memory_.SetData8(0x5014, 1);
+ this->memory_.SetData8(0x5015, '\0');
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x5100, 0xffffffff);
+ this->memory_.SetData64(0x5104, 0xf4);
+ this->memory_.SetData64(0x510c, 0x1000);
+ this->memory_.SetData64(0x5114, 0x1500);
+ this->memory_.SetData64(0x511c, 0x200);
+
+ ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 1);
+ // Augment string.
+ this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'R', 'P', 'L', '\0'});
+ // Code alignment factor.
+ this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x80, 0x00});
+ // Data alignment factor.
+ this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+ // Return address register
+ this->memory_.SetData8(0x5014, 0x84);
+ // Augmentation length
+ this->memory_.SetMemory(0x5015, std::vector<uint8_t>{0x84, 0x00});
+ // R data.
+ this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0);
+ this->memory_.SetData16(0x5108, 0x1500);
+ this->memory_.SetData16(0x510a, 0x200);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+ ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x1500U, info.start);
+ EXPECT_EQ(0x1700U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init_version4) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 4);
+ // Augment string.
+ this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
+ // Address size.
+ this->memory_.SetData8(0x500e, 4);
+ // Segment size.
+ this->memory_.SetData8(0x500f, 0);
+ // Code alignment factor.
+ this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x80, 0x00});
+ // Data alignment factor.
+ this->memory_.SetMemory(0x5012, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+ // Return address register
+ this->memory_.SetMemory(0x5016, std::vector<uint8_t>{0x85, 0x10});
+ // Augmentation length
+ this->memory_.SetMemory(0x5018, std::vector<uint8_t>{0x84, 0x00});
+ // L data.
+ this->memory_.SetData8(0x501a, 0x10);
+ // P data.
+ this->memory_.SetData8(0x501b, DW_EH_PE_udata4);
+ this->memory_.SetData32(0x501c, 0x100);
+ // R data.
+ this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0);
+ this->memory_.SetData16(0x5108, 0x1500);
+ this->memory_.SetData16(0x510a, 0x200);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+ ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x1500U, info.start);
+ EXPECT_EQ(0x1700U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeOffsetFromPc) {
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+ for (size_t i = 0; i < 9; i++) {
+ info.start = 0x1000 * (i + 1);
+ info.end = 0x1000 * (i + 2) - 0x10;
+ info.offset = 0x5000 + i * 0x20;
+ this->debug_frame_->TestPushFdeInfo(info);
+ }
+
+ this->debug_frame_->TestSetFdeCount(0);
+ uint64_t fde_offset;
+ ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+
+ this->debug_frame_->TestSetFdeCount(9);
+ ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+ // Odd number of elements.
+ for (size_t i = 0; i < 9; i++) {
+ TypeParam pc = 0x1000 * (i + 1);
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
+ << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
+ << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+ << "Failed at index " << i;
+ ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+ }
+
+ // Even number of elements.
+ this->debug_frame_->TestSetFdeCount(10);
+ info.start = 0xa000;
+ info.end = 0xaff0;
+ info.offset = 0x5120;
+ this->debug_frame_->TestPushFdeInfo(info);
+
+ for (size_t i = 0; i < 10; i++) {
+ TypeParam pc = 0x1000 * (i + 1);
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
+ << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
+ << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+ << "Failed at index " << i;
+ ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+ }
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde32) {
+ this->debug_frame_->TestSetOffset(0x4000);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0xf000, 0x100);
+ this->memory_.SetData32(0xf004, 0xffffffff);
+ this->memory_.SetData8(0xf008, 0x1);
+ this->memory_.SetData8(0xf009, '\0');
+ this->memory_.SetData8(0xf00a, 4);
+ this->memory_.SetData8(0xf00b, 8);
+ this->memory_.SetData8(0xf00c, 0x20);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x14000, 0x20);
+ this->memory_.SetData32(0x14004, 0xb000);
+ this->memory_.SetData32(0x14008, 0x9000);
+ this->memory_.SetData32(0x1400c, 0x100);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x14000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x9000U, fde->pc_start);
+ EXPECT_EQ(0x9100U, fde->pc_end);
+ EXPECT_EQ(0xf000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde64) {
+ this->debug_frame_->TestSetOffset(0x2000);
+
+ // CIE 64 information.
+ this->memory_.SetData32(0x6000, 0xffffffff);
+ this->memory_.SetData64(0x6004, 0x100);
+ this->memory_.SetData64(0x600c, 0xffffffffffffffffULL);
+ this->memory_.SetData8(0x6014, 0x1);
+ this->memory_.SetData8(0x6015, '\0');
+ this->memory_.SetData8(0x6016, 4);
+ this->memory_.SetData8(0x6017, 8);
+ this->memory_.SetData8(0x6018, 0x20);
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x8000, 0xffffffff);
+ this->memory_.SetData64(0x8004, 0x200);
+ this->memory_.SetData64(0x800c, 0x4000);
+ this->memory_.SetData64(0x8014, 0x5000);
+ this->memory_.SetData64(0x801c, 0x300);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x8000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+ EXPECT_EQ(0x5000U, fde->pc_start);
+ EXPECT_EQ(0x5300U, fde->pc_end);
+ EXPECT_EQ(0x6000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie, Init64,
+ Init64_fde_not_following_cie, Init_version1, Init_version4,
+ GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
new file mode 100644
index 0000000..4e538d4
--- /dev/null
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DwarfEhFrame.h"
+#include "DwarfEncoding.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+template <typename TypeParam>
+class MockDwarfEhFrame : public DwarfEhFrame<TypeParam> {
+ public:
+ MockDwarfEhFrame(Memory* memory) : DwarfEhFrame<TypeParam>(memory) {}
+ ~MockDwarfEhFrame() = default;
+
+ void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
+ void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
+ void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; }
+ void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; }
+ void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; }
+ void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; }
+
+ void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
+ void TestSetFdeInfo(uint64_t index, const typename DwarfEhFrame<TypeParam>::FdeInfo& info) {
+ this->fde_info_[index] = info;
+ }
+
+ uint8_t TestGetVersion() { return this->version_; }
+ uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; }
+ uint64_t TestGetPtrOffset() { return this->ptr_offset_; }
+ uint8_t TestGetTableEncoding() { return this->table_encoding_; }
+ uint64_t TestGetTableEntrySize() { return this->table_entry_size_; }
+ uint64_t TestGetFdeCount() { return this->fde_count_; }
+ uint64_t TestGetEntriesOffset() { return this->entries_offset_; }
+ uint64_t TestGetEntriesEnd() { return this->entries_end_; }
+ uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; }
+ uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; }
+};
+
+template <typename TypeParam>
+class DwarfEhFrameTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ eh_frame_ = new MockDwarfEhFrame<TypeParam>(&memory_);
+ ResetLogs();
+ }
+
+ void TearDown() override { delete eh_frame_; }
+
+ MemoryFake memory_;
+ MockDwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfEhFrameTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfEhFrameTest, Init) {
+ this->memory_.SetMemory(
+ 0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
+ this->memory_.SetData16(0x1004, 0x500);
+ this->memory_.SetData32(0x1006, 126);
+
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100));
+ EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+ EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
+ EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
+ EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+ EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount());
+ EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
+ EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
+ EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+
+ // Verify an unexpected version will cause a fail.
+ this->memory_.SetData8(0x1000, 0);
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+ ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+ this->memory_.SetData8(0x1000, 2);
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+ ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_expect_cache_fail) {
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+ ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+ ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_pcrel) {
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
+ this->eh_frame_->TestSetEntriesOffset(0x1000);
+ this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x1380U, info->pc);
+ EXPECT_EQ(0x1540U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_datarel) {
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4);
+ this->eh_frame_->TestSetEntriesOffset(0x1000);
+ this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x3340U, info->pc);
+ EXPECT_EQ(0x3500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_cached) {
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+ this->eh_frame_->TestSetEntriesOffset(0x1000);
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x340U, info->pc);
+ EXPECT_EQ(0x500U, info->offset);
+
+ // Clear the memory so that this will fail if it doesn't read cached data.
+ this->memory_.Clear();
+
+ info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x340U, info->pc);
+ EXPECT_EQ(0x500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetBinary_verify) {
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+ this->eh_frame_->TestSetFdeCount(10);
+
+ typename DwarfEhFrame<TypeParam>::FdeInfo info;
+ for (size_t i = 0; i < 10; i++) {
+ info.pc = 0x1000 * (i + 1);
+ info.offset = 0x5000 + i * 0x20;
+ this->eh_frame_->TestSetFdeInfo(i, info);
+ }
+
+ uint64_t fde_offset;
+ EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
+ // Not an error, just not found.
+ ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+ // Even number of elements.
+ for (size_t i = 0; i < 10; i++) {
+ TypeParam pc = 0x1000 * (i + 1);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10)) << "Failed at index "
+ << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10))
+ << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ }
+ // Odd number of elements.
+ for (size_t i = 0; i < 9; i++) {
+ TypeParam pc = 0x1000 * (i + 1);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9)) << "Failed at index "
+ << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9))
+ << "Failed at index " << i;
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ }
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential) {
+ this->eh_frame_->TestSetFdeCount(10);
+ this->eh_frame_->TestSetEntriesDataOffset(0x100);
+ this->eh_frame_->TestSetEntriesEnd(0x2000);
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ this->memory_.SetData32(0x1048, 0x440);
+ this->memory_.SetData32(0x104c, 0x600);
+
+ // Verify that if entries is zero, that it fails.
+ uint64_t fde_offset;
+ ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset));
+ this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset));
+ EXPECT_EQ(0x500U, fde_offset);
+
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset));
+ EXPECT_EQ(0x600U, fde_offset);
+
+ // Expect that the data is cached so no more memory reads will occur.
+ this->memory_.Clear();
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset));
+ EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_last_element) {
+ this->eh_frame_->TestSetFdeCount(2);
+ this->eh_frame_->TestSetEntriesDataOffset(0x100);
+ this->eh_frame_->TestSetEntriesEnd(0x2000);
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+ this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ this->memory_.SetData32(0x1048, 0x440);
+ this->memory_.SetData32(0x104c, 0x600);
+
+ uint64_t fde_offset;
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+ EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_end_check) {
+ this->eh_frame_->TestSetFdeCount(2);
+ this->eh_frame_->TestSetEntriesDataOffset(0x100);
+ this->eh_frame_->TestSetEntriesEnd(0x1048);
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ this->memory_.SetData32(0x1048, 0x440);
+ this->memory_.SetData32(0x104c, 0x600);
+
+ uint64_t fde_offset;
+ ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_fail_fde_count) {
+ this->eh_frame_->TestSetFdeCount(0);
+
+ uint64_t fde_offset;
+ ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_binary_search) {
+ this->eh_frame_->TestSetTableEntrySize(16);
+ this->eh_frame_->TestSetFdeCount(10);
+
+ typename DwarfEhFrame<TypeParam>::FdeInfo info;
+ info.pc = 0x550;
+ info.offset = 0x10500;
+ this->eh_frame_->TestSetFdeInfo(5, info);
+ info.pc = 0x750;
+ info.offset = 0x10700;
+ this->eh_frame_->TestSetFdeInfo(7, info);
+ info.pc = 0x850;
+ info.offset = 0x10800;
+ this->eh_frame_->TestSetFdeInfo(8, info);
+
+ uint64_t fde_offset;
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x800, &fde_offset));
+ EXPECT_EQ(0x10700U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_sequential_search) {
+ this->eh_frame_->TestSetFdeCount(10);
+ this->eh_frame_->TestSetTableEntrySize(0);
+
+ typename DwarfEhFrame<TypeParam>::FdeInfo info;
+ info.pc = 0x50;
+ info.offset = 0x10000;
+ this->eh_frame_->TestSetFdeInfo(0, info);
+ info.pc = 0x150;
+ info.offset = 0x10100;
+ this->eh_frame_->TestSetFdeInfo(1, info);
+ info.pc = 0x250;
+ info.offset = 0x10200;
+ this->eh_frame_->TestSetFdeInfo(2, info);
+
+ uint64_t fde_offset;
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset));
+ EXPECT_EQ(0x10100U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) {
+ // CIE 32 information.
+ this->memory_.SetData32(0xf000, 0x100);
+ this->memory_.SetData32(0xf004, 0);
+ this->memory_.SetData8(0xf008, 0x1);
+ this->memory_.SetData8(0xf009, '\0');
+ this->memory_.SetData8(0xf00a, 4);
+ this->memory_.SetData8(0xf00b, 8);
+ this->memory_.SetData8(0xf00c, 0x20);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x14000, 0x20);
+ this->memory_.SetData32(0x14004, 0x5004);
+ this->memory_.SetData32(0x14008, 0x9000);
+ this->memory_.SetData32(0x1400c, 0x100);
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x14000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x1d00cU, fde->pc_start);
+ EXPECT_EQ(0x1d10cU, fde->pc_end);
+ EXPECT_EQ(0xf000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) {
+ // CIE 64 information.
+ this->memory_.SetData32(0x6000, 0xffffffff);
+ this->memory_.SetData64(0x6004, 0x100);
+ this->memory_.SetData64(0x600c, 0);
+ this->memory_.SetData8(0x6014, 0x1);
+ this->memory_.SetData8(0x6015, '\0');
+ this->memory_.SetData8(0x6016, 4);
+ this->memory_.SetData8(0x6017, 8);
+ this->memory_.SetData8(0x6018, 0x20);
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x8000, 0xffffffff);
+ this->memory_.SetData64(0x8004, 0x200);
+ this->memory_.SetData64(0x800c, 0x200c);
+ this->memory_.SetData64(0x8014, 0x5000);
+ this->memory_.SetData64(0x801c, 0x300);
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x8000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+ EXPECT_EQ(0xd01cU, fde->pc_start);
+ EXPECT_EQ(0xd31cU, fde->pc_end);
+ EXPECT_EQ(0x6000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init, GetFdeInfoFromIndex_expect_cache_fail,
+ GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel,
+ GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify,
+ GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
+ GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
+ GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
+ GetCieFde32, GetCieFde64);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index c31903d..81cdaf5 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -67,6 +67,18 @@
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();
+
MemoryFake memory_;
};
@@ -571,3 +583,138 @@
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>();
+}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index a64b3b1..efe1096 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -62,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.
*
@@ -1106,11 +1044,17 @@
}
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) {
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 971db4f..de93ecd 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -26,6 +26,69 @@
#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:
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 42167dd..52099c3 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>
@@ -708,8 +710,7 @@
int32_t error_code = 0;
ExtractEntryToMemory(invalid_csize, &entry, &error_code);
- ASSERT_GT(0, error_code);
- ASSERT_STREQ("Inconsistent information", ErrorCodeString(error_code));
+ ASSERT_EQ(kInconsistentInformation, error_code);
std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
invalid_csize[kSizeOffset] = 0xfe;
@@ -718,8 +719,17 @@
entry.clear();
ExtractEntryToMemory(invalid_csize, &entry, &error_code);
- ASSERT_GT(0, error_code);
- ASSERT_STREQ("Inconsistent information", ErrorCodeString(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) {
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/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 4f4fc5d..6d35fed 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -3,6 +3,7 @@
required: [
"bzip2",
"grep",
+ "grep_vendor",
"gzip",
"mkshrc",
"mkshrc_vendor",
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/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 7b8d5c7..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 com.android.timezone.distro.TimeZoneDistro / com.android.timezone.distro.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 com.android.timezone.distro.TimeZoneDistro / com.android.timezone.distro.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 com.android.timezone.distro.TimeZoneDistro / com.android.timezone.distro.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 com.android.timezone.distro.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;
-}