Merge "init: more header cleanup"
diff --git a/base/Android.bp b/base/Android.bp
index 121da42..3af7686 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -98,6 +98,7 @@
"parseint_test.cpp",
"parsenetaddress_test.cpp",
"quick_exit_test.cpp",
+ "scopeguard_test.cpp",
"stringprintf_test.cpp",
"strings_test.cpp",
"test_main.cpp",
diff --git a/base/file.cpp b/base/file.cpp
index d4e5894..7fbebc5 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -28,8 +28,9 @@
#include <string>
#include <vector>
-#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
#include "android-base/logging.h"
+#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/unique_fd.h"
#include "android-base/utf8.h"
#include "utils/Compat.h"
@@ -69,13 +70,11 @@
content->clear();
int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
if (fd == -1) {
return false;
}
- bool result = ReadFdToString(fd, content);
- close(fd);
- return result;
+ return ReadFdToString(fd, content);
}
bool WriteStringToFd(const std::string& content, int fd) {
@@ -106,7 +105,7 @@
bool follow_symlinks) {
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
(follow_symlinks ? 0 : O_NOFOLLOW);
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
if (fd == -1) {
PLOG(ERROR) << "android::WriteStringToFile open failed";
return false;
@@ -126,7 +125,6 @@
PLOG(ERROR) << "android::WriteStringToFile write failed";
return CleanUpAfterFailedWrite(path);
}
- close(fd);
return true;
}
#endif
@@ -135,14 +133,11 @@
bool follow_symlinks) {
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
(follow_symlinks ? 0 : O_NOFOLLOW);
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE)));
if (fd == -1) {
return false;
}
-
- bool result = WriteStringToFd(content, fd);
- close(fd);
- return result || CleanUpAfterFailedWrite(path);
+ return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
}
bool ReadFully(int fd, void* data, size_t byte_count) {
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
new file mode 100644
index 0000000..abcf4bc
--- /dev/null
+++ b/base/include/android-base/scopeguard.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 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 ANDROID_BASE_SCOPEGUARD_H
+#define ANDROID_BASE_SCOPEGUARD_H
+
+#include <utility> // for std::move
+
+namespace android {
+namespace base {
+
+template <typename F>
+class ScopeGuard {
+ public:
+ ScopeGuard(F f) : f_(f), active_(true) {}
+
+ ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
+ that.active_ = false;
+ }
+
+ ~ScopeGuard() {
+ if (active_) f_();
+ }
+
+ ScopeGuard() = delete;
+ ScopeGuard(const ScopeGuard&) = delete;
+ void operator=(const ScopeGuard&) = delete;
+ void operator=(ScopeGuard&& that) = delete;
+
+ void Disable() { active_ = false; }
+
+ bool active() const { return active_; }
+
+ private:
+ F f_;
+ bool active_;
+};
+
+template <typename T>
+ScopeGuard<T> make_scope_guard(T f) {
+ return ScopeGuard<T>(f);
+}
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_SCOPEGUARD_H
diff --git a/base/scopeguard_test.cpp b/base/scopeguard_test.cpp
new file mode 100644
index 0000000..e11154a
--- /dev/null
+++ b/base/scopeguard_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "android-base/scopeguard.h"
+
+#include <utility>
+
+#include <gtest/gtest.h>
+
+TEST(scopeguard, normal) {
+ bool guarded_var = true;
+ {
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ }
+ ASSERT_FALSE(guarded_var);
+}
+
+TEST(scopeguard, disabled) {
+ bool guarded_var = true;
+ {
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ scopeguard.Disable();
+ }
+ ASSERT_TRUE(guarded_var);
+}
+
+TEST(scopeguard, moved) {
+ int guarded_var = true;
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ { decltype(scopeguard) new_guard(std::move(scopeguard)); }
+ EXPECT_FALSE(scopeguard.active());
+ ASSERT_FALSE(guarded_var);
+}
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index 95c9af5..bc90a6e 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -74,6 +74,7 @@
// -----------------------------------------------------------------------------
cc_test {
name: "bootstat_tests",
+ test_suites: ["device-tests"],
defaults: ["bootstat_defaults"],
host_supported: true,
static_libs: [
diff --git a/bootstat/AndroidTest.xml b/bootstat/AndroidTest.xml
new file mode 100644
index 0000000..f3783fa
--- /dev/null
+++ b/bootstat/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for bootstat_tests">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="bootstat_tests->/data/local/tmp/bootstat_tests" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="bootstat_tests" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 224444f..2be13c6 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -58,28 +58,31 @@
}
bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
- int timeout_ms) {
+ unsigned int timeout_ms) {
LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
unique_fd sockfd;
const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
- auto time_left = [timeout_ms, &end]() { return end - std::chrono::steady_clock::now(); };
+ auto time_left = [&end]() { return end - std::chrono::steady_clock::now(); };
auto set_timeout = [timeout_ms, &time_left](int sockfd) {
if (timeout_ms <= 0) {
- return -1;
+ return sockfd;
}
auto remaining = time_left();
if (remaining < decltype(remaining)::zero()) {
- LOG(ERROR) << "timeout expired";
+ LOG(ERROR) << "libdebuggerd_client: timeout expired";
return -1;
}
+
struct timeval timeout;
populate_timeval(&timeout, remaining);
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
+ PLOG(ERROR) << "libdebuggerd_client: failed to set receive timeout";
return -1;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {
+ PLOG(ERROR) << "libdebuggerd_client: failed to set send timeout";
return -1;
}
@@ -137,7 +140,9 @@
}
bool backtrace = dump_type == kDebuggerdBacktrace;
- send_signal(pid, backtrace);
+ if (!send_signal(pid, backtrace)) {
+ return false;
+ }
rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
if (rc == 0) {
@@ -158,8 +163,10 @@
// Forward output from the pipe to the output fd.
while (true) {
- auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_left());
- if (remaining_ms <= 1ms) {
+ auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_left()).count();
+ if (timeout_ms <= 0) {
+ remaining_ms = -1;
+ } else if (remaining_ms < 0) {
LOG(ERROR) << "libdebuggerd_client: timeout expired";
return false;
}
@@ -168,7 +175,7 @@
.fd = pipe_read.get(), .events = POLLIN, .revents = 0,
};
- rc = poll(&pfd, 1, remaining_ms.count());
+ rc = poll(&pfd, 1, remaining_ms);
if (rc == -1) {
if (errno == EINTR) {
continue;
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index 86d0314..aff03e5 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -89,3 +89,23 @@
EXPECT_EQ(1, found_end) << "\nOutput: \n" << result;
}
+
+TEST(debuggerd_client, no_timeout) {
+ unique_fd pipe_read, pipe_write;
+ ASSERT_TRUE(Pipe(&pipe_read, &pipe_write));
+
+ pid_t forkpid = fork();
+ ASSERT_NE(-1, forkpid);
+ if (forkpid == 0) {
+ pipe_write.reset();
+ char dummy;
+ TEMP_FAILURE_RETRY(read(pipe_read.get(), &dummy, sizeof(dummy)));
+ exit(0);
+ }
+
+ pipe_read.reset();
+
+ unique_fd output_read, output_write;
+ ASSERT_TRUE(Pipe(&output_read, &output_write));
+ ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(output_write), kDebuggerdBacktrace, 0));
+}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index fa2838e..b705e27 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -500,9 +500,6 @@
TEST(crash_dump, zombie) {
pid_t forkpid = fork();
- int pipefd[2];
- ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
-
pid_t rc;
int status;
diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h
index 91f143b..01de57b 100644
--- a/debuggerd/include/debuggerd/client.h
+++ b/debuggerd/include/debuggerd/client.h
@@ -28,9 +28,9 @@
};
// Trigger a dump of specified process to output_fd.
-// output_fd is *not* consumed, timeouts <= 0 will wait forever.
+// output_fd is consumed, timeout of 0 will wait forever.
bool debuggerd_trigger_dump(pid_t pid, android::base::unique_fd output_fd,
- enum DebuggerdDumpType dump_type, int timeout_ms);
+ enum DebuggerdDumpType dump_type, unsigned int timeout_ms);
int dump_backtrace_to_file(pid_t tid, int fd);
int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index 41ca2e1..08ead48 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -75,7 +75,8 @@
uint32_t uncompressed_size;
uint16_t last_mod_time;
uint16_t last_mod_date;
- uint32_t local_file_header_offset;
+ uint32_t padding_length;
+ off64_t local_file_header_offset;
};
static const char* ErrorCodeString(int32_t error_code);
@@ -172,6 +173,7 @@
};
FILE* file_;
+ bool seekable_;
off64_t current_offset_;
State state_;
std::vector<FileEntry> files_;
diff --git a/init/Android.mk b/init/Android.mk
index 1ca88d7..730ffc4 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -62,6 +62,7 @@
action.cpp \
capabilities.cpp \
descriptors.cpp \
+ devices.cpp \
import_parser.cpp \
init_parser.cpp \
log.cpp \
@@ -81,7 +82,6 @@
LOCAL_SRC_FILES:= \
bootchart.cpp \
builtins.cpp \
- devices.cpp \
init.cpp \
keychords.cpp \
property_service.cpp \
@@ -138,6 +138,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE := init_tests
LOCAL_SRC_FILES := \
+ devices_test.cpp \
init_parser_test.cpp \
property_service_test.cpp \
util_test.cpp \
diff --git a/init/devices.cpp b/init/devices.cpp
index 4dc6468..760e0f5 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -282,8 +282,7 @@
}
}
-static void add_platform_device(const char *path)
-{
+void add_platform_device(const char* path) {
int path_len = strlen(path);
struct platform_node *bus;
const char *name = path;
@@ -325,8 +324,7 @@
return NULL;
}
-static void remove_platform_device(const char *path)
-{
+void remove_platform_device(const char* path) {
struct listnode *node;
struct platform_node *bus;
@@ -469,8 +467,7 @@
}
}
-static char **get_character_device_symlinks(struct uevent *uevent)
-{
+char** get_character_device_symlinks(struct uevent* uevent) {
const char *parent;
const char *slash;
char **links;
@@ -522,8 +519,24 @@
return NULL;
}
-static char **get_block_device_symlinks(struct uevent *uevent)
-{
+// replaces any unacceptable characters with '_', the
+// length of the resulting string is equal to the input string
+void sanitize_partition_name(char* s) {
+ const char* accept =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "_-.";
+
+ if (!s) return;
+
+ while (*s) {
+ s += strspn(s, accept);
+ if (*s) *s++ = '_';
+ }
+}
+
+char** get_block_device_symlinks(struct uevent* uevent) {
const char *device;
struct platform_node *pdev;
const char *slash;
@@ -558,7 +571,7 @@
if (uevent->partition_name) {
p = strdup(uevent->partition_name);
- sanitize(p);
+ sanitize_partition_name(p);
if (strcmp(uevent->partition_name, p)) {
LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '" << p << "'";
}
diff --git a/init/devices.h b/init/devices.h
index 83f0eef..abf1e7c 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -56,4 +56,11 @@
unsigned short wildcard);
int get_device_fd();
-#endif /* _INIT_DEVICES_H */
+// Exposed for testing
+void add_platform_device(const char* path);
+void remove_platform_device(const char* path);
+char** get_character_device_symlinks(uevent* uevent);
+char** get_block_device_symlinks(struct uevent* uevent);
+void sanitize_partition_name(char* s);
+
+#endif /* _INIT_DEVICES_H */
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
new file mode 100644
index 0000000..f79c96d
--- /dev/null
+++ b/init/devices_test.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 "devices.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/scopeguard.h>
+#include <gtest/gtest.h>
+
+template <char** (*Function)(uevent*)>
+void test_get_symlinks(const std::string& platform_device_name, uevent* uevent,
+ const std::vector<std::string> expected_links) {
+ add_platform_device(platform_device_name.c_str());
+ auto platform_device_remover = android::base::make_scope_guard(
+ [&platform_device_name]() { remove_platform_device(platform_device_name.c_str()); });
+
+ char** result = Function(uevent);
+ auto result_freer = android::base::make_scope_guard([result]() {
+ if (result) {
+ for (int i = 0; result[i]; i++) {
+ free(result[i]);
+ }
+ free(result);
+ }
+ });
+
+ auto expected_size = expected_links.size();
+ if (expected_size == 0) {
+ ASSERT_EQ(nullptr, result);
+ } else {
+ ASSERT_NE(nullptr, result);
+ // First assert size is equal, so we don't overrun expected_links
+ unsigned int size = 0;
+ while (result[size]) ++size;
+ ASSERT_EQ(expected_size, size);
+
+ for (unsigned int i = 0; i < size; ++i) {
+ EXPECT_EQ(expected_links[i], result[i]);
+ }
+ }
+}
+
+TEST(devices, get_character_device_symlinks_success) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
+ .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result{"/dev/usb/ttyname"};
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_pdev_match) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/device/name/tty2-1:1.0", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_nothing_after_platform_device) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_usb_found) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_roothub) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_usb_device) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_final_slash) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_final_name) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform) {
+ // These are actual paths from bullhead
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0",
+ .partition_name = nullptr,
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition) {
+ // These are actual paths from bullhead
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = "modem",
+ .partition_num = 1,
+ };
+ std::vector<std::string> expected_result{
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+ "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+ };
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_num) {
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = nullptr,
+ .partition_num = 1,
+ };
+ std::vector<std::string> expected_result{
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+ "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+ };
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_name) {
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = "modem",
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+ "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+ };
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_pci) {
+ const char* platform_device = "/devices/do/not/match";
+ uevent uevent = {
+ .path = "/devices/pci0000:00/0000:00:1f.2/mmcblk0",
+ .partition_name = nullptr,
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_vbd) {
+ const char* platform_device = "/devices/do/not/match";
+ uevent uevent = {
+ .path = "/devices/vbd-1234/mmcblk0", .partition_name = nullptr, .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_no_matches) {
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ uevent uevent = {
+ .path = "/devices/soc.0/not_the_device/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = nullptr,
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, sanitize_null) {
+ sanitize_partition_name(nullptr);
+}
+
+TEST(devices, sanitize_empty) {
+ std::string empty;
+ sanitize_partition_name(&empty[0]);
+ EXPECT_EQ(0u, empty.size());
+}
+
+TEST(devices, sanitize_allgood) {
+ std::string good =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "_-.";
+ std::string good_copy = good;
+ sanitize_partition_name(&good[0]);
+ EXPECT_EQ(good_copy, good);
+}
+
+TEST(devices, sanitize_somebad) {
+ std::string string = "abc!@#$%^&*()";
+ sanitize_partition_name(&string[0]);
+ EXPECT_EQ("abc__________", string);
+}
+
+TEST(devices, sanitize_allbad) {
+ std::string string = "!@#$%^&*()";
+ sanitize_partition_name(&string[0]);
+ EXPECT_EQ("__________", string);
+}
+
+TEST(devices, sanitize_onebad) {
+ std::string string = ")";
+ sanitize_partition_name(&string[0]);
+ EXPECT_EQ("_", string);
+}
diff --git a/init/util.cpp b/init/util.cpp
index b33e3ff..bbc59cb 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -228,27 +228,6 @@
return 0;
}
-/*
- * replaces any unacceptable characters with '_', the
- * length of the resulting string is equal to the input string
- */
-void sanitize(char *s)
-{
- const char* accept =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789"
- "_-.";
-
- if (!s)
- return;
-
- while (*s) {
- s += strspn(s, accept);
- if (*s) *s++ = '_';
- }
-}
-
int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
boot_clock::time_point timeout_time = boot_clock::now() + timeout;
while (boot_clock::now() < timeout_time) {
diff --git a/init/util.h b/init/util.h
index 1034c9b..38a7bdb 100644
--- a/init/util.h
+++ b/init/util.h
@@ -61,7 +61,6 @@
unsigned int decode_uid(const char *s);
int mkdir_recursive(const char *pathname, mode_t mode);
-void sanitize(char *p);
int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
void import_kernel_cmdline(bool in_qemu,
const std::function<void(const std::string&, const std::string&, bool)>&);
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index f729faf..e659f79 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -24,6 +24,7 @@
cc_test {
name: "libappfuse_test",
+ test_suites: ["device-tests"],
defaults: ["libappfuse_defaults"],
shared_libs: ["libappfuse"],
srcs: [
diff --git a/libappfuse/AndroidTest.xml b/libappfuse/AndroidTest.xml
new file mode 100644
index 0000000..a9cd754
--- /dev/null
+++ b/libappfuse/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for libappfuse_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="libappfuse_test->/data/local/tmp/libappfuse_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="libappfuse_test" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index c663a5d..a0b1d7b 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -62,6 +62,7 @@
cc_test {
name: "libcutils_test",
+ test_suites: ["device-tests"],
defaults: ["libcutils_test_default"],
host_supported: true,
shared_libs: test_libraries,
diff --git a/libcutils/tests/AndroidTest.xml b/libcutils/tests/AndroidTest.xml
index c945f4d..dd7aca2 100644
--- a/libcutils/tests/AndroidTest.xml
+++ b/libcutils/tests/AndroidTest.xml
@@ -13,14 +13,14 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Config for libcutils_test_static">
+<configuration description="Config for libcutils_test">
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
- <option name="push" value="libcutils_test_static->/data/local/tmp/libcutils_test_static" />
+ <option name="push" value="libcutils_test->/data/local/tmp/libcutils_test" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="libcutils_test_static" />
+ <option name="module-name" value="libcutils_test" />
</test>
</configuration>
\ No newline at end of file
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index bdad2c2..0b977c2 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -445,7 +445,7 @@
mmap(NULL, end[which], which ? PROT_READ : PROT_READ | PROT_WRITE,
which ? MAP_SHARED : MAP_PRIVATE, fd[which], 0);
save_errno = errno;
- close(fd[which]);
+ close(fd[which]); /* fd DONE */
fd[which] = -1;
if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
(newTagMap->mapAddr[which] != NULL)) {
@@ -465,6 +465,7 @@
delete newTagMap;
return NULL;
}
+ /* See 'fd DONE' comments above and below, no need to clean up here */
}
return newTagMap;
@@ -473,7 +474,7 @@
save_errno = EINVAL;
delete newTagMap;
fail_close:
- for (which = 0; which < NUM_MAPS; ++which) close(fd[which]);
+ for (which = 0; which < NUM_MAPS; ++which) close(fd[which]); /* fd DONE */
fail_errno:
errno = save_errno;
return NULL;
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 0538c4c..70b8a28 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1984,6 +1984,8 @@
EXPECT_EQ(0, setuid(AID_SYSTEM)); // only one that can read security buffer
+ uid = getuid();
+ gid = getgid();
pid_t pid = getpid();
ASSERT_TRUE(NULL !=
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index dabeac1..ee646de 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -48,12 +48,14 @@
srcs: [
"ArmExidx.cpp",
"DwarfMemory.cpp",
+ "DwarfOp.cpp",
"Elf.cpp",
"ElfInterface.cpp",
"ElfInterfaceArm.cpp",
"Log.cpp",
"Regs.cpp",
"Memory.cpp",
+ "Symbols.cpp",
],
shared_libs: [
@@ -89,6 +91,8 @@
"tests/ArmExidxDecodeTest.cpp",
"tests/ArmExidxExtractTest.cpp",
"tests/DwarfMemoryTest.cpp",
+ "tests/DwarfOpLogTest.cpp",
+ "tests/DwarfOpTest.cpp",
"tests/ElfInterfaceArmTest.cpp",
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
@@ -99,6 +103,7 @@
"tests/MemoryRangeTest.cpp",
"tests/MemoryRemoteTest.cpp",
"tests/RegsTest.cpp",
+ "tests/SymbolsTest.cpp",
],
cflags: [
diff --git a/libunwindstack/DwarfError.h b/libunwindstack/DwarfError.h
new file mode 100644
index 0000000..824c307
--- /dev/null
+++ b/libunwindstack/DwarfError.h
@@ -0,0 +1,32 @@
+/*
+ * 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_ERROR_H
+#define _LIBUNWINDSTACK_DWARF_ERROR_H
+
+#include <stdint.h>
+
+enum DwarfError : uint8_t {
+ DWARF_ERROR_NONE,
+ DWARF_ERROR_MEMORY_INVALID,
+ DWARF_ERROR_ILLEGAL_VALUE,
+ DWARF_ERROR_ILLEGAL_STATE,
+ DWARF_ERROR_STACK_INDEX_NOT_VALID,
+ DWARF_ERROR_NOT_IMPLEMENTED,
+ DWARF_ERROR_TOO_MANY_ITERATIONS,
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
new file mode 100644
index 0000000..507ca08
--- /dev/null
+++ b/libunwindstack/DwarfOp.cpp
@@ -0,0 +1,462 @@
+/*
+ * 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 <deque>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Memory.h"
+#include "Regs.h"
+
+template <typename AddressType>
+constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end, uint8_t dwarf_version) {
+ uint32_t iterations = 0;
+ is_register_ = false;
+ stack_.clear();
+ memory_->set_cur_offset(start);
+ while (memory_->cur_offset() < end) {
+ if (!Decode(dwarf_version)) {
+ return false;
+ }
+ // To protect against a branch that creates an infinite loop,
+ // terminate if the number of iterations gets too high.
+ if (iterations++ == 1000) {
+ last_error_ = DWARF_ERROR_TOO_MANY_ITERATIONS;
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::Decode(uint8_t dwarf_version) {
+ last_error_ = DWARF_ERROR_NONE;
+ if (!memory_->ReadBytes(&cur_op_, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ const auto* op = &kCallbackTable[cur_op_];
+ const auto handle_func = op->handle_func;
+ if (handle_func == nullptr) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Check for an unsupported opcode.
+ if (dwarf_version < op->supported_version) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Make sure that the required number of stack elements is available.
+ if (stack_.size() < op->num_required_stack_values) {
+ last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+ return false;
+ }
+
+ operands_.clear();
+ for (size_t i = 0; i < op->num_operands; i++) {
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ operands_.push_back(value);
+ }
+ return (this->*handle_func)();
+}
+
+template <typename AddressType>
+void DwarfOp<AddressType>::GetLogInfo(uint64_t start, uint64_t end,
+ std::vector<std::string>* lines) {
+ memory_->set_cur_offset(start);
+ while (memory_->cur_offset() < end) {
+ uint8_t cur_op;
+ if (!memory_->ReadBytes(&cur_op, 1)) {
+ return;
+ }
+
+ std::string raw_string(android::base::StringPrintf("Raw Data: 0x%02x", cur_op));
+ std::string log_string;
+ const auto* op = &kCallbackTable[cur_op];
+ if (op->handle_func == nullptr) {
+ log_string = "Illegal";
+ } else {
+ log_string = op->name;
+ uint64_t start_offset = memory_->cur_offset();
+ for (size_t i = 0; i < op->num_operands; i++) {
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
+ return;
+ }
+ log_string += ' ' + std::to_string(value);
+ }
+ uint64_t end_offset = memory_->cur_offset();
+
+ memory_->set_cur_offset(start_offset);
+ for (size_t i = start_offset; i < end_offset; i++) {
+ uint8_t byte;
+ if (!memory_->ReadBytes(&byte, 1)) {
+ return;
+ }
+ raw_string += android::base::StringPrintf(" 0x%02x", byte);
+ }
+ memory_->set_cur_offset(end_offset);
+ }
+ lines->push_back(std::move(log_string));
+ lines->push_back(std::move(raw_string));
+ }
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_deref() {
+ // Read the address and dereference it.
+ AddressType addr = StackPop();
+ AddressType value;
+ if (!regular_memory()->Read(addr, &value, sizeof(value))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ stack_.push_front(value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_deref_size() {
+ AddressType bytes_to_read = OperandAt(0);
+ if (bytes_to_read > sizeof(AddressType) || bytes_to_read == 0) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ // Read the address and dereference it.
+ AddressType addr = StackPop();
+ AddressType value = 0;
+ if (!regular_memory()->Read(addr, &value, bytes_to_read)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ stack_.push_front(value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_push() {
+ // Push all of the operands.
+ for (auto operand : operands_) {
+ stack_.push_front(operand);
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_dup() {
+ stack_.push_front(StackAt(0));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_drop() {
+ StackPop();
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_over() {
+ stack_.push_front(StackAt(1));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_pick() {
+ AddressType index = OperandAt(0);
+ if (index > StackSize()) {
+ last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+ return false;
+ }
+ stack_.push_front(StackAt(index));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_swap() {
+ AddressType old_value = stack_[0];
+ stack_[0] = stack_[1];
+ stack_[1] = old_value;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_rot() {
+ AddressType top = stack_[0];
+ stack_[0] = stack_[1];
+ stack_[1] = stack_[2];
+ stack_[2] = top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_abs() {
+ SignedType signed_value = static_cast<SignedType>(stack_[0]);
+ if (signed_value < 0) {
+ signed_value = -signed_value;
+ }
+ stack_[0] = static_cast<AddressType>(signed_value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_and() {
+ AddressType top = StackPop();
+ stack_[0] &= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_div() {
+ AddressType top = StackPop();
+ if (top == 0) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ SignedType signed_divisor = static_cast<SignedType>(top);
+ SignedType signed_dividend = static_cast<SignedType>(stack_[0]);
+ stack_[0] = static_cast<AddressType>(signed_dividend / signed_divisor);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_minus() {
+ AddressType top = StackPop();
+ stack_[0] -= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_mod() {
+ AddressType top = StackPop();
+ if (top == 0) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ stack_[0] %= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_mul() {
+ AddressType top = StackPop();
+ stack_[0] *= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_neg() {
+ SignedType signed_value = static_cast<SignedType>(stack_[0]);
+ stack_[0] = static_cast<AddressType>(-signed_value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_not() {
+ stack_[0] = ~stack_[0];
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_or() {
+ AddressType top = StackPop();
+ stack_[0] |= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_plus() {
+ AddressType top = StackPop();
+ stack_[0] += top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_plus_uconst() {
+ stack_[0] += OperandAt(0);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shl() {
+ AddressType top = StackPop();
+ stack_[0] <<= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shr() {
+ AddressType top = StackPop();
+ stack_[0] >>= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shra() {
+ AddressType top = StackPop();
+ SignedType signed_value = static_cast<SignedType>(stack_[0]) >> top;
+ stack_[0] = static_cast<AddressType>(signed_value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_xor() {
+ AddressType top = StackPop();
+ stack_[0] ^= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_bra() {
+ // Requires one stack element.
+ AddressType top = StackPop();
+ int16_t offset = static_cast<int16_t>(OperandAt(0));
+ uint64_t cur_offset;
+ if (top != 0) {
+ cur_offset = memory_->cur_offset() + offset;
+ } else {
+ cur_offset = memory_->cur_offset() - offset;
+ }
+ memory_->set_cur_offset(cur_offset);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_eq() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] == top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_ge() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] >= top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_gt() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] > top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_le() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] <= top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_lt() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] < top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_ne() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] != top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_skip() {
+ int16_t offset = static_cast<int16_t>(OperandAt(0));
+ uint64_t cur_offset = memory_->cur_offset() + offset;
+ memory_->set_cur_offset(cur_offset);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_lit() {
+ stack_.push_front(cur_op() - 0x30);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_reg() {
+ is_register_ = true;
+ stack_.push_front(cur_op() - 0x50);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_regx() {
+ is_register_ = true;
+ stack_.push_front(OperandAt(0));
+ return true;
+}
+
+// It's not clear for breg/bregx, if this op should read the current
+// value of the register, or where we think that register is located.
+// For simplicity, the code will read the value before doing the unwind.
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_breg() {
+ uint16_t reg = cur_op() - 0x70;
+ if (reg >= regs_->total_regs()) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ stack_.push_front((*regs_)[reg] + OperandAt(0));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_bregx() {
+ AddressType reg = OperandAt(0);
+ if (reg >= regs_->total_regs()) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ stack_.push_front((*regs_)[reg] + OperandAt(1));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_nop() {
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_not_implemented() {
+ last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
+ return false;
+}
+
+// Explicitly instantiate DwarfOp.
+template class DwarfOp<uint32_t>;
+template class DwarfOp<uint64_t>;
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
new file mode 100644
index 0000000..ed6537a
--- /dev/null
+++ b/libunwindstack/DwarfOp.h
@@ -0,0 +1,1636 @@
+/*
+ * 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_OP_H
+#define _LIBUNWINDSTACK_DWARF_OP_H
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "DwarfEncoding.h"
+
+enum DwarfVersion : uint8_t {
+ DWARF_VERSION_2 = 2,
+ DWARF_VERSION_3 = 3,
+ DWARF_VERSION_4 = 4,
+ DWARF_VERSION_MAX = DWARF_VERSION_4,
+};
+
+// Forward declarations.
+class DwarfMemory;
+class Memory;
+template <typename AddressType>
+class RegsTmpl;
+
+template <typename AddressType>
+class DwarfOp {
+ // Signed version of AddressType
+ typedef typename std::make_signed<AddressType>::type SignedType;
+
+ struct OpCallback {
+ const char* name;
+ bool (DwarfOp::*handle_func)();
+ uint8_t supported_version;
+ uint8_t num_required_stack_values;
+ uint8_t num_operands;
+ uint8_t operands[2];
+ };
+
+ public:
+ DwarfOp(DwarfMemory* memory, Memory* regular_memory)
+ : memory_(memory), regular_memory_(regular_memory) {}
+ virtual ~DwarfOp() = default;
+
+ bool Decode(uint8_t dwarf_version);
+
+ bool Eval(uint64_t start, uint64_t end, uint8_t dwarf_version);
+
+ void GetLogInfo(uint64_t start, uint64_t end, std::vector<std::string>* lines);
+
+ AddressType StackAt(size_t index) { return stack_[index]; }
+ size_t StackSize() { return stack_.size(); }
+
+ void set_regs(RegsTmpl<AddressType>* regs) { regs_ = regs; }
+
+ DwarfError last_error() { return last_error_; }
+
+ bool is_register() { return is_register_; }
+
+ uint8_t cur_op() { return cur_op_; }
+
+ Memory* regular_memory() { return regular_memory_; }
+
+ protected:
+ AddressType OperandAt(size_t index) { return operands_[index]; }
+ size_t OperandsSize() { return operands_.size(); }
+
+ AddressType StackPop() {
+ AddressType value = stack_.front();
+ stack_.pop_front();
+ return value;
+ }
+
+ private:
+ DwarfMemory* memory_;
+ Memory* regular_memory_;
+
+ RegsTmpl<AddressType>* regs_;
+ bool is_register_ = false;
+ DwarfError last_error_ = DWARF_ERROR_NONE;
+ uint8_t cur_op_;
+ std::vector<AddressType> operands_;
+ std::deque<AddressType> stack_;
+
+ inline AddressType bool_to_dwarf_bool(bool value) { return value ? 1 : 0; }
+
+ // Op processing functions.
+ bool op_deref();
+ bool op_deref_size();
+ bool op_push();
+ bool op_dup();
+ bool op_drop();
+ bool op_over();
+ bool op_pick();
+ bool op_swap();
+ bool op_rot();
+ bool op_abs();
+ bool op_and();
+ bool op_div();
+ bool op_minus();
+ bool op_mod();
+ bool op_mul();
+ bool op_neg();
+ bool op_not();
+ bool op_or();
+ bool op_plus();
+ bool op_plus_uconst();
+ bool op_shl();
+ bool op_shr();
+ bool op_shra();
+ bool op_xor();
+ bool op_bra();
+ bool op_eq();
+ bool op_ge();
+ bool op_gt();
+ bool op_le();
+ bool op_lt();
+ bool op_ne();
+ bool op_skip();
+ bool op_lit();
+ bool op_reg();
+ bool op_regx();
+ bool op_breg();
+ bool op_bregx();
+ bool op_nop();
+ bool op_not_implemented();
+
+ constexpr static OpCallback kCallbackTable[256] = {
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x00 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x01 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x02 illegal op
+ {
+ // 0x03 DW_OP_addr
+ "DW_OP_addr",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_absptr},
+ },
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x04 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x05 illegal op
+ {
+ // 0x06 DW_OP_deref
+ "DW_OP_deref",
+ &DwarfOp::op_deref,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x07 illegal op
+ {
+ // 0x08 DW_OP_const1u
+ "DW_OP_const1u",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x09 DW_OP_const1s
+ "DW_OP_const1s",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata1},
+ },
+ {
+ // 0x0a DW_OP_const2u
+ "DW_OP_const2u",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata2},
+ },
+ {
+ // 0x0b DW_OP_const2s
+ "DW_OP_const2s",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x0c DW_OP_const4u
+ "DW_OP_const4u",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata4},
+ },
+ {
+ // 0x0d DW_OP_const4s
+ "DW_OP_const4s",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata4},
+ },
+ {
+ // 0x0e DW_OP_const8u
+ "DW_OP_const8u",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata8},
+ },
+ {
+ // 0x0f DW_OP_const8s
+ "DW_OP_const8s",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata8},
+ },
+ {
+ // 0x10 DW_OP_constu
+ "DW_OP_constu",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x11 DW_OP_consts
+ "DW_OP_consts",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x12 DW_OP_dup
+ "DW_OP_dup",
+ &DwarfOp::op_dup,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x13 DW_OP_drop
+ "DW_OP_drop",
+ &DwarfOp::op_drop,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x14 DW_OP_over
+ "DW_OP_over",
+ &DwarfOp::op_over,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x15 DW_OP_pick
+ "DW_OP_pick",
+ &DwarfOp::op_pick,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x16 DW_OP_swap
+ "DW_OP_swap",
+ &DwarfOp::op_swap,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x17 DW_OP_rot
+ "DW_OP_rot",
+ &DwarfOp::op_rot,
+ DWARF_VERSION_2,
+ 3,
+ 0,
+ {},
+ },
+ {
+ // 0x18 DW_OP_xderef
+ "DW_OP_xderef",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x19 DW_OP_abs
+ "DW_OP_abs",
+ &DwarfOp::op_abs,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x1a DW_OP_and
+ "DW_OP_and",
+ &DwarfOp::op_and,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1b DW_OP_div
+ "DW_OP_div",
+ &DwarfOp::op_div,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1c DW_OP_minus
+ "DW_OP_minus",
+ &DwarfOp::op_minus,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1d DW_OP_mod
+ "DW_OP_mod",
+ &DwarfOp::op_mod,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1e DW_OP_mul
+ "DW_OP_mul",
+ &DwarfOp::op_mul,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1f DW_OP_neg
+ "DW_OP_neg",
+ &DwarfOp::op_neg,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x20 DW_OP_not
+ "DW_OP_not",
+ &DwarfOp::op_not,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x21 DW_OP_or
+ "DW_OP_or",
+ &DwarfOp::op_or,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x22 DW_OP_plus
+ "DW_OP_plus",
+ &DwarfOp::op_plus,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x23 DW_OP_plus_uconst
+ "DW_OP_plus_uconst",
+ &DwarfOp::op_plus_uconst,
+ DWARF_VERSION_2,
+ 1,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x24 DW_OP_shl
+ "DW_OP_shl",
+ &DwarfOp::op_shl,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x25 DW_OP_shr
+ "DW_OP_shr",
+ &DwarfOp::op_shr,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x26 DW_OP_shra
+ "DW_OP_shra",
+ &DwarfOp::op_shra,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x27 DW_OP_xor
+ "DW_OP_xor",
+ &DwarfOp::op_xor,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x28 DW_OP_bra
+ "DW_OP_bra",
+ &DwarfOp::op_bra,
+ DWARF_VERSION_2,
+ 1,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x29 DW_OP_eq
+ "DW_OP_eq",
+ &DwarfOp::op_eq,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2a DW_OP_ge
+ "DW_OP_ge",
+ &DwarfOp::op_ge,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2b DW_OP_gt
+ "DW_OP_gt",
+ &DwarfOp::op_gt,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2c DW_OP_le
+ "DW_OP_le",
+ &DwarfOp::op_le,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2d DW_OP_lt
+ "DW_OP_lt",
+ &DwarfOp::op_lt,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2e DW_OP_ne
+ "DW_OP_ne",
+ &DwarfOp::op_ne,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2f DW_OP_skip
+ "DW_OP_skip",
+ &DwarfOp::op_skip,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x30 DW_OP_lit0
+ "DW_OP_lit0",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x31 DW_OP_lit1
+ "DW_OP_lit1",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x32 DW_OP_lit2
+ "DW_OP_lit2",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x33 DW_OP_lit3
+ "DW_OP_lit3",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x34 DW_OP_lit4
+ "DW_OP_lit4",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x35 DW_OP_lit5
+ "DW_OP_lit5",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x36 DW_OP_lit6
+ "DW_OP_lit6",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x37 DW_OP_lit7
+ "DW_OP_lit7",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x38 DW_OP_lit8
+ "DW_OP_lit8",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x39 DW_OP_lit9
+ "DW_OP_lit9",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3a DW_OP_lit10
+ "DW_OP_lit10",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3b DW_OP_lit11
+ "DW_OP_lit11",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3c DW_OP_lit12
+ "DW_OP_lit12",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3d DW_OP_lit13
+ "DW_OP_lit13",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3e DW_OP_lit14
+ "DW_OP_lit14",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3f DW_OP_lit15
+ "DW_OP_lit15",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x40 DW_OP_lit16
+ "DW_OP_lit16",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x41 DW_OP_lit17
+ "DW_OP_lit17",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x42 DW_OP_lit18
+ "DW_OP_lit18",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x43 DW_OP_lit19
+ "DW_OP_lit19",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x44 DW_OP_lit20
+ "DW_OP_lit20",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x45 DW_OP_lit21
+ "DW_OP_lit21",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x46 DW_OP_lit22
+ "DW_OP_lit22",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x47 DW_OP_lit23
+ "DW_OP_lit23",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x48 DW_OP_lit24
+ "DW_OP_lit24",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x49 DW_OP_lit25
+ "DW_OP_lit25",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4a DW_OP_lit26
+ "DW_OP_lit26",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4b DW_OP_lit27
+ "DW_OP_lit27",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4c DW_OP_lit28
+ "DW_OP_lit28",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4d DW_OP_lit29
+ "DW_OP_lit29",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4e DW_OP_lit30
+ "DW_OP_lit30",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4f DW_OP_lit31
+ "DW_OP_lit31",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x50 DW_OP_reg0
+ "DW_OP_reg0",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x51 DW_OP_reg1
+ "DW_OP_reg1",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x52 DW_OP_reg2
+ "DW_OP_reg2",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x53 DW_OP_reg3
+ "DW_OP_reg3",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x54 DW_OP_reg4
+ "DW_OP_reg4",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x55 DW_OP_reg5
+ "DW_OP_reg5",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x56 DW_OP_reg6
+ "DW_OP_reg6",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x57 DW_OP_reg7
+ "DW_OP_reg7",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x58 DW_OP_reg8
+ "DW_OP_reg8",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x59 DW_OP_reg9
+ "DW_OP_reg9",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5a DW_OP_reg10
+ "DW_OP_reg10",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5b DW_OP_reg11
+ "DW_OP_reg11",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5c DW_OP_reg12
+ "DW_OP_reg12",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5d DW_OP_reg13
+ "DW_OP_reg13",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5e DW_OP_reg14
+ "DW_OP_reg14",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5f DW_OP_reg15
+ "DW_OP_reg15",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x60 DW_OP_reg16
+ "DW_OP_reg16",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x61 DW_OP_reg17
+ "DW_OP_reg17",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x62 DW_OP_reg18
+ "DW_OP_reg18",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x63 DW_OP_reg19
+ "DW_OP_reg19",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x64 DW_OP_reg20
+ "DW_OP_reg20",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x65 DW_OP_reg21
+ "DW_OP_reg21",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x66 DW_OP_reg22
+ "DW_OP_reg22",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x67 DW_OP_reg23
+ "DW_OP_reg23",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x68 DW_OP_reg24
+ "DW_OP_reg24",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x69 DW_OP_reg25
+ "DW_OP_reg25",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6a DW_OP_reg26
+ "DW_OP_reg26",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6b DW_OP_reg27
+ "DW_OP_reg27",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6c DW_OP_reg28
+ "DW_OP_reg28",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6d DW_OP_reg29
+ "DW_OP_reg29",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6e DW_OP_reg30
+ "DW_OP_reg30",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6f DW_OP_reg31
+ "DW_OP_reg31",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x70 DW_OP_breg0
+ "DW_OP_breg0",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x71 DW_OP_breg1
+ "DW_OP_breg1",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x72 DW_OP_breg2
+ "DW_OP_breg2",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x73 DW_OP_breg3
+ "DW_OP_breg3",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x74 DW_OP_breg4
+ "DW_OP_breg4",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x75 DW_OP_breg5
+ "DW_OP_breg5",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x76 DW_OP_breg6
+ "DW_OP_breg6",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x77 DW_OP_breg7
+ "DW_OP_breg7",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x78 DW_OP_breg8
+ "DW_OP_breg8",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x79 DW_OP_breg9
+ "DW_OP_breg9",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7a DW_OP_breg10
+ "DW_OP_breg10",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7b DW_OP_breg11
+ "DW_OP_breg11",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7c DW_OP_breg12
+ "DW_OP_breg12",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7d DW_OP_breg13
+ "DW_OP_breg13",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7e DW_OP_breg14
+ "DW_OP_breg14",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7f DW_OP_breg15
+ "DW_OP_breg15",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x80 DW_OP_breg16
+ "DW_OP_breg16",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x81 DW_OP_breg17
+ "DW_OP_breg17",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x82 DW_OP_breg18
+ "DW_OP_breg18",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x83 DW_OP_breg19
+ "DW_OP_breg19",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x84 DW_OP_breg20
+ "DW_OP_breg20",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x85 DW_OP_breg21
+ "DW_OP_breg21",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x86 DW_OP_breg22
+ "DW_OP_breg22",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x87 DW_OP_breg23
+ "DW_OP_breg23",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x88 DW_OP_breg24
+ "DW_OP_breg24",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x89 DW_OP_breg25
+ "DW_OP_breg25",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8a DW_OP_breg26
+ "DW_OP_breg26",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8b DW_OP_breg27
+ "DW_OP_breg27",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8c DW_OP_breg28
+ "DW_OP_breg28",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8d DW_OP_breg29
+ "DW_OP_breg29",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8e DW_OP_breg30
+ "DW_OP_breg30",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8f DW_OP_breg31
+ "DW_OP_breg31",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x90 DW_OP_regx
+ "DW_OP_regx",
+ &DwarfOp::op_regx,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x91 DW_OP_fbreg
+ "DW_OP_fbreg",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x92 DW_OP_bregx
+ "DW_OP_bregx",
+ &DwarfOp::op_bregx,
+ DWARF_VERSION_2,
+ 0,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ },
+ {
+ // 0x93 DW_OP_piece
+ "DW_OP_piece",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x94 DW_OP_deref_size
+ "DW_OP_deref_size",
+ &DwarfOp::op_deref_size,
+ DWARF_VERSION_2,
+ 1,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x95 DW_OP_xderef_size
+ "DW_OP_xderef_size",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x96 DW_OP_nop
+ "DW_OP_nop",
+ &DwarfOp::op_nop,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x97 DW_OP_push_object_address
+ "DW_OP_push_object_address",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x98 DW_OP_call2
+ "DW_OP_call2",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 1,
+ {DW_EH_PE_udata2},
+ },
+ {
+ // 0x99 DW_OP_call4
+ "DW_OP_call4",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 1,
+ {DW_EH_PE_udata4},
+ },
+ {
+ // 0x9a DW_OP_call_ref
+ "DW_OP_call_ref",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 0, // Has a different sized operand (4 bytes or 8 bytes).
+ {},
+ },
+ {
+ // 0x9b DW_OP_form_tls_address
+ "DW_OP_form_tls_address",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x9c DW_OP_call_frame_cfa
+ "DW_OP_call_frame_cfa",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x9d DW_OP_bit_piece
+ "DW_OP_bit_piece",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ },
+ {
+ // 0x9e DW_OP_implicit_value
+ "DW_OP_implicit_value",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_4,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x9f DW_OP_stack_value
+ "DW_OP_stack_value",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_4,
+ 1,
+ 0,
+ {},
+ },
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xaa illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xab illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xac illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xad illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xae illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xaf illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xba illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbc illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbd illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbe illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbf illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xca illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xcb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xcc illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xcd illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xce illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xcf illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xda illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xdb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xdc illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xdd illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xde illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xdf illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe0 DW_OP_lo_user
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xea illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xeb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xec illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xed illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xee illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xef illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfa illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfc illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfd illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfe illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xff DW_OP_hi_user
+ };
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
new file mode 100644
index 0000000..86c1233
--- /dev/null
+++ b/libunwindstack/Symbols.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 <elf.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "Memory.h"
+#include "Symbols.h"
+
+Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
+ uint64_t str_size)
+ : cur_offset_(offset),
+ offset_(offset),
+ end_(offset + size),
+ entry_size_(entry_size),
+ str_offset_(str_offset),
+ str_end_(str_offset_ + str_size) {}
+
+const Symbols::Info* Symbols::GetInfoFromCache(uint64_t addr) {
+ // Binary search the table.
+ size_t first = 0;
+ size_t last = symbols_.size();
+ while (first < last) {
+ size_t current = first + (last - first) / 2;
+ const Info* info = &symbols_[current];
+ if (addr < info->start_offset) {
+ last = current;
+ } else if (addr < info->end_offset) {
+ return info;
+ } else {
+ first = current + 1;
+ }
+ }
+ return nullptr;
+}
+
+template <typename SymType>
+bool Symbols::GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
+ uint64_t* func_offset) {
+ addr += load_bias;
+
+ if (symbols_.size() != 0) {
+ const Info* info = GetInfoFromCache(addr);
+ if (info) {
+ assert(addr >= info->start_offset && addr <= info->end_offset);
+ *func_offset = addr - info->start_offset;
+ return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset);
+ }
+ }
+
+ bool symbol_added = false;
+ bool return_value = false;
+ while (cur_offset_ + entry_size_ <= end_) {
+ SymType entry;
+ if (!elf_memory->Read(cur_offset_, &entry, sizeof(entry))) {
+ // Stop all processing, something looks like it is corrupted.
+ cur_offset_ = UINT64_MAX;
+ return false;
+ }
+ cur_offset_ += entry_size_;
+
+ if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
+ // Treat st_value as virtual address.
+ uint64_t start_offset = entry.st_value;
+ if (entry.st_shndx != SHN_ABS) {
+ start_offset += load_bias;
+ }
+ uint64_t end_offset = start_offset + entry.st_size;
+
+ // Cache the value.
+ symbols_.emplace_back(start_offset, end_offset, str_offset_ + entry.st_name);
+ symbol_added = true;
+
+ if (addr >= start_offset && addr < end_offset) {
+ *func_offset = addr - start_offset;
+ uint64_t offset = str_offset_ + entry.st_name;
+ if (offset < str_end_) {
+ return_value = elf_memory->ReadString(offset, name, str_end_ - offset);
+ }
+ break;
+ }
+ }
+ }
+
+ if (symbol_added) {
+ std::sort(symbols_.begin(), symbols_.end(),
+ [](const Info& a, const Info& b) { return a.start_offset < b.start_offset; });
+ }
+ return return_value;
+}
+
+// Instantiate all of the needed template functions.
+template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
new file mode 100644
index 0000000..3c0d033
--- /dev/null
+++ b/libunwindstack/Symbols.h
@@ -0,0 +1,64 @@
+/*
+ * 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_SYMBOLS_H
+#define _LIBUNWINDSTACK_SYMBOLS_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+// Forward declaration.
+class Memory;
+
+class Symbols {
+ struct Info {
+ Info(uint64_t start_offset, uint64_t end_offset, uint64_t str_offset)
+ : start_offset(start_offset), end_offset(end_offset), str_offset(str_offset) {}
+ uint64_t start_offset;
+ uint64_t end_offset;
+ uint64_t str_offset;
+ };
+
+ public:
+ Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
+ uint64_t str_size);
+ virtual ~Symbols() = default;
+
+ const Info* GetInfoFromCache(uint64_t addr);
+
+ template <typename SymType>
+ bool GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
+ uint64_t* func_offset);
+
+ void ClearCache() {
+ symbols_.clear();
+ cur_offset_ = offset_;
+ }
+
+ private:
+ uint64_t cur_offset_;
+ uint64_t offset_;
+ uint64_t end_;
+ uint64_t entry_size_;
+ uint64_t str_offset_;
+ uint64_t str_end_;
+
+ std::vector<Info> symbols_;
+};
+
+#endif // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
new file mode 100644
index 0000000..d18aad0
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Regs.h"
+
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class DwarfOpLogTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ op_memory_.Clear();
+ regular_memory_.Clear();
+ mem_.reset(new DwarfMemory(&op_memory_));
+ op_.reset(new DwarfOp<TypeParam>(mem_.get(), ®ular_memory_));
+ }
+
+ MemoryFake op_memory_;
+ MemoryFake regular_memory_;
+
+ std::unique_ptr<DwarfMemory> mem_;
+ std::unique_ptr<DwarfOp<TypeParam>> op_;
+};
+TYPED_TEST_CASE_P(DwarfOpLogTest);
+
+TYPED_TEST_P(DwarfOpLogTest, multiple_ops) {
+ // Multi operation opcodes.
+ std::vector<uint8_t> opcode_buffer = {
+ 0x0a, 0x20, 0x10, 0x08, 0x03, 0x12, 0x27,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ std::vector<std::string> lines;
+ this->op_->GetLogInfo(0, opcode_buffer.size(), &lines);
+ std::vector<std::string> expected{
+ "DW_OP_const2u 4128", "Raw Data: 0x0a 0x20 0x10", "DW_OP_const1u 3", "Raw Data: 0x08 0x03",
+ "DW_OP_dup", "Raw Data: 0x12", "DW_OP_xor", "Raw Data: 0x27"};
+ ASSERT_EQ(expected, lines);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfOpLogTest, multiple_ops);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
new file mode 100644
index 0000000..520c545
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -0,0 +1,1593 @@
+/*
+ * 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 <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Regs.h"
+
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class RegsFake : public RegsTmpl<TypeParam> {
+ public:
+ RegsFake(uint16_t total_regs, uint16_t sp_reg)
+ : RegsTmpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+ virtual ~RegsFake() = default;
+
+ uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
+ uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+ bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
+};
+
+template <typename TypeParam>
+class DwarfOpTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ op_memory_.Clear();
+ regular_memory_.Clear();
+ mem_.reset(new DwarfMemory(&op_memory_));
+ op_.reset(new DwarfOp<TypeParam>(mem_.get(), ®ular_memory_));
+ }
+
+ MemoryFake op_memory_;
+ MemoryFake regular_memory_;
+
+ std::unique_ptr<DwarfMemory> mem_;
+ std::unique_ptr<DwarfOp<TypeParam>> op_;
+};
+TYPED_TEST_CASE_P(DwarfOpTest);
+
+TYPED_TEST_P(DwarfOpTest, decode) {
+ // Memory error.
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+
+ // No error.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
+ this->mem_->set_cur_offset(0);
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+ ASSERT_EQ(0x96U, this->op_->cur_op());
+ ASSERT_EQ(1U, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, eval) {
+ // Memory error.
+ ASSERT_FALSE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+
+ // Register set.
+ // Do this first, to verify that subsequent calls reset the value.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x50});
+ ASSERT_TRUE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+ ASSERT_TRUE(this->op_->is_register());
+ ASSERT_EQ(1U, this->mem_->cur_offset());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ // Multi operation opcodes.
+ std::vector<uint8_t> opcode_buffer = {
+ 0x08, 0x04, 0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Eval(0, 8, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+ ASSERT_FALSE(this->op_->is_register());
+ ASSERT_EQ(8U, this->mem_->cur_offset());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(1U, this->op_->StackAt(0));
+ ASSERT_EQ(2U, this->op_->StackAt(1));
+ ASSERT_EQ(3U, this->op_->StackAt(2));
+ ASSERT_EQ(4U, this->op_->StackAt(3));
+
+ // Infinite loop.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x2f, 0xfd, 0xff});
+ ASSERT_FALSE(this->op_->Eval(0, 4, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->last_error());
+ ASSERT_FALSE(this->op_->is_register());
+ ASSERT_EQ(0U, this->op_->StackSize());
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_opcode) {
+ // Fill the buffer with all of the illegal opcodes.
+ std::vector<uint8_t> opcode_buffer = {0x00, 0x01, 0x02, 0x04, 0x05, 0x07};
+ for (size_t opcode = 0xa0; opcode < 256; opcode++) {
+ opcode_buffer.push_back(opcode);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+ ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_in_version3) {
+ std::vector<uint8_t> opcode_buffer = {0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d};
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ ASSERT_FALSE(this->op_->Decode(2));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+ ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_in_version4) {
+ std::vector<uint8_t> opcode_buffer = {0x9e, 0x9f};
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ ASSERT_FALSE(this->op_->Decode(3));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+ ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, not_implemented) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push values so that any not implemented ops will return the right error.
+ 0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
+ // xderef
+ 0x18,
+ // fbreg
+ 0x91, 0x01,
+ // piece
+ 0x93, 0x01,
+ // xderef_size
+ 0x95, 0x01,
+ // push_object_address
+ 0x97,
+ // call2
+ 0x98, 0x01, 0x02,
+ // call4
+ 0x99, 0x01, 0x02, 0x03, 0x04,
+ // call_ref
+ 0x9a,
+ // form_tls_address
+ 0x9b,
+ // call_frame_cfa
+ 0x9c,
+ // bit_piece
+ 0x9d, 0x01, 0x01,
+ // implicit_value
+ 0x9e, 0x01,
+ // stack_value
+ 0x9f,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // Push the stack values.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+
+ while (this->mem_->cur_offset() < opcode_buffer.size()) {
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->last_error());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_addr) {
+ std::vector<uint8_t> opcode_buffer = {0x03, 0x12, 0x23, 0x34, 0x45};
+ if (sizeof(TypeParam) == 8) {
+ opcode_buffer.push_back(0x56);
+ opcode_buffer.push_back(0x67);
+ opcode_buffer.push_back(0x78);
+ opcode_buffer.push_back(0x89);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x03, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x8978675645342312UL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_deref) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Try a dereference with nothing on the stack.
+ 0x06,
+ // Add an address, then dereference.
+ 0x0a, 0x10, 0x20, 0x06,
+ // Now do another dereference that should fail in memory.
+ 0x06,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+ TypeParam value = 0x12345678;
+ this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x06, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(value, this->op_->StackAt(0));
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_deref_size) {
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x94});
+ TypeParam value = 0x12345678;
+ this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ // Read all byte sizes up to the sizeof the type.
+ for (size_t i = 1; i < sizeof(TypeParam); i++) {
+ this->op_memory_.SetMemory(
+ 0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, static_cast<uint8_t>(i)});
+ ASSERT_TRUE(this->op_->Eval(0, 5, DWARF_VERSION_MAX)) << "Failed at size " << i;
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed at size " << i;
+ ASSERT_EQ(0x94, this->op_->cur_op()) << "Failed at size " << i;
+ TypeParam expected_value = 0;
+ memcpy(&expected_value, &value, i);
+ ASSERT_EQ(expected_value, this->op_->StackAt(0)) << "Failed at size " << i;
+ }
+
+ // Zero byte read.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, 0x00});
+ ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+
+ // Read too many bytes.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, sizeof(TypeParam) + 1});
+ ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+
+ // Force bad memory read.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x40, 0x94, 0x01});
+ ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, const_unsigned) {
+ std::vector<uint8_t> opcode_buffer = {
+ // const1u
+ 0x08, 0x12, 0x08, 0xff,
+ // const2u
+ 0x0a, 0x45, 0x12, 0x0a, 0x00, 0xff,
+ // const4u
+ 0x0c, 0x12, 0x23, 0x34, 0x45, 0x0c, 0x03, 0x02, 0x01, 0xff,
+ // const8u
+ 0x0e, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x0e, 0x87, 0x98, 0xa9, 0xba, 0xcb,
+ 0xdc, 0xed, 0xfe,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // const1u
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x08, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x12U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x08, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0xffU, this->op_->StackAt(0));
+
+ // const2u
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0a, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1245U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0a, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0xff00U, this->op_->StackAt(0));
+
+ // const4u
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0c, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0c, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ ASSERT_EQ(0xff010203U, this->op_->StackAt(0));
+
+ // const8u
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0e, this->op_->cur_op());
+ ASSERT_EQ(7U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x05060708U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x0102030405060708ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0e, this->op_->cur_op());
+ ASSERT_EQ(8U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0xbaa99887UL, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0xfeeddccbbaa99887ULL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_signed) {
+ std::vector<uint8_t> opcode_buffer = {
+ // const1s
+ 0x09, 0x12, 0x09, 0xff,
+ // const2s
+ 0x0b, 0x21, 0x32, 0x0b, 0x08, 0xff,
+ // const4s
+ 0x0d, 0x45, 0x34, 0x23, 0x12, 0x0d, 0x01, 0x02, 0x03, 0xff,
+ // const8s
+ 0x0f, 0x89, 0x78, 0x67, 0x56, 0x45, 0x34, 0x23, 0x12, 0x0f, 0x04, 0x03, 0x02, 0x01, 0xef,
+ 0xef, 0xef, 0xff,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // const1s
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x09, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x12U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x09, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
+
+ // const2s
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0b, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x3221U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0b, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-248), this->op_->StackAt(0));
+
+ // const4s
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0d, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ ASSERT_EQ(0x12233445U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0d, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-16580095), this->op_->StackAt(0));
+
+ // const8s
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0f, this->op_->cur_op());
+ ASSERT_EQ(7U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x56677889ULL, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x1223344556677889ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0f, this->op_->cur_op());
+ ASSERT_EQ(8U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x01020304U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(static_cast<TypeParam>(-4521264810949884LL), this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_uleb) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Single byte ULEB128
+ 0x10, 0x22, 0x10, 0x7f,
+ // Multi byte ULEB128
+ 0x10, 0xa2, 0x22, 0x10, 0xa2, 0x74, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x09, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x79,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // Single byte ULEB128
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x22U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x7fU, this->op_->StackAt(0));
+
+ // Multi byte ULEB128
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1122U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0x3a22U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x79101c305080c101ULL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_sleb) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Single byte SLEB128
+ 0x11, 0x22, 0x11, 0x7f,
+ // Multi byte SLEB128
+ 0x11, 0xa2, 0x22, 0x11, 0xa2, 0x74, 0x11, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x09, 0x11,
+ };
+ if (sizeof(TypeParam) == 4) {
+ opcode_buffer.push_back(0xb8);
+ opcode_buffer.push_back(0xd3);
+ opcode_buffer.push_back(0x63);
+ } else {
+ opcode_buffer.push_back(0x81);
+ opcode_buffer.push_back(0x82);
+ opcode_buffer.push_back(0x83);
+ opcode_buffer.push_back(0x84);
+ opcode_buffer.push_back(0x85);
+ opcode_buffer.push_back(0x86);
+ opcode_buffer.push_back(0x87);
+ opcode_buffer.push_back(0x88);
+ opcode_buffer.push_back(0x79);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // Single byte SLEB128
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x22U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
+
+ // Multi byte SLEB128
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1122U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-1502), this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(static_cast<TypeParam>(-464456), this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(static_cast<TypeParam>(-499868564803501823LL), this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_dup) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Should fail since nothing is on the stack.
+ 0x12,
+ // Push on a value and dup.
+ 0x08, 0x15, 0x12,
+ // Do it again.
+ 0x08, 0x23, 0x12,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x12, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x12, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x15U, this->op_->StackAt(0));
+ ASSERT_EQ(0x15U, this->op_->StackAt(1));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x12, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0x23U, this->op_->StackAt(0));
+ ASSERT_EQ(0x23U, this->op_->StackAt(1));
+ ASSERT_EQ(0x15U, this->op_->StackAt(2));
+ ASSERT_EQ(0x15U, this->op_->StackAt(3));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_drop) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a couple of values.
+ 0x08, 0x10, 0x08, 0x20,
+ // Drop the values.
+ 0x13, 0x13,
+ // Attempt to drop empty stack.
+ 0x13,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x13, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x13, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x13, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_over) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a couple of values.
+ 0x08, 0x1a, 0x08, 0xed,
+ // Copy a value.
+ 0x14,
+ // Remove all but one element.
+ 0x13, 0x13,
+ // Provoke a failure with this opcode.
+ 0x14,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x14, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1aU, this->op_->StackAt(0));
+ ASSERT_EQ(0xedU, this->op_->StackAt(1));
+ ASSERT_EQ(0x1aU, this->op_->StackAt(2));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x14, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_pick) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a few values.
+ 0x08, 0x1a, 0x08, 0xed, 0x08, 0x34,
+ // Copy the value at offset 2.
+ 0x15, 0x01,
+ // Copy the last value in the stack.
+ 0x15, 0x03,
+ // Choose an invalid index.
+ 0x15, 0x10,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x15, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0xedU, this->op_->StackAt(0));
+ ASSERT_EQ(0x34U, this->op_->StackAt(1));
+ ASSERT_EQ(0xedU, this->op_->StackAt(2));
+ ASSERT_EQ(0x1aU, this->op_->StackAt(3));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x15, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ ASSERT_EQ(0x1aU, this->op_->StackAt(0));
+ ASSERT_EQ(0xedU, this->op_->StackAt(1));
+ ASSERT_EQ(0x34U, this->op_->StackAt(2));
+ ASSERT_EQ(0xedU, this->op_->StackAt(3));
+ ASSERT_EQ(0x1aU, this->op_->StackAt(4));
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x15, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_swap) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a couple of values.
+ 0x08, 0x26, 0x08, 0xab,
+ // Swap values.
+ 0x16,
+ // Pop a value to cause a failure.
+ 0x13, 0x16,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0xabU, this->op_->StackAt(0));
+ ASSERT_EQ(0x26U, this->op_->StackAt(1));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x16, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x26U, this->op_->StackAt(0));
+ ASSERT_EQ(0xabU, this->op_->StackAt(1));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x16, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_rot) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Rotate that should cause a failure.
+ 0x17, 0x08, 0x10,
+ // Only 1 value on stack, should fail.
+ 0x17, 0x08, 0x20,
+ // Only 2 values on stack, should fail.
+ 0x17, 0x08, 0x30,
+ // Should rotate properly.
+ 0x17,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x30U, this->op_->StackAt(0));
+ ASSERT_EQ(0x20U, this->op_->StackAt(1));
+ ASSERT_EQ(0x10U, this->op_->StackAt(2));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x17, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x20U, this->op_->StackAt(0));
+ ASSERT_EQ(0x10U, this->op_->StackAt(1));
+ ASSERT_EQ(0x30U, this->op_->StackAt(2));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_abs) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Abs that should fail.
+ 0x19,
+ // A value that is already positive.
+ 0x08, 0x10, 0x19,
+ // A value that is negative.
+ 0x11, 0x7f, 0x19,
+ // A value that is large and negative.
+ 0x11, 0x81, 0x80, 0x80, 0x80,
+ };
+ if (sizeof(TypeParam) == 4) {
+ opcode_buffer.push_back(0x08);
+ } else {
+ opcode_buffer.push_back(0x80);
+ opcode_buffer.push_back(0x80);
+ opcode_buffer.push_back(0x01);
+ }
+ opcode_buffer.push_back(0x19);
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x19, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x19, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x1U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x19, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(2147483647U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(4398046511105UL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_and) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1b,
+ // Push a single value.
+ 0x08, 0x20,
+ // One element stack, and op will fail.
+ 0x1b,
+ // Push another value.
+ 0x08, 0x02, 0x1b,
+ // Push on two negative values.
+ 0x11, 0x7c, 0x11, 0x7f, 0x1b,
+ // Push one negative, one positive.
+ 0x11, 0x10, 0x11, 0x7c, 0x1b,
+ // Divide by zero.
+ 0x11, 0x10, 0x11, 0x00, 0x1b,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ // Two positive values.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1b, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ // Two negative values.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1b, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x04U, this->op_->StackAt(0));
+
+ // One negative value, one positive value.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(4U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1b, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-4), this->op_->StackAt(0));
+
+ // Divide by zero.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(4U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(5U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_div) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1a,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x1a,
+ // Push another value.
+ 0x08, 0xf0, 0x1a,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1a, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x40U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_minus) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1c,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x1c,
+ // Push another value.
+ 0x08, 0x04, 0x1c,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1c, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x44U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_mod) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1d,
+ // Push a single value.
+ 0x08, 0x47,
+ // One element stack, and op will fail.
+ 0x1d,
+ // Push another value.
+ 0x08, 0x04, 0x1d,
+ // Try a mod of zero.
+ 0x08, 0x01, 0x08, 0x00, 0x1d,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1d, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x03U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_mul) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1e,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x1e,
+ // Push another value.
+ 0x08, 0x04, 0x1e,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1e, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x120U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_neg) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1f,
+ // Push a single value.
+ 0x08, 0x48, 0x1f,
+ // Push a negative value.
+ 0x11, 0x7f, 0x1f,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1f, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-72), this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1f, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x01U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_not) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x20,
+ // Push a single value.
+ 0x08, 0x4, 0x20,
+ // Push a negative value.
+ 0x11, 0x7c, 0x20,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x20, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-5), this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x20, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x03U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_or) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x21,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x21,
+ // Push another value.
+ 0x08, 0xf4, 0x21,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x21, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0xfcU, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_plus) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x22,
+ // Push a single value.
+ 0x08, 0xff,
+ // One element stack, and op will fail.
+ 0x22,
+ // Push another value.
+ 0x08, 0xf2, 0x22,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x22, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x1f1U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_plus_uconst) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x23,
+ // Push a single value.
+ 0x08, 0x50, 0x23, 0x80, 0x51,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x23, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x28d0U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shl) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x24,
+ // Push a single value.
+ 0x08, 0x67,
+ // One element stack, and op will fail.
+ 0x24,
+ // Push another value.
+ 0x08, 0x03, 0x24,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x24, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x338U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shr) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x25,
+ // Push a single value.
+ 0x11, 0x70,
+ // One element stack, and op will fail.
+ 0x25,
+ // Push another value.
+ 0x08, 0x03, 0x25,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x25, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x1ffffffeU, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x1ffffffffffffffeULL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shra) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x26,
+ // Push a single value.
+ 0x11, 0x70,
+ // One element stack, and op will fail.
+ 0x26,
+ // Push another value.
+ 0x08, 0x03, 0x26,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x26, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-2), this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_xor) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x27,
+ // Push a single value.
+ 0x08, 0x11,
+ // One element stack, and op will fail.
+ 0x27,
+ // Push another value.
+ 0x08, 0x41, 0x27,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x27, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x50U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_bra) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x28,
+ // Push on a non-zero value with a positive branch.
+ 0x08, 0x11, 0x28, 0x02, 0x01,
+ // Push on a zero value with a positive branch.
+ 0x08, 0x00, 0x28, 0x05, 0x00,
+ // Push on a non-zero value with a negative branch.
+ 0x08, 0x11, 0x28, 0xfc, 0xff,
+ // Push on a zero value with a negative branch.
+ 0x08, 0x00, 0x28, 0xf0, 0xff,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ // Push on a non-zero value with a positive branch.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ uint64_t offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset + 0x102, this->mem_->cur_offset());
+
+ // Push on a zero value with a positive branch.
+ this->mem_->set_cur_offset(offset);
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset - 5, this->mem_->cur_offset());
+
+ // Push on a non-zero value with a negative branch.
+ this->mem_->set_cur_offset(offset);
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset - 4, this->mem_->cur_offset());
+
+ // Push on a zero value with a negative branch.
+ this->mem_->set_cur_offset(offset);
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset + 16, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, compare_opcode_stack_error) {
+ // All of the ops require two stack elements. Loop through all of these
+ // ops with potential errors.
+ std::vector<uint8_t> opcode_buffer = {
+ 0xff, // Place holder for compare op.
+ 0x08, 0x11,
+ 0xff, // Place holder for compare op.
+ };
+
+ for (uint8_t opcode = 0x29; opcode <= 0x2e; opcode++) {
+ opcode_buffer[0] = opcode;
+ opcode_buffer[3] = opcode;
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+ ASSERT_EQ(opcode, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_FALSE(this->op_->Eval(1, 4, DWARF_VERSION_MAX));
+ ASSERT_EQ(opcode, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, compare_opcodes) {
+ // Have three different checks for each compare op:
+ // - Both values the same.
+ // - The first value larger than the second.
+ // - The second value larger than the first.
+ std::vector<uint8_t> opcode_buffer = {
+ // Values the same.
+ 0x08, 0x11, 0x08, 0x11,
+ 0xff, // Placeholder.
+ // First value larger.
+ 0x08, 0x12, 0x08, 0x10,
+ 0xff, // Placeholder.
+ // Second value larger.
+ 0x08, 0x10, 0x08, 0x12,
+ 0xff, // Placeholder.
+ };
+
+ // Opcode followed by the expected values on the stack.
+ std::vector<uint8_t> expected = {
+ 0x29, 1, 0, 0, // eq
+ 0x2a, 1, 1, 0, // ge
+ 0x2b, 0, 1, 0, // gt
+ 0x2c, 1, 0, 1, // le
+ 0x2d, 0, 0, 1, // lt
+ 0x2e, 0, 1, 1, // ne
+ };
+ for (size_t i = 0; i < expected.size(); i += 4) {
+ opcode_buffer[4] = expected[i];
+ opcode_buffer[9] = expected[i];
+ opcode_buffer[14] = expected[i];
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Eval(0, 15, DWARF_VERSION_MAX))
+ << "Op: 0x" << std::hex << static_cast<uint32_t>(expected[i]) << " failed";
+
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(expected[i + 1], this->op_->StackAt(2));
+ ASSERT_EQ(expected[i + 2], this->op_->StackAt(1));
+ ASSERT_EQ(expected[i + 3], this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_skip) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Positive value.
+ 0x2f, 0x10, 0x20,
+ // Negative value.
+ 0x2f, 0xfd, 0xff,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ uint64_t offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x2f, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset + 0x2010, this->mem_->cur_offset());
+
+ this->mem_->set_cur_offset(offset);
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x2f, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset - 3, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_lit) {
+ std::vector<uint8_t> opcode_buffer;
+
+ // Verify every lit opcode.
+ for (uint8_t op = 0x30; op <= 0x4f; op++) {
+ opcode_buffer.push_back(op);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ uint32_t op = opcode_buffer[i];
+ ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x30U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_reg) {
+ std::vector<uint8_t> opcode_buffer;
+
+ // Verify every reg opcode.
+ for (uint8_t op = 0x50; op <= 0x6f; op++) {
+ opcode_buffer.push_back(op);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ uint32_t op = opcode_buffer[i];
+ ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_TRUE(this->op_->is_register()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x50U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_regx) {
+ std::vector<uint8_t> opcode_buffer = {
+ 0x90, 0x02, 0x90, 0x80, 0x15,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x90, this->op_->cur_op());
+ ASSERT_TRUE(this->op_->is_register());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x02U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Eval(2, 5, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x90, this->op_->cur_op());
+ ASSERT_TRUE(this->op_->is_register());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0xa80U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_breg) {
+ std::vector<uint8_t> opcode_buffer;
+
+ // Verify every reg opcode.
+ for (uint8_t op = 0x70; op <= 0x8f; op++) {
+ // Positive value added to register.
+ opcode_buffer.push_back(op);
+ opcode_buffer.push_back(0x12);
+ // Negative value added to register.
+ opcode_buffer.push_back(op);
+ opcode_buffer.push_back(0x7e);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ RegsFake<TypeParam> regs(32, 10);
+ for (size_t i = 0; i < 32; i++) {
+ regs[i] = i + 10;
+ }
+ this->op_->set_regs(®s);
+
+ uint64_t offset = 0;
+ for (uint32_t op = 0x70; op <= 0x8f; op++) {
+ // Positive value added to register.
+ ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
+ << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x70 + 10 + 0x12, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ offset += 2;
+
+ // Negative value added to register.
+ ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
+ << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x70 + 10 - 2, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ offset += 2;
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_breg_invalid_register) {
+ std::vector<uint8_t> opcode_buffer = {
+ 0x7f, 0x12, 0x80, 0x12,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ RegsFake<TypeParam> regs(16, 10);
+ for (size_t i = 0; i < 16; i++) {
+ regs[i] = i + 10;
+ }
+ this->op_->set_regs(®s);
+
+ // Should pass since this references the last regsister.
+ ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x7fU, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x2bU, this->op_->StackAt(0));
+
+ // Should fail since this references a non-existent register.
+ ASSERT_FALSE(this->op_->Eval(2, 4, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_bregx) {
+ std::vector<uint8_t> opcode_buffer = {// Positive value added to register.
+ 0x92, 0x05, 0x20,
+ // Negative value added to register.
+ 0x92, 0x06, 0x80, 0x7e,
+ // Illegal register.
+ 0x92, 0x80, 0x15, 0x80, 0x02};
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ RegsFake<TypeParam> regs(10, 10);
+ regs[5] = 0x45;
+ regs[6] = 0x190;
+ this->op_->set_regs(®s);
+
+ ASSERT_TRUE(this->op_->Eval(0, 3, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x92, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x65U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Eval(3, 7, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x92, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x90U, this->op_->StackAt(0));
+
+ ASSERT_FALSE(this->op_->Eval(7, 12, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_nop) {
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x96, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, illegal_in_version3,
+ illegal_in_version4, not_implemented, op_addr, op_deref, op_deref_size,
+ const_unsigned, const_signed, const_uleb, const_sleb, op_dup, op_drop,
+ op_over, op_pick, op_swap, op_rot, op_abs, op_and, op_div, op_minus,
+ op_mod, op_mul, op_neg, op_not, op_or, op_plus, op_plus_uconst, op_shl,
+ op_shr, op_shra, op_xor, op_bra, compare_opcode_stack_error,
+ compare_opcodes, op_skip, op_lit, op_reg, op_regx, op_breg,
+ op_breg_invalid_register, op_bregx, op_nop);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
new file mode 100644
index 0000000..a0a21e6
--- /dev/null
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+#include "MemoryFake.h"
+#include "Symbols.h"
+
+template <typename TypeParam>
+class SymbolsTest : public ::testing::Test {
+ protected:
+ void SetUp() override { memory_.Clear(); }
+
+ void InitSym(TypeParam* sym, uint32_t st_value, uint32_t st_size, uint32_t st_name) {
+ memset(sym, 0, sizeof(*sym));
+ sym->st_info = STT_FUNC;
+ sym->st_value = st_value;
+ sym->st_size = st_size;
+ sym->st_name = st_name;
+ sym->st_shndx = SHN_COMMON;
+ }
+
+ MemoryFake memory_;
+};
+TYPED_TEST_CASE_P(SymbolsTest);
+
+TYPED_TEST_P(SymbolsTest, function_bounds_check) {
+ Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+ TypeParam sym;
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ uint64_t offset = 0x1000;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0xfU, func_offset);
+
+ // Check one before and one after the function.
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, 0, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, no_symbol) {
+ Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+ TypeParam sym;
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ uint64_t offset = 0x1000;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+ // First verify that we can get the name.
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0U, func_offset);
+
+ // Now modify the info field so it's no longer a function.
+ sym.st_info = 0;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ // Clear the cache to force the symbol data to be re-read.
+ symbols.ClearCache();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+
+ // Set the function back, and set the shndx to UNDEF.
+ sym.st_info = STT_FUNC;
+ sym.st_shndx = SHN_UNDEF;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ // Clear the cache to force the symbol data to be re-read.
+ symbols.ClearCache();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, multiple_entries) {
+ Symbols symbols(0x1000, sizeof(TypeParam) * 3, sizeof(TypeParam), 0x2000, 0x500);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+ std::string fake_name;
+
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_one";
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x3004, 0x200, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_two";
+ this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0xa010, 0x20, 0x230);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_three";
+ this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_two", name);
+ ASSERT_EQ(1U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_one", name);
+ ASSERT_EQ(4U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_three", name);
+ ASSERT_EQ(1U, func_offset);
+
+ // Reget some of the others to verify getting one function name doesn't
+ // affect any of the next calls.
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_one", name);
+ ASSERT_EQ(8U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_two", name);
+ ASSERT_EQ(4U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_three", name);
+ ASSERT_EQ(0xaU, func_offset);
+}
+
+TYPED_TEST_P(SymbolsTest, multiple_entries_nonstandard_size) {
+ uint64_t entry_size = sizeof(TypeParam) + 5;
+ Symbols symbols(0x1000, entry_size * 3, entry_size, 0x2000, 0x500);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+ std::string fake_name;
+
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_one";
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+ offset += entry_size;
+
+ this->InitSym(&sym, 0x3004, 0x200, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_two";
+ this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
+ offset += entry_size;
+
+ this->InitSym(&sym, 0xa010, 0x20, 0x230);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_three";
+ this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_two", name);
+ ASSERT_EQ(1U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_one", name);
+ ASSERT_EQ(4U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_three", name);
+ ASSERT_EQ(1U, func_offset);
+}
+
+TYPED_TEST_P(SymbolsTest, load_bias) {
+ Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+ TypeParam sym;
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ uint64_t offset = 0x1000;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+ // Set a non-zero load_bias that should be a valid function offset.
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(4U, func_offset);
+
+ // Set a flag that should cause the load_bias to be ignored.
+ sym.st_shndx = SHN_ABS;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ // Clear the cache to force the symbol data to be re-read.
+ symbols.ClearCache();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, symtab_value_out_of_bounds) {
+ Symbols symbols_end_at_100(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x100);
+ Symbols symbols_end_at_200(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x200);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+
+ this->InitSym(&sym, 0x5000, 0x10, 0xfb);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x3000, 0x10, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ // Put the name across the end of the tab.
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x20fb, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ // Verify that we can get the function name properly for both entries.
+ ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0U, func_offset);
+ ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function", name);
+ ASSERT_EQ(0U, func_offset);
+
+ // Now use the symbol table that ends at 0x100.
+ ASSERT_FALSE(
+ symbols_end_at_100.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(
+ symbols_end_at_100.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+}
+
+// Verify the entire func table is cached.
+TYPED_TEST_P(SymbolsTest, symtab_read_cached) {
+ Symbols symbols(0x1000, 3 * sizeof(TypeParam), sizeof(TypeParam), 0xa000, 0x1000);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+
+ // Make sure that these entries are not in ascending order.
+ this->InitSym(&sym, 0x5000, 0x10, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x2000, 0x300, 0x200);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x1000, 0x100, 0x300);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ // Do call that should cache all of the entries (except the string data).
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+ this->memory_.Clear();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+
+ // Clear the memory and only put the symbol data string data in memory.
+ this->memory_.Clear();
+
+ std::string fake_name;
+ fake_name = "first_entry";
+ this->memory_.SetMemory(0xa100, fake_name.c_str(), fake_name.size() + 1);
+ fake_name = "second_entry";
+ this->memory_.SetMemory(0xa200, fake_name.c_str(), fake_name.size() + 1);
+ fake_name = "third_entry";
+ this->memory_.SetMemory(0xa300, fake_name.c_str(), fake_name.size() + 1);
+
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("first_entry", name);
+ ASSERT_EQ(1U, func_offset);
+
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("second_entry", name);
+ ASSERT_EQ(2U, func_offset);
+
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("third_entry", name);
+ ASSERT_EQ(3U, func_offset);
+}
+
+REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
+ multiple_entries_nonstandard_size, load_bias, symtab_value_out_of_bounds,
+ symtab_read_cached);
+
+typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 7600528..2edf224 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -18,6 +18,7 @@
#include <cstdio>
#include <sys/param.h>
+#include <sys/stat.h>
#include <zlib.h>
#define DEF_MEM_LEVEL 8 // normally in zutil.h?
@@ -84,11 +85,19 @@
delete stream;
}
-ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip),
- z_stream_(nullptr, DeleteZStream), buffer_(kBufSize) {
+ZipWriter::ZipWriter(FILE* f) : file_(f), seekable_(false), current_offset_(0),
+ state_(State::kWritingZip), z_stream_(nullptr, DeleteZStream),
+ buffer_(kBufSize) {
+ // Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls
+ // will fail as well.
+ struct stat file_stats;
+ if (fstat(fileno(f), &file_stats) == 0) {
+ seekable_ = S_ISREG(file_stats.st_mode);
+ }
}
ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
+ seekable_(writer.seekable_),
current_offset_(writer.current_offset_),
state_(writer.state_),
files_(std::move(writer.files_)),
@@ -100,6 +109,7 @@
ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
file_ = writer.file_;
+ seekable_ = writer.seekable_;
current_offset_ = writer.current_offset_;
state_ = writer.state_;
files_ = std::move(writer.files_);
@@ -159,6 +169,30 @@
*out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
}
+static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor,
+ LocalFileHeader* dst) {
+ dst->lfh_signature = LocalFileHeader::kSignature;
+ if (use_data_descriptor) {
+ // Set this flag to denote that a DataDescriptor struct will appear after the data,
+ // containing the crc and size fields.
+ dst->gpb_flags |= kGPBDDFlagMask;
+
+ // The size and crc fields must be 0.
+ dst->compressed_size = 0u;
+ dst->uncompressed_size = 0u;
+ dst->crc32 = 0u;
+ } else {
+ dst->compressed_size = src.compressed_size;
+ dst->uncompressed_size = src.uncompressed_size;
+ dst->crc32 = src.crc32;
+ }
+ dst->compression_method = src.compression_method;
+ dst->last_mod_time = src.last_mod_time;
+ dst->last_mod_date = src.last_mod_date;
+ dst->file_name_length = src.path.size();
+ dst->extra_field_length = src.padding_length;
+}
+
int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
time_t time, uint32_t alignment) {
if (state_ != State::kWritingZip) {
@@ -173,66 +207,58 @@
return kInvalidAlignment;
}
- current_file_entry_ = {};
- current_file_entry_.path = path;
- current_file_entry_.local_file_header_offset = current_offset_;
+ FileEntry file_entry = {};
+ file_entry.local_file_header_offset = current_offset_;
+ file_entry.path = path;
- if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(current_file_entry_.path.data()),
- current_file_entry_.path.size())) {
+ if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(file_entry.path.data()),
+ file_entry.path.size())) {
return kInvalidEntryName;
}
- LocalFileHeader header = {};
- header.lfh_signature = LocalFileHeader::kSignature;
-
- // Set this flag to denote that a DataDescriptor struct will appear after the data,
- // containing the crc and size fields.
- header.gpb_flags |= kGPBDDFlagMask;
-
if (flags & ZipWriter::kCompress) {
- current_file_entry_.compression_method = kCompressDeflated;
+ file_entry.compression_method = kCompressDeflated;
int32_t result = PrepareDeflate();
if (result != kNoError) {
return result;
}
} else {
- current_file_entry_.compression_method = kCompressStored;
+ file_entry.compression_method = kCompressStored;
}
- header.compression_method = current_file_entry_.compression_method;
- ExtractTimeAndDate(time, ¤t_file_entry_.last_mod_time, ¤t_file_entry_.last_mod_date);
- header.last_mod_time = current_file_entry_.last_mod_time;
- header.last_mod_date = current_file_entry_.last_mod_date;
+ ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
- header.file_name_length = current_file_entry_.path.size();
-
- off64_t offset = current_offset_ + sizeof(header) + current_file_entry_.path.size();
+ off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
std::vector<char> zero_padding;
if (alignment != 0 && (offset & (alignment - 1))) {
// Pad the extra field so the data will be aligned.
uint16_t padding = alignment - (offset % alignment);
- header.extra_field_length = padding;
+ file_entry.padding_length = padding;
offset += padding;
- zero_padding.resize(padding);
- memset(zero_padding.data(), 0, zero_padding.size());
+ zero_padding.resize(padding, 0);
}
+ LocalFileHeader header = {};
+ // Always start expecting a data descriptor. When the data has finished being written,
+ // if it is possible to seek back, the GPB flag will reset and the sizes written.
+ CopyFromFileEntry(file_entry, true /*use_data_descriptor*/, &header);
+
if (fwrite(&header, sizeof(header), 1, file_) != 1) {
return HandleError(kIoError);
}
- if (fwrite(path, sizeof(*path), current_file_entry_.path.size(), file_)
- != current_file_entry_.path.size()) {
+ if (fwrite(path, sizeof(*path), file_entry.path.size(), file_) != file_entry.path.size()) {
return HandleError(kIoError);
}
- if (header.extra_field_length != 0 &&
- fwrite(zero_padding.data(), 1, header.extra_field_length, file_)
- != header.extra_field_length) {
+ if (file_entry.padding_length != 0 &&
+ fwrite(zero_padding.data(), 1, file_entry.padding_length, file_)
+ != file_entry.padding_length) {
return HandleError(kIoError);
}
+ current_file_entry_ = std::move(file_entry);
current_offset_ = offset;
state_ = State::kWritingEntry;
return kNoError;
@@ -405,23 +431,41 @@
}
}
- const uint32_t sig = DataDescriptor::kOptSignature;
- if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
- state_ = State::kError;
- return kIoError;
- }
+ if ((current_file_entry_.compression_method & kCompressDeflated) || !seekable_) {
+ // Some versions of ZIP don't allow STORED data to have a trailing DataDescriptor.
+ // If this file is not seekable, or if the data is compressed, write a DataDescriptor.
+ const uint32_t sig = DataDescriptor::kOptSignature;
+ if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
- DataDescriptor dd = {};
- dd.crc32 = current_file_entry_.crc32;
- dd.compressed_size = current_file_entry_.compressed_size;
- dd.uncompressed_size = current_file_entry_.uncompressed_size;
- if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
- return HandleError(kIoError);
+ DataDescriptor dd = {};
+ dd.crc32 = current_file_entry_.crc32;
+ dd.compressed_size = current_file_entry_.compressed_size;
+ dd.uncompressed_size = current_file_entry_.uncompressed_size;
+ if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+ current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
+ } else {
+ // Seek back to the header and rewrite to include the size.
+ if (fseeko(file_, current_file_entry_.local_file_header_offset, SEEK_SET) != 0) {
+ return HandleError(kIoError);
+ }
+
+ LocalFileHeader header = {};
+ CopyFromFileEntry(current_file_entry_, false /*use_data_descriptor*/, &header);
+
+ if (fwrite(&header, sizeof(header), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
+ return HandleError(kIoError);
+ }
}
files_.emplace_back(std::move(current_file_entry_));
-
- current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
state_ = State::kWritingZip;
return kNoError;
}
@@ -431,7 +475,7 @@
return kInvalidState;
}
- off64_t startOfCdr = current_offset_;
+ off_t startOfCdr = current_offset_;
for (FileEntry& file : files_) {
CentralDirectoryRecord cdr = {};
cdr.record_signature = CentralDirectoryRecord::kSignature;
@@ -443,7 +487,7 @@
cdr.compressed_size = file.compressed_size;
cdr.uncompressed_size = file.uncompressed_size;
cdr.file_name_length = file.path.size();
- cdr.local_file_header_offset = file.local_file_header_offset;
+ cdr.local_file_header_offset = static_cast<uint32_t>(file.local_file_header_offset);
if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
return HandleError(kIoError);
}
@@ -473,7 +517,7 @@
// Since we can BackUp() and potentially finish writing at an offset less than one we had
// already written at, we must truncate the file.
- if (ftruncate64(fileno(file_), current_offset_) != 0) {
+ if (ftruncate(fileno(file_), current_offset_) != 0) {
return HandleError(kIoError);
}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 30f4950..5b526a4 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -64,6 +64,7 @@
ZipEntry data;
ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(0u, data.has_data_descriptor);
EXPECT_EQ(strlen(expected), data.compressed_length);
ASSERT_EQ(strlen(expected), data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
diff --git a/logcat/Android.mk b/logcat/Android.mk
index f564f0f..4e11ca9 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -15,6 +15,16 @@
include $(CLEAR_VARS)
+LOCAL_MODULE := logcatd
+LOCAL_MODULE_TAGS := debug
+LOCAL_SRC_FILES := logcatd_main.cpp event.logtags
+LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
LOCAL_MODULE := liblogcat
LOCAL_SRC_FILES := logcat.cpp getopt_long.cpp logcat_system.cpp
LOCAL_SHARED_LIBRARIES := $(logcatLibs)
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index b082a64..06cc90d 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -34,9 +34,6 @@
on property:logd.logpersistd.enable=true && property:logd.logpersistd=logcatd
# all exec/services are called with umask(077), so no gain beyond 0700
mkdir /data/misc/logd 0700 logd log
- # logd for write to /data/misc/logd, log group for read from pstore (-L)
- # b/28788401 b/30041146 b/30612424
- # exec - logd log -- /system/bin/logcat -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
start logcatd
# stop logcatd service and clear data
@@ -57,7 +54,7 @@
stop logcatd
# logcatd service
-service logcatd /system/bin/logcat -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
+service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
class late_start
disabled
# logd for write to /data/misc/logd, log group for read from log daemon
diff --git a/logcat/logcatd_main.cpp b/logcat/logcatd_main.cpp
new file mode 100644
index 0000000..9109eb1
--- /dev/null
+++ b/logcat/logcatd_main.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include <log/logcat.h>
+
+int main(int argc, char** argv, char** envp) {
+ android_logcat_context ctx = create_android_logcat();
+ if (!ctx) return -1;
+
+ signal(SIGPIPE, exit);
+
+ // Save and detect presence of -L or --last flag
+ std::vector<std::string> args;
+ bool last = false;
+ for (int i = 0; i < argc; ++i) {
+ if (!argv[i]) continue;
+ args.push_back(std::string(argv[i]));
+ if (!strcmp(argv[i], "-L") || !strcmp(argv[i], "--last")) last = true;
+ }
+
+ // Generate argv from saved content
+ std::vector<const char*> argv_hold;
+ for (auto& str : args) argv_hold.push_back(str.c_str());
+ argv_hold.push_back(nullptr);
+
+ int ret = 0;
+ if (last) {
+ // Run logcat command with -L flag
+ ret = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
+ (char* const*)&argv_hold[0], envp);
+ // Remove -L and --last flags from argument list
+ for (std::vector<const char*>::iterator it = argv_hold.begin();
+ it != argv_hold.end();) {
+ if (!*it || (strcmp(*it, "-L") && strcmp(*it, "--last"))) {
+ ++it;
+ } else {
+ it = argv_hold.erase(it);
+ }
+ }
+ // fall through to re-run the command regardless of the arguments
+ // passed in. For instance, we expect -h to report help stutter.
+ }
+
+ // Run logcat command without -L flag
+ int retval = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
+ (char* const*)&argv_hold[0], envp);
+ if (!ret) ret = retval;
+ retval = android_logcat_destroy(&ctx);
+ if (!ret) ret = retval;
+ return ret;
+}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index 22aca17..defd3c4 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -50,6 +50,7 @@
test_src_files := \
logcat_test.cpp \
+ logcatd_test.cpp \
liblogcat_test.cpp \
# Build tests for the device (with .so). Run with:
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index d802b26..21868f2 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/cdefs.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -39,6 +40,10 @@
#define logcat_pclose(context, fp) pclose(fp)
#define logcat_system(command) system(command)
#endif
+#ifndef logcat_executable
+#define USING_LOGCAT_EXECUTABLE_DEFAULT
+#define logcat_executable "logcat"
+#endif
#define BIG_BUFFER (5 * 1024)
@@ -64,14 +69,13 @@
#undef LOG_TAG
#define LOG_TAG "inject"
- RLOGE("logcat.buckets");
+ RLOGE(logcat_executable ".buckets");
sleep(1);
- ASSERT_TRUE(
- NULL !=
- (fp = logcat_popen(
- ctx,
- "logcat -b radio -b events -b system -b main -d 2>/dev/null")));
+ ASSERT_TRUE(NULL !=
+ (fp = logcat_popen(
+ ctx, logcat_executable
+ " -b radio -b events -b system -b main -d 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -101,8 +105,8 @@
logcat_define(ctx);
ASSERT_TRUE(NULL !=
- (fp = logcat_popen(ctx,
- "logcat -b events -d -s auditd "
+ (fp = logcat_popen(ctx, logcat_executable
+ " -b events -d -s auditd "
"am_proc_start am_pss am_proc_bound "
"dvm_lock_sample am_wtf 2>/dev/null")));
@@ -170,10 +174,9 @@
#endif
strftime(needle, sizeof(needle), "[ %Y-", ptm);
- ASSERT_TRUE(
- NULL !=
- (fp = logcat_popen(
- ctx, "logcat -v long -v year -b all -t 3 2>/dev/null")));
+ ASSERT_TRUE(NULL != (fp = logcat_popen(
+ ctx, logcat_executable
+ " -v long -v year -b all -t 3 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -237,8 +240,8 @@
logcat_define(ctx);
ASSERT_TRUE(NULL !=
- (fp = logcat_popen(ctx,
- "logcat -v long -v America/Los_Angeles "
+ (fp = logcat_popen(ctx, logcat_executable
+ " -v long -v America/Los_Angeles "
"-b all -t 3 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -264,10 +267,9 @@
FILE* fp;
logcat_define(ctx);
- ASSERT_TRUE(NULL !=
- (fp = logcat_popen(ctx,
- "logcat -v long -v America/Los_Angeles -v "
- "zone -b all -t 3 2>/dev/null")));
+ ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, logcat_executable
+ " -v long -v America/Los_Angeles -v "
+ "zone -b all -t 3 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -435,11 +437,11 @@
}
TEST(logcat, tail_time) {
- do_tail_time("logcat -v long -v nsec -b all");
+ do_tail_time(logcat_executable " -v long -v nsec -b all");
}
TEST(logcat, tail_time_epoch) {
- do_tail_time("logcat -v long -v nsec -v epoch -b all");
+ do_tail_time(logcat_executable " -v long -v nsec -v epoch -b all");
}
TEST(logcat, End_to_End) {
@@ -452,8 +454,8 @@
FILE* fp;
logcat_define(ctx);
ASSERT_TRUE(NULL !=
- (fp = logcat_popen(
- ctx, "logcat -v brief -b events -t 100 2>/dev/null")));
+ (fp = logcat_popen(ctx, logcat_executable
+ " -v brief -b events -t 100 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -492,8 +494,8 @@
size_t num = 0;
do {
EXPECT_TRUE(NULL !=
- (fp[num] = logcat_popen(
- ctx[num], "logcat -v brief -b events -t 100")));
+ (fp[num] = logcat_popen(ctx[num], logcat_executable
+ " -v brief -b events -t 100")));
if (!fp[num]) {
fprintf(stderr,
"WARNING: limiting to %zu simultaneous logcat operations\n",
@@ -604,22 +606,23 @@
}
TEST(logcat, get_size) {
- ASSERT_EQ(4, get_groups("logcat -v brief -b radio -b events -b system -b "
+ ASSERT_EQ(4, get_groups(logcat_executable
+ " -v brief -b radio -b events -b system -b "
"main -g 2>/dev/null"));
}
// duplicate test for get_size, but use comma-separated list of buffers
TEST(logcat, multiple_buffer) {
ASSERT_EQ(
- 4, get_groups(
- "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
+ 4, get_groups(logcat_executable
+ " -v brief -b radio,events,system,main -g 2>/dev/null"));
}
TEST(logcat, bad_buffer) {
- ASSERT_EQ(
- 0,
- get_groups(
- "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
+ ASSERT_EQ(0,
+ get_groups(
+ logcat_executable
+ " -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
}
#ifndef logcat
@@ -774,8 +777,8 @@
char buf[sizeof(form)];
ASSERT_TRUE(NULL != mkdtemp(strcpy(buf, form)));
- static const char comm[] =
- "logcat -b radio -b events -b system -b main"
+ static const char comm[] = logcat_executable
+ " -b radio -b events -b system -b main"
" -d -f %s/log.txt -n 7 -r 1";
char command[sizeof(buf) + sizeof(comm)];
snprintf(command, sizeof(command), comm, buf);
@@ -820,8 +823,8 @@
char tmp_out_dir[sizeof(tmp_out_dir_form)];
ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
- static const char logcat_cmd[] =
- "logcat -b radio -b events -b system -b main"
+ static const char logcat_cmd[] = logcat_executable
+ " -b radio -b events -b system -b main"
" -d -f %s/log.txt -n 10 -r 1";
char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
@@ -880,7 +883,7 @@
static const char log_filename[] = "log.txt";
static const char logcat_cmd[] =
- "logcat -b all -v nsec -d -f %s/%s -n 256 -r 1024";
+ logcat_executable " -b all -v nsec -d -f %s/%s -n 256 -r 1024";
static const char cleanup_cmd[] = "rm -rf %s";
char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
@@ -1005,7 +1008,8 @@
static const char log_filename[] = "log.txt";
static const unsigned num_val = 32;
- static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n %d -r 1";
+ static const char logcat_cmd[] =
+ logcat_executable " -b all -d -f %s/%s -n %d -r 1";
static const char clear_cmd[] = " -c";
static const char cleanup_cmd[] = "rm -rf %s";
char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) +
@@ -1109,9 +1113,9 @@
TEST(logcat, logrotate_id) {
static const char logcat_cmd[] =
- "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test";
+ logcat_executable " -b all -d -f %s/%s -n 32 -r 1 --id=test";
static const char logcat_short_cmd[] =
- "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
+ logcat_executable " -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
static const char tmp_out_dir_form[] =
"/data/local/tmp/logcat.logrotate.XXXXXX";
static const char log_filename[] = "log.txt";
@@ -1155,8 +1159,8 @@
TEST(logcat, logrotate_nodir) {
// expect logcat to error out on writing content and not exit(0) for nodir
- static const char command[] =
- "logcat -b all -d"
+ static const char command[] = logcat_executable
+ " -b all -d"
" -f /das/nein/gerfingerpoken/logcat/log.txt"
" -n 256 -r 1024";
EXPECT_FALSE(IsFalse(0 == logcat_system(command), command));
@@ -1292,7 +1296,7 @@
FILE* fp;
logcat_define(ctx);
- fp = logcat_popen(ctx, "logcat -p 2>/dev/null");
+ fp = logcat_popen(ctx, logcat_executable " -p 2>/dev/null");
if (fp == NULL) {
fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
return false;
@@ -1330,7 +1334,8 @@
char buffer[BIG_BUFFER];
- snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : "");
+ snprintf(buffer, sizeof(buffer), logcat_executable " -P '%s' 2>&1",
+ list ? list : "");
fp = logcat_popen(ctx, buffer);
if (fp == NULL) {
fprintf(stderr, "ERROR: %s\n", buffer);
@@ -1392,15 +1397,11 @@
int count = 0;
char buffer[BIG_BUFFER];
-// Have to make liblogcat data unique from logcat data injection
-#ifdef logcat
-#define logcat_regex_prefix "lolcat_test"
-#else
-#define logcat_regex_prefix "logcat_test"
-#endif
+#define logcat_regex_prefix ___STRING(logcat) "_test"
snprintf(buffer, sizeof(buffer),
- "logcat --pid %d -d -e " logcat_regex_prefix "_a+b", getpid());
+ logcat_executable " --pid %d -d -e " logcat_regex_prefix "_a+b",
+ getpid());
LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
logcat_regex_prefix "_ab"));
@@ -1437,8 +1438,8 @@
char buffer[BIG_BUFFER];
- snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3",
- getpid());
+ snprintf(buffer, sizeof(buffer),
+ logcat_executable " --pid %d -d --max-count 3", getpid());
LOG_FAILURE_RETRY(
__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
@@ -1663,10 +1664,11 @@
}
TEST(logcat, security) {
- EXPECT_FALSE(reportedSecurity("logcat -b all -g 2>&1"));
- EXPECT_TRUE(reportedSecurity("logcat -b security -g 2>&1"));
- EXPECT_TRUE(reportedSecurity("logcat -b security -c 2>&1"));
- EXPECT_TRUE(reportedSecurity("logcat -b security -G 256K 2>&1"));
+ EXPECT_FALSE(reportedSecurity(logcat_executable " -b all -g 2>&1"));
+ EXPECT_TRUE(reportedSecurity(logcat_executable " -b security -g 2>&1"));
+ EXPECT_TRUE(reportedSecurity(logcat_executable " -b security -c 2>&1"));
+ EXPECT_TRUE(
+ reportedSecurity(logcat_executable " -b security -G 256K 2>&1"));
}
static size_t commandOutputSize(const char* command) {
@@ -1682,8 +1684,14 @@
}
TEST(logcat, help) {
- size_t logcatHelpTextSize = commandOutputSize("logcat -h 2>&1");
+ size_t logcatHelpTextSize = commandOutputSize(logcat_executable " -h 2>&1");
EXPECT_LT(4096UL, logcatHelpTextSize);
- size_t logcatLastHelpTextSize = commandOutputSize("logcat -L -h 2>&1");
+ size_t logcatLastHelpTextSize =
+ commandOutputSize(logcat_executable " -L -h 2>&1");
+#ifdef USING_LOGCAT_EXECUTABLE_DEFAULT // logcat and liblogcat
EXPECT_EQ(logcatHelpTextSize, logcatLastHelpTextSize);
+#else
+ // logcatd -L -h prints the help twice, as designed.
+ EXPECT_EQ(logcatHelpTextSize * 2, logcatLastHelpTextSize);
+#endif
}
diff --git a/logcat/tests/logcatd_test.cpp b/logcat/tests/logcatd_test.cpp
new file mode 100644
index 0000000..bb7534e
--- /dev/null
+++ b/logcat/tests/logcatd_test.cpp
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#define logcat logcatd
+#define logcat_executable "logcatd"
+
+#include "logcat_test.cpp"
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index d0101ed..88cb67a 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -999,16 +999,18 @@
}
// We may have DAC, but let's not have MAC
- if (setcon("u:object_r:shell:s0") < 0) {
+ if ((setcon("u:object_r:shell:s0") < 0) && (setcon("u:r:shell:s0") < 0)) {
int save_errno = errno;
security_context_t context;
getcon(&context);
- fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n", context,
- strerror(save_errno));
- freecon(context);
- _exit(-1);
- // NOTREACHED
- return 0;
+ if (strcmp(context, "u:r:shell:s0")) {
+ fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
+ context, strerror(save_errno));
+ freecon(context);
+ _exit(-1);
+ // NOTREACHED
+ return 0;
+ }
}
// The key here is we are root, but we are in u:r:shell:s0,
diff --git a/rootdir/init.rc b/rootdir/init.rc
index d48b892..ff96c14 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -301,6 +301,9 @@
start logd
start hwservicemanager
+ # HALs required before data is mounted
+ class_start early_hal
+
# once everything is setup, no need to modify /
mount rootfs rootfs / ro remount
# Mount shared so changes propagate into child namespaces