Merge "Add test config to libappfuse_test"
diff --git a/adb/Android.mk b/adb/Android.mk
index 8a43e37..e841205 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -124,12 +124,11 @@
adbd_auth.cpp \
jdwp_service.cpp \
-LOCAL_C_INCLUDES := system/core/qemu_pipe/include
LOCAL_SANITIZE := $(adb_target_sanitize)
# Even though we're building a static library (and thus there's no link step for
# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libqemu_pipe libbase
+LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase
LOCAL_WHOLE_STATIC_LIBRARIES := libadbd_usb
@@ -362,7 +361,6 @@
LOCAL_STATIC_LIBRARIES := \
libadbd \
libbase \
- libqemu_pipe \
libbootloader_message \
libfs_mgr \
libfec \
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 577e9b9..cf6b359 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -59,10 +59,12 @@
std::string adb_version() {
// Don't change the format of this --- it's parsed by ddmlib.
- return android::base::StringPrintf("Android Debug Bridge version %d.%d.%d\n"
- "Revision %s\n",
- ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
- ADB_REVISION);
+ return android::base::StringPrintf(
+ "Android Debug Bridge version %d.%d.%d\n"
+ "Revision %s\n"
+ "Installed as %s\n",
+ ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_REVISION,
+ android::base::GetExecutablePath().c_str());
}
void fatal(const char *fmt, ...) {
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 7adb262..c48a251 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -306,14 +306,6 @@
}
device_serial.resize(rc);
- // Try to reset the device.
- rc = libusb_reset_device(handle_raw);
- if (rc != 0) {
- LOG(WARNING) << "failed to reset opened device '" << device_serial
- << "': " << libusb_error_name(rc);
- continue;
- }
-
// WARNING: this isn't released via RAII.
rc = libusb_claim_interface(handle.get(), interface_num);
if (rc != 0) {
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 12b98ba..4198a52 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -289,7 +289,7 @@
#define open adb_open
#define read adb_read
#define write adb_write
-#include <qemu_pipe.h>
+#include <system/qemu_pipe.h>
#undef open
#undef read
#undef write
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 1befcb1..fa2838e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -479,6 +479,7 @@
err(1, "failed to drop ambient capabilities");
}
+ pthread_setname_np(pthread_self(), "thread_name");
raise(SIGSYS);
});
@@ -492,6 +493,7 @@
FinishIntercept(&intercept_result);
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(name: thread_name\s+>>> .+debuggerd_test(32|64) <<<)");
ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 2927b16..704dc43 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -115,6 +115,7 @@
{"system_other.img", "system.sig", "system", true, true},
{"vendor.img", "vendor.sig", "vendor", true, false},
{"vendor_other.img", "vendor.sig", "vendor", true, true},
+ {"vbmeta.img", "vbmeta.sig", "vbmeta", true, false},
};
static std::string find_item_given_name(const char* img_name, const char* product) {
@@ -144,6 +145,8 @@
fn = "system.img";
} else if(!strcmp(item,"vendor")) {
fn = "vendor.img";
+ } else if(!strcmp(item,"vbmeta")) {
+ fn = "vbmeta.img";
} else if(!strcmp(item,"userdata")) {
fn = "userdata.img";
} else if(!strcmp(item,"cache")) {
@@ -1536,6 +1539,7 @@
setvbuf(stderr, nullptr, _IONBF, 0);
} else if (strcmp("version", longopts[longindex].name) == 0) {
fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
+ fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
return 0;
} else if (strcmp("slot", longopts[longindex].name) == 0) {
slot_override = std::string(optarg);
diff --git a/fingerprintd/FingerprintDaemonProxy.cpp b/fingerprintd/FingerprintDaemonProxy.cpp
index 1c7da30..b3c0cd7 100644
--- a/fingerprintd/FingerprintDaemonProxy.cpp
+++ b/fingerprintd/FingerprintDaemonProxy.cpp
@@ -31,7 +31,7 @@
FingerprintDaemonProxy* FingerprintDaemonProxy::sInstance = NULL;
// Supported fingerprint HAL version
-static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 0);
+static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 1);
FingerprintDaemonProxy::FingerprintDaemonProxy() : mModule(NULL), mDevice(NULL), mCallback(NULL) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 720ec03..0406efd 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -625,17 +625,6 @@
return fstab;
}
-/* combines fstab entries passed in from device tree with
- * the ones found from fstab_path
- */
-struct fstab *fs_mgr_read_fstab_with_dt(const char *fstab_path)
-{
- struct fstab *fstab_dt = fs_mgr_read_fstab_dt();
- struct fstab *fstab = fs_mgr_read_fstab(fstab_path);
-
- return in_place_merge(fstab_dt, fstab);
-}
-
/*
* tries to load default fstab.<hardware> file from /odm/etc, /vendor/etc
* or /. loads the first one found and also combines fstab entries passed
@@ -658,7 +647,12 @@
LWARNING << __FUNCTION__ << "(): failed to find device hardware name";
}
- return fs_mgr_read_fstab_with_dt(default_fstab.c_str());
+ // combines fstab entries passed in from device tree with
+ // the ones found from default_fstab file
+ struct fstab *fstab_dt = fs_mgr_read_fstab_dt();
+ struct fstab *fstab = fs_mgr_read_fstab(default_fstab.c_str());
+
+ return in_place_merge(fstab_dt, fstab);
}
void fs_mgr_free_fstab(struct fstab *fstab)
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index e4aeb48..4b626de 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -88,7 +88,6 @@
struct fstab *fs_mgr_read_fstab_default();
struct fstab *fs_mgr_read_fstab_dt();
struct fstab *fs_mgr_read_fstab(const char *fstab_path);
-struct fstab *fs_mgr_read_fstab_with_dt(const char *fstab_path);
void fs_mgr_free_fstab(struct fstab *fstab);
#define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index d4a92e5..96bda07 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -166,7 +166,14 @@
enrolled_password_handle, enrolled_password_handle_length);
}
- if (ret == 0) {
+ if (ret == GATEKEEPER_RESPONSE_OK && (*enrolled_password_handle == nullptr ||
+ *enrolled_password_handle_length != sizeof(password_handle_t))) {
+ ret = GATEKEEPER_RESPONSE_ERROR;
+ ALOGE("HAL: password_handle=%p size_of_handle=%" PRIu32 "\n",
+ *enrolled_password_handle, *enrolled_password_handle_length);
+ }
+
+ if (ret == GATEKEEPER_RESPONSE_OK) {
gatekeeper::password_handle_t *handle =
reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle);
store_sid(uid, handle->user_id);
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 7c5e35b..b292725 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -21,6 +21,10 @@
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
+LOCAL_SHARED_LIBRARIES += libsuspend
+endif
LOCAL_SRC_FILES := \
healthd_mode_android.cpp \
healthd_mode_charger.cpp \
@@ -90,6 +94,14 @@
LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
+ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
+LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
+endif
+
+ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
+LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
+endif
+
LOCAL_STATIC_LIBRARIES := \
libhealthd_internal \
libbatterymonitor \
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index 20a6bf6..aa6735d 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -35,9 +35,19 @@
using namespace android;
-// Periodic chores intervals in seconds
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
+#ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
+ // Periodic chores fast interval in seconds
+ #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
+#else
+ #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (BOARD_PERIODIC_CHORES_INTERVAL_FAST)
+#endif
+
+#ifndef BOARD_PERIODIC_CHORES_INTERVAL_SLOW
+ // Periodic chores fast interval in seconds
+ #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
+#else
+ #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
+#endif
static struct healthd_config healthd_config = {
.periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
@@ -143,10 +153,14 @@
struct android::BatteryProperties* /*props*/) {
}
-int healthd_register_event(int fd, void (*handler)(uint32_t)) {
+int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
struct epoll_event ev;
- ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.events = EPOLLIN;
+
+ if (wakeup == EVENT_WAKEUP_FD)
+ ev.events |= EPOLLWAKEUP;
+
ev.data.ptr = (void *)handler;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
KLOG_ERROR(LOG_TAG,
@@ -252,7 +266,7 @@
}
fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
- if (healthd_register_event(uevent_fd, uevent_event))
+ if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD))
KLOG_ERROR(LOG_TAG,
"register for uevent events failed\n");
}
@@ -275,7 +289,7 @@
return;
}
- if (healthd_register_event(wakealarm_fd, wakealarm_event))
+ if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
KLOG_ERROR(LOG_TAG,
"Registration of wakealarm event failed\n");
@@ -283,17 +297,22 @@
}
static void healthd_mainloop(void) {
+ int nevents = 0;
while (1) {
struct epoll_event events[eventct];
- int nevents;
int timeout = awake_poll_interval;
int mode_timeout;
+ /* Don't wait for first timer timeout to run periodic chores */
+ if (!nevents)
+ periodic_chores();
+
+ healthd_mode_ops->heartbeat();
+
mode_timeout = healthd_mode_ops->preparetowait();
if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
timeout = mode_timeout;
nevents = epoll_wait(epollfd, events, eventct, timeout);
-
if (nevents == -1) {
if (errno == EINTR)
continue;
@@ -305,11 +324,6 @@
if (events[n].data.ptr)
(*(void (*)(int))events[n].data.ptr)(events[n].events);
}
-
- if (!nevents)
- periodic_chores();
-
- healthd_mode_ops->heartbeat();
}
return;
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 2f69372..91774c6 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -343,14 +343,19 @@
static void draw_percent(const animation& anim)
{
- if (anim.cur_level <= 0 || anim.cur_status != BATTERY_STATUS_CHARGING) return;
+ int cur_level = anim.cur_level;
+ if (anim.cur_status == BATTERY_STATUS_FULL) {
+ cur_level = 100;
+ }
+
+ if (cur_level <= 0) return;
const animation::text_field& field = anim.text_percent;
if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
return;
}
- std::string str = base::StringPrintf("%d%%", anim.cur_level);
+ std::string str = base::StringPrintf("%d%%", cur_level);
int x, y;
determine_xy(field, str.size(), &x, &y);
@@ -840,7 +845,7 @@
std::placeholders::_2));
if (!ret) {
epollfd = ev_get_epollfd();
- healthd_register_event(epollfd, charger_event_handler);
+ healthd_register_event(epollfd, charger_event_handler, EVENT_WAKEUP_FD);
}
struct animation* anim = init_animation();
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index 34ea55f..17efbd6 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -73,9 +73,14 @@
bool (*screen_on)(android::BatteryProperties *props);
};
+enum EventWakeup {
+ EVENT_NO_WAKEUP_FD,
+ EVENT_WAKEUP_FD,
+};
+
// Global helper functions
-int healthd_register_event(int fd, void (*handler)(uint32_t));
+int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup = EVENT_NO_WAKEUP_FD);
void healthd_battery_update();
android::status_t healthd_get_property(int id,
struct android::BatteryProperty *val);
diff --git a/include/system/qemu_pipe.h b/include/system/qemu_pipe.h
new file mode 100644
index 0000000..af25079
--- /dev/null
+++ b/include/system/qemu_pipe.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2011 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_INCLUDE_SYSTEM_QEMU_PIPE_H
+#define ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
+// occurs during pipe operations. The macro should simply take a printf-style
+// formatting string followed by optional arguments.
+#ifndef QEMU_PIPE_DEBUG
+# define QEMU_PIPE_DEBUG(...) (void)0
+#endif
+
+// Try to open a new Qemu fast-pipe. This function returns a file descriptor
+// that can be used to communicate with a named service managed by the
+// emulator.
+//
+// This file descriptor can be used as a standard pipe/socket descriptor.
+//
+// 'pipeName' is the name of the emulator service you want to connect to,
+// and must begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
+//
+// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
+//
+// EINVAL -> unknown/unsupported pipeName
+// ENOSYS -> fast pipes not available in this system.
+//
+// ENOSYS should never happen, except if you're trying to run within a
+// misconfigured emulator.
+//
+// You should be able to open several pipes to the same pipe service,
+// except for a few special cases (e.g. GSM modem), where EBUSY will be
+// returned if more than one client tries to connect to it.
+static __inline__ int qemu_pipe_open(const char* pipeName) {
+ // Sanity check.
+ if (!pipeName || memcmp(pipeName, "pipe:", 5) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
+ if (fd < 0) {
+ QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
+ strerror(errno));
+ return -1;
+ }
+
+ // Write the pipe name, *including* the trailing zero which is necessary.
+ size_t pipeNameLen = strlen(pipeName);
+ ssize_t ret = TEMP_FAILURE_RETRY(write(fd, pipeName, pipeNameLen + 1U));
+ if (ret != (ssize_t)pipeNameLen + 1) {
+ QEMU_PIPE_DEBUG("%s: Could not connect to %s pipe service: %s",
+ __FUNCTION__, pipeName, strerror(errno));
+ if (ret == 0) {
+ errno = ECONNRESET;
+ } else if (ret > 0) {
+ errno = EINVAL;
+ }
+ return -1;
+ }
+ return fd;
+}
+
+// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
+// This really adds a 4-hexchar prefix describing the payload size.
+// Returns 0 on success, and -1 on error.
+static int __inline__ qemu_pipe_frame_send(int fd,
+ const void* buff,
+ size_t len) {
+ char header[5];
+ snprintf(header, sizeof(header), "%04zx", len);
+ ssize_t ret = TEMP_FAILURE_RETRY(write(fd, header, 4));
+ if (ret != 4) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ ret = TEMP_FAILURE_RETRY(write(fd, buff, len));
+ if (ret != (ssize_t)len) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
+// If the framed message is larger than |len|, then this returns -1 and the
+// content is lost. Otherwise, this returns the size of the message. NOTE:
+// empty messages are possible in a framed wire protocol and do not mean
+// end-of-stream.
+static int __inline__ qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
+ char header[5];
+ ssize_t ret = TEMP_FAILURE_RETRY(read(fd, header, 4));
+ if (ret != 4) {
+ QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ header[4] = '\0';
+ size_t size;
+ if (sscanf(header, "%04zx", &size) != 1) {
+ QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
+ return -1;
+ }
+ if (size > len) {
+ QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
+ len);
+ return -1;
+ }
+ ret = TEMP_FAILURE_RETRY(read(fd, buff, size));
+ if (ret != (ssize_t)size) {
+ QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
+ strerror(errno));
+ return -1;
+ }
+ return size;
+}
+
+#endif /* ANDROID_INCLUDE_HARDWARE_QEMUD_PIPE_H */
diff --git a/init/README.md b/init/README.md
index e66ade2..fc50730 100644
--- a/init/README.md
+++ b/init/README.md
@@ -198,6 +198,13 @@
is in the class "default" if one is not specified via the
class option. Additional classnames beyond the (required) first
one are used to group services.
+`animation class`
+> 'animation' class should include all services necessary for both
+ boot animation and shutdown animation. As these services can be
+ launched very early during bootup and can run until the last stage
+ of shutdown, access to /data partition is not guaranteed. These
+ services can check files under /data but it should not keep files opened
+ and should work when /data is not available.
`onrestart`
> Execute a Command (see below) when service restarts.
@@ -286,6 +293,11 @@
`copy <src> <dst>`
> Copies a file. Similar to write, but useful for binary/large
amounts of data.
+ Regarding to the src file, copying from symbolic link file and world-writable
+ or group-writable files are not allowed.
+ Regarding to the dst file, the default mode created is 0600 if it does not
+ exist. And it will be truncated if dst file is a normal regular file and
+ already exists.
`domainname <name>`
> Set the domain name.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index dc2bda6..64c00e9 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -155,7 +155,7 @@
}
static int do_domainname(const std::vector<std::string>& args) {
- return write_file("/proc/sys/kernel/domainname", args[1].c_str()) ? 0 : 1;
+ return write_file("/proc/sys/kernel/domainname", args[1]) ? 0 : 1;
}
static int do_enable(const std::vector<std::string>& args) {
@@ -179,7 +179,7 @@
}
static int do_hostname(const std::vector<std::string>& args) {
- return write_file("/proc/sys/kernel/hostname", args[1].c_str()) ? 0 : 1;
+ return write_file("/proc/sys/kernel/hostname", args[1]) ? 0 : 1;
}
static int do_ifup(const std::vector<std::string>& args) {
@@ -696,67 +696,15 @@
}
static int do_write(const std::vector<std::string>& args) {
- const char* path = args[1].c_str();
- const char* value = args[2].c_str();
- return write_file(path, value) ? 0 : 1;
+ return write_file(args[1], args[2]) ? 0 : 1;
}
static int do_copy(const std::vector<std::string>& args) {
- char *buffer = NULL;
- int rc = 0;
- int fd1 = -1, fd2 = -1;
- struct stat info;
- int brtw, brtr;
- char *p;
-
- if (stat(args[1].c_str(), &info) < 0)
- return -1;
-
- if ((fd1 = open(args[1].c_str(), O_RDONLY|O_CLOEXEC)) < 0)
- goto out_err;
-
- if ((fd2 = open(args[2].c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
- goto out_err;
-
- if (!(buffer = (char*) malloc(info.st_size)))
- goto out_err;
-
- p = buffer;
- brtr = info.st_size;
- while(brtr) {
- rc = read(fd1, p, brtr);
- if (rc < 0)
- goto out_err;
- if (rc == 0)
- break;
- p += rc;
- brtr -= rc;
+ std::string data;
+ if (read_file(args[1], &data)) {
+ return write_file(args[2], data) ? 0 : 1;
}
-
- p = buffer;
- brtw = info.st_size;
- while(brtw) {
- rc = write(fd2, p, brtw);
- if (rc < 0)
- goto out_err;
- if (rc == 0)
- break;
- p += rc;
- brtw -= rc;
- }
-
- rc = 0;
- goto out;
-out_err:
- rc = -1;
-out:
- if (buffer)
- free(buffer);
- if (fd1 >= 0)
- close(fd1);
- if (fd2 >= 0)
- close(fd2);
- return rc;
+ return 1;
}
static int do_chown(const std::vector<std::string>& args) {
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 326ebf2..a192862 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -96,7 +96,7 @@
LOG(INFO) << "Parsing file " << path << "...";
Timer t;
std::string data;
- if (!read_file(path.c_str(), &data)) {
+ if (!read_file(path, &data)) {
return false;
}
diff --git a/init/reboot.cpp b/init/reboot.cpp
index e34abdb..62e5c85 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -26,6 +26,7 @@
#include <sys/wait.h>
#include <memory>
+#include <set>
#include <string>
#include <thread>
#include <vector>
@@ -41,6 +42,7 @@
#include <logwrap/logwrap.h>
#include "log.h"
+#include "property_service.h"
#include "reboot.h"
#include "service.h"
#include "util.h"
@@ -248,8 +250,9 @@
flags);
} else {
umountDone = false;
- PLOG(WARNING) << StringPrintf("cannot umount %s, flags:0x%x",
- entry.mnt_fsname().c_str(), flags);
+ PLOG(WARNING) << StringPrintf("cannot umount %s, mnt_dir %s, flags:0x%x",
+ entry.mnt_fsname().c_str(),
+ entry.mnt_dir().c_str(), flags);
}
}
}
@@ -351,26 +354,40 @@
}
LOG(INFO) << "Shutdown timeout: " << shutdownTimeout;
- static const constexpr char* shutdown_critical_services[] = {"vold", "watchdogd"};
- for (const char* name : shutdown_critical_services) {
- Service* s = ServiceManager::GetInstance().FindServiceByName(name);
- if (s == nullptr) {
- LOG(WARNING) << "Shutdown critical service not found:" << name;
- continue;
+ // keep debugging tools until non critical ones are all gone.
+ const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
+ // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
+ const std::set<std::string> to_starts{"watchdogd", "vold"};
+ ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
+ if (kill_after_apps.count(s->name())) {
+ s->SetShutdownCritical();
+ } else if (to_starts.count(s->name())) {
+ s->Start();
+ s->SetShutdownCritical();
}
- s->Start(); // make sure that it is running.
- s->SetShutdownCritical();
+ });
+
+ Service* bootAnim = ServiceManager::GetInstance().FindServiceByName("bootanim");
+ Service* surfaceFlinger = ServiceManager::GetInstance().FindServiceByName("surfaceflinger");
+ if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
+ property_set("service.bootanim.exit", "0");
+ // Could be in the middle of animation. Stop and start so that it can pick
+ // up the right mode.
+ bootAnim->Stop();
+ // start all animation classes if stopped.
+ ServiceManager::GetInstance().ForEachServiceInClass("animation", [](Service* s) {
+ s->Start();
+ s->SetShutdownCritical(); // will not check animation class separately
+ });
+ bootAnim->Start();
+ surfaceFlinger->SetShutdownCritical();
+ bootAnim->SetShutdownCritical();
}
+
// optional shutdown step
// 1. terminate all services except shutdown critical ones. wait for delay to finish
if (shutdownTimeout > 0) {
LOG(INFO) << "terminating init services";
- // tombstoned can write to data when other services are killed. so finish it first.
- static const constexpr char* first_to_kill[] = {"tombstoned"};
- for (const char* name : first_to_kill) {
- Service* s = ServiceManager::GetInstance().FindServiceByName(name);
- if (s != nullptr) s->Stop();
- }
// Ask all services to terminate except shutdown critical ones.
ServiceManager::GetInstance().ForEachService([](Service* s) {
@@ -409,8 +426,8 @@
// minimum safety steps before restarting
// 2. kill all services except ones that are necessary for the shutdown sequence.
- ServiceManager::GetInstance().ForEachService([](Service* s) {
- if (!s->IsShutdownCritical()) s->Stop();
+ ServiceManager::GetInstance().ForEachService([&kill_after_apps](Service* s) {
+ if (!s->IsShutdownCritical() || kill_after_apps.count(s->name())) s->Stop();
});
ServiceManager::GetInstance().ReapAnyOutstandingChildren();
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index f27be64..ba53e47 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -94,7 +94,7 @@
return 0;
}
-void set_device_permission(int nargs, char **args)
+void set_device_permission(const char* fn, int line, int nargs, char **args)
{
char *name;
char *attr = 0;
@@ -121,7 +121,7 @@
}
if (nargs != 4) {
- LOG(ERROR) << "invalid line ueventd.rc line for '" << args[0] << "'";
+ LOG(ERROR) << "invalid line (" << fn << ":" << line << ") line for '" << args[0] << "'";
return;
}
@@ -136,20 +136,20 @@
perm = strtol(args[1], &endptr, 8);
if (!endptr || *endptr != '\0') {
- LOG(ERROR) << "invalid mode '" << args[1] << "'";
+ LOG(ERROR) << "invalid mode (" << fn << ":" << line << ") '" << args[1] << "'";
return;
}
struct passwd* pwd = getpwnam(args[2]);
if (!pwd) {
- LOG(ERROR) << "invalid uid '" << args[2] << "'";
+ LOG(ERROR) << "invalid uid (" << fn << ":" << line << ") '" << args[2] << "'";
return;
}
uid = pwd->pw_uid;
struct group* grp = getgrnam(args[3]);
if (!grp) {
- LOG(ERROR) << "invalid gid '" << args[3] << "'";
+ LOG(ERROR) << "invalid gid (" << fn << ":" << line << ") '" << args[3] << "'";
return;
}
gid = grp->gr_gid;
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index baff58c..554c1e3 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -236,6 +236,6 @@
return 0;
}
-static void parse_line_device(parse_state*, int nargs, char** args) {
- set_device_permission(nargs, args);
+static void parse_line_device(parse_state* state, int nargs, char** args) {
+ set_device_permission(state->filename, state->line, nargs, args);
}
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 907cc49..4d69897 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -22,7 +22,7 @@
#define UEVENTD_PARSER_MAXARGS 5
int ueventd_parse_config_file(const char *fn);
-void set_device_permission(int nargs, char **args);
+void set_device_permission(const char* fn, int line, int nargs, char **args);
struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name);
#endif
diff --git a/init/util.cpp b/init/util.cpp
index 3f8f244..bf4109c 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -163,10 +163,11 @@
return -1;
}
-bool read_file(const char* path, std::string* content) {
+bool read_file(const std::string& path, std::string* content) {
content->clear();
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
if (fd == -1) {
return false;
}
@@ -186,9 +187,9 @@
return android::base::ReadFdToString(fd, content);
}
-bool write_file(const char* path, const char* content) {
- android::base::unique_fd fd(
- TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, 0600)));
+bool write_file(const std::string& path, const std::string& content) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+ open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
if (fd == -1) {
PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
return false;
diff --git a/init/util.h b/init/util.h
index 23509d3..1034c9b 100644
--- a/init/util.h
+++ b/init/util.h
@@ -35,8 +35,8 @@
int create_socket(const char *name, int type, mode_t perm,
uid_t uid, gid_t gid, const char *socketcon);
-bool read_file(const char* path, std::string* content);
-bool write_file(const char* path, const char* content);
+bool read_file(const std::string& path, std::string* content);
+bool write_file(const std::string& path, const std::string& content);
class Timer {
public:
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 24c75c4..0c0350a 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -17,7 +17,11 @@
#include "util.h"
#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
#include <gtest/gtest.h>
TEST(util, read_file_ENOENT) {
@@ -28,6 +32,35 @@
EXPECT_EQ("", s); // s was cleared.
}
+TEST(util, read_file_group_writeable) {
+ std::string s("hello");
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ EXPECT_TRUE(write_file(tf.path, s)) << strerror(errno);
+ EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
+ EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+ EXPECT_EQ("", s); // s was cleared.
+}
+
+TEST(util, read_file_world_writeable) {
+ std::string s("hello");
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
+ EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
+ EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+ EXPECT_EQ("", s); // s was cleared.
+}
+
+TEST(util, read_file_symbolic_link) {
+ std::string s("hello");
+ errno = 0;
+ // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
+ EXPECT_FALSE(read_file("/charger", &s));
+ EXPECT_EQ(ELOOP, errno);
+ EXPECT_EQ("", s); // s was cleared.
+}
+
TEST(util, read_file_success) {
std::string s("hello");
EXPECT_TRUE(read_file("/proc/version", &s));
@@ -37,6 +70,51 @@
EXPECT_STREQ("Linux", s.c_str());
}
+TEST(util, write_file_binary) {
+ std::string contents("abcd");
+ contents.push_back('\0');
+ contents.push_back('\0');
+ contents.append("dcba");
+ ASSERT_EQ(10u, contents.size());
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ EXPECT_TRUE(write_file(tf.path, contents)) << strerror(errno);
+
+ std::string read_back_contents;
+ EXPECT_TRUE(read_file(tf.path, &read_back_contents)) << strerror(errno);
+ EXPECT_EQ(contents, read_back_contents);
+ EXPECT_EQ(10u, read_back_contents.size());
+}
+
+TEST(util, write_file_not_exist) {
+ std::string s("hello");
+ std::string s2("hello");
+ TemporaryDir test_dir;
+ std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
+ EXPECT_TRUE(write_file(path, s));
+ EXPECT_TRUE(read_file(path, &s2));
+ EXPECT_EQ(s, s2);
+ struct stat sb;
+ int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+ EXPECT_NE(-1, fd);
+ EXPECT_EQ(0, fstat(fd, &sb));
+ EXPECT_EQ((const unsigned int)(S_IRUSR | S_IWUSR), sb.st_mode & 0777);
+ EXPECT_EQ(0, unlink(path.c_str()));
+}
+
+TEST(util, write_file_exist) {
+ std::string s2("");
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ EXPECT_TRUE(write_file(tf.path, "1hello1")) << strerror(errno);
+ EXPECT_TRUE(read_file(tf.path, &s2));
+ EXPECT_STREQ("1hello1", s2.c_str());
+ EXPECT_TRUE(write_file(tf.path, "2ll2"));
+ EXPECT_TRUE(read_file(tf.path, &s2));
+ EXPECT_STREQ("2ll2", s2.c_str());
+}
+
TEST(util, decode_uid) {
EXPECT_EQ(0U, decode_uid("root"));
EXPECT_EQ(UINT_MAX, decode_uid("toot"));
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 6a57a41..daa9ff5 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -35,22 +35,13 @@
#include <log/log.h>
#include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
#include <utils/Compat.h>
#ifndef O_BINARY
#define O_BINARY 0
#endif
-/* The following structure is stored little endian */
-struct fs_path_config_from_file {
- uint16_t len;
- uint16_t mode;
- uint16_t uid;
- uint16_t gid;
- uint64_t capabilities;
- char prefix[];
-} __attribute__((__aligned__(sizeof(uint64_t))));
-
/* My kingdom for <endian.h> */
static inline uint16_t get2LE(const uint8_t* src) { return src[0] | (src[1] << 8); }
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
new file mode 100644
index 0000000..7dad668
--- /dev/null
+++ b/libcutils/include/private/fs_config.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
+#define _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
+
+#include <stdint.h>
+
+/*
+ * binary format for the runtime <partition>/etc/fs_config_(dirs|files)
+ * filesystem override files.
+ */
+
+/* The following structure is stored little endian */
+struct fs_path_config_from_file {
+ uint16_t len;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t gid;
+ uint64_t capabilities;
+ char prefix[];
+} __attribute__((__aligned__(sizeof(uint64_t))));
+
+#endif /* _LIBS_CUTILS_PRIVATE_FS_CONFIG_H */
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index d322c0f..84feb20 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -410,16 +410,46 @@
if (!tag) tag = "";
/* XXX: This needs to go! */
- if ((bufID != LOG_ID_RADIO) &&
- (!strcmp(tag, "HTC_RIL") ||
- !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
- !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
- !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") ||
- !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || !strcmp(tag, "SMS"))) {
- bufID = LOG_ID_RADIO;
- /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
- snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
- tag = tmp_tag;
+ if (bufID != LOG_ID_RADIO) {
+ switch (tag[0]) {
+ case 'H':
+ if (strcmp(tag + 1, "HTC_RIL" + 1)) break;
+ goto inform;
+ case 'R':
+ /* Any log tag with "RIL" as the prefix */
+ if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break;
+ goto inform;
+ case 'Q':
+ /* Any log tag with "QC_RIL" as the prefix */
+ if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break;
+ goto inform;
+ case 'I':
+ /* Any log tag with "IMS" as the prefix */
+ if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break;
+ goto inform;
+ case 'A':
+ if (strcmp(tag + 1, "AT" + 1)) break;
+ goto inform;
+ case 'G':
+ if (strcmp(tag + 1, "GSM" + 1)) break;
+ goto inform;
+ case 'S':
+ if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break;
+ goto inform;
+ case 'C':
+ if (strcmp(tag + 1, "CDMA" + 1)) break;
+ goto inform;
+ case 'P':
+ if (strcmp(tag + 1, "PHONE" + 1)) break;
+ /* FALLTHRU */
+ inform:
+ bufID = LOG_ID_RADIO;
+ snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+ tag = tmp_tag;
+ /* FALLTHRU */
+ default:
+ break;
+ }
}
#if __BIONIC__
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 3f79552..c4bf959 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -527,11 +527,13 @@
/*
* Measure the time it takes to submit the android event logging call
* using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
- * under light load. Expect this to be a dozen or so syscall periods (40us)
+ * under light load. Expect this to be a long path to logger to convert the
+ * unknown tag (0) into a tagname (less than 200us).
*/
static void BM_log_event_overhead(int iters) {
for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
StartBenchmarkTiming();
+ // log tag number 0 is not known, nor shall it ever be known
__android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
StopBenchmarkTiming();
logd_yield();
@@ -539,6 +541,28 @@
}
BENCHMARK(BM_log_event_overhead);
+/*
+ * Measure the time it takes to submit the android event logging call
+ * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
+ * under light load with a known logtag. Expect this to be a dozen or so
+ * syscall periods (less than 40us)
+ */
+static void BM_log_event_overhead_42(int iters) {
+ for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
+ StartBenchmarkTiming();
+ // In system/core/logcat/event.logtags:
+ // # These are used for testing, do not modify without updating
+ // # tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+ // # system/core/liblog/tests/liblog_benchmark.cpp
+ // # system/core/liblog/tests/liblog_test.cpp
+ // 42 answer (to life the universe etc|3)
+ __android_log_btwrite(42, EVENT_TYPE_LONG, &i, sizeof(i));
+ StopBenchmarkTiming();
+ logd_yield();
+ }
+}
+BENCHMARK(BM_log_event_overhead_42);
+
static void BM_log_event_overhead_null(int iters) {
set_log_null();
BM_log_event_overhead(iters);
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c
index 2da204a..4dedf7f 100644
--- a/libsuspend/autosuspend_wakeup_count.c
+++ b/libsuspend/autosuspend_wakeup_count.c
@@ -24,7 +24,6 @@
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
-#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -36,24 +35,12 @@
#define SYS_POWER_STATE "/sys/power/state"
#define SYS_POWER_WAKEUP_COUNT "/sys/power/wakeup_count"
-#define BASE_SLEEP_TIME 100000
-
static int state_fd;
static int wakeup_count_fd;
static pthread_t suspend_thread;
static sem_t suspend_lockout;
static const char *sleep_state = "mem";
static void (*wakeup_func)(bool success) = NULL;
-static int sleep_time = BASE_SLEEP_TIME;
-
-static void update_sleep_time(bool success) {
- if (success) {
- sleep_time = BASE_SLEEP_TIME;
- return;
- }
- // double sleep time after each failure up to one minute
- sleep_time = MIN(sleep_time * 2, 60000000);
-}
static void *suspend_thread_func(void *arg __attribute__((unused)))
{
@@ -61,12 +48,10 @@
char wakeup_count[20];
int wakeup_count_len;
int ret;
- bool success = true;
+ bool success;
while (1) {
- update_sleep_time(success);
- usleep(sleep_time);
- success = false;
+ usleep(100000);
ALOGV("%s: read wakeup_count\n", __func__);
lseek(wakeup_count_fd, 0, SEEK_SET);
wakeup_count_len = TEMP_FAILURE_RETRY(read(wakeup_count_fd, wakeup_count,
@@ -90,6 +75,7 @@
continue;
}
+ success = true;
ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count);
ret = TEMP_FAILURE_RETRY(write(wakeup_count_fd, wakeup_count, wakeup_count_len));
if (ret < 0) {
@@ -98,8 +84,8 @@
} else {
ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE);
ret = TEMP_FAILURE_RETRY(write(state_fd, sleep_state, strlen(sleep_state)));
- if (ret >= 0) {
- success = true;
+ if (ret < 0) {
+ success = false;
}
void (*func)(bool success) = wakeup_func;
if (func != NULL) {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index dabeac1..17ede51 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -48,6 +48,7 @@
srcs: [
"ArmExidx.cpp",
"DwarfMemory.cpp",
+ "DwarfOp.cpp",
"Elf.cpp",
"ElfInterface.cpp",
"ElfInterfaceArm.cpp",
@@ -89,6 +90,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",
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/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index d59e9d8..087457c 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -42,7 +42,7 @@
uint64_t offset = ehdr.e_phoff;
for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
PhdrType phdr;
- if (!memory_->Read(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
return false;
}
@@ -54,20 +54,20 @@
case PT_LOAD:
{
// Get the flags first, if this isn't an executable header, ignore it.
- if (!memory_->Read(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
return false;
}
if ((phdr.p_flags & PF_X) == 0) {
continue;
}
- if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
return false;
}
- if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
return false;
}
- if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
return false;
}
pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
@@ -79,22 +79,22 @@
}
case PT_GNU_EH_FRAME:
- if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
return false;
}
eh_frame_offset_ = phdr.p_offset;
- if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
return false;
}
eh_frame_size_ = phdr.p_memsz;
break;
case PT_DYNAMIC:
- if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
return false;
}
dynamic_offset_ = phdr.p_offset;
- if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
return false;
}
dynamic_size_ = phdr.p_memsz;
@@ -116,8 +116,8 @@
ShdrType shdr;
if (ehdr.e_shstrndx < ehdr.e_shnum) {
uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
- if (memory_->Read(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
- && memory_->Read(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+ if (memory_->ReadField(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+ memory_->ReadField(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
sec_offset = shdr.sh_offset;
sec_size = shdr.sh_size;
}
@@ -125,27 +125,27 @@
// Skip the first header, it's always going to be NULL.
for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
- if (!memory_->Read(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
+ if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
return false;
}
if (shdr.sh_type == SHT_PROGBITS) {
// Look for the .debug_frame and .gnu_debugdata.
- if (!memory_->Read(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
+ if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
return false;
}
if (shdr.sh_name < sec_size) {
std::string name;
if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
if (name == ".debug_frame") {
- if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
- && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+ if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+ memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
debug_frame_offset_ = shdr.sh_offset;
debug_frame_size_ = shdr.sh_size;
}
} else if (name == ".gnu_debugdata") {
- if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
- && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+ if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+ memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
gnu_debugdata_offset_ = shdr.sh_offset;
gnu_debugdata_size_ = shdr.sh_size;
}
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index e157320..bab84cc 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -85,10 +85,10 @@
}
Elf32_Phdr phdr;
- if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
return true;
}
- if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
return true;
}
// The load_bias_ should always be set by this time.
@@ -98,13 +98,15 @@
}
bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
- return StepExidx(pc, regs, process_memory) ||
- ElfInterface32::Step(pc, regs, process_memory);
+ // Dwarf unwind information is precise about whether a pc is covered or not,
+ // but arm unwind information only has ranges of pc. In order to avoid
+ // incorrectly doing a bad unwind using arm unwind information for a
+ // different function, always try and unwind with the dwarf information first.
+ return ElfInterface32::Step(pc, regs, process_memory) || StepExidx(pc, regs, process_memory);
}
bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) {
RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
- // First try arm, then try dwarf.
uint64_t entry_offset;
if (!FindEntry(pc, &entry_offset)) {
return false;
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 1fcf842..9e46509 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -96,10 +96,16 @@
offset_ = offset & (getpagesize() - 1);
uint64_t aligned_offset = offset & ~(getpagesize() - 1);
+ if (aligned_offset > static_cast<uint64_t>(buf.st_size) ||
+ offset > static_cast<uint64_t>(buf.st_size)) {
+ return false;
+ }
+
size_ = buf.st_size - aligned_offset;
- if (size < (UINT64_MAX - offset_) && size + offset_ < size_) {
+ uint64_t max_size;
+ if (!__builtin_add_overflow(size, offset_, &max_size) && max_size < size_) {
// Truncate the mapped size.
- size_ = size + offset_;
+ size_ = max_size;
}
void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
if (map == MAP_FAILED) {
@@ -113,14 +119,15 @@
}
bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
- if (addr + size > size_) {
+ uint64_t max_size;
+ if (__builtin_add_overflow(addr, size, &max_size) || max_size > size_) {
return false;
}
memcpy(dst, &data_[addr], size);
return true;
}
-static bool PtraceRead(pid_t pid, uint64_t addr, long* value) {
+bool MemoryRemote::PtraceRead(uint64_t addr, long* value) {
#if !defined(__LP64__)
// Cannot read an address greater than 32 bits.
if (addr > UINT32_MAX) {
@@ -130,7 +137,7 @@
// ptrace() returns -1 and sets errno when the operation fails.
// To disambiguate -1 from a valid result, we clear errno beforehand.
errno = 0;
- *value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
+ *value = ptrace(PTRACE_PEEKTEXT, pid_, reinterpret_cast<void*>(addr), nullptr);
if (*value == -1 && errno) {
return false;
}
@@ -138,11 +145,17 @@
}
bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
+ // Make sure that there is no overflow.
+ uint64_t max_size;
+ if (__builtin_add_overflow(addr, bytes, &max_size)) {
+ return false;
+ }
+
size_t bytes_read = 0;
long data;
size_t align_bytes = addr & (sizeof(long) - 1);
if (align_bytes != 0) {
- if (!PtraceRead(pid_, addr & ~(sizeof(long) - 1), &data)) {
+ if (!PtraceRead(addr & ~(sizeof(long) - 1), &data)) {
return false;
}
size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
@@ -154,7 +167,7 @@
}
for (size_t i = 0; i < bytes / sizeof(long); i++) {
- if (!PtraceRead(pid_, addr, &data)) {
+ if (!PtraceRead(addr, &data)) {
return false;
}
memcpy(dst, &data, sizeof(long));
@@ -165,7 +178,7 @@
size_t left_over = bytes & (sizeof(long) - 1);
if (left_over) {
- if (!PtraceRead(pid_, addr, &data)) {
+ if (!PtraceRead(addr, &data)) {
return false;
}
memcpy(dst, &data, left_over);
@@ -175,7 +188,13 @@
}
bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
- // The process_vm_readv call does will not always work on remote
+ // Make sure that there is no overflow.
+ uint64_t max_size;
+ if (__builtin_add_overflow(addr, size, &max_size)) {
+ return false;
+ }
+
+ // The process_vm_readv call will not always work on remote
// processes, so only use it for reads from the current pid.
// Use this method to avoid crashes if an address is invalid since
// unwind data could try to access any part of the address space.
@@ -208,9 +227,29 @@
}
bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
- if (addr < start_ || addr + size > start_ + offset_ + size_) {
+ uint64_t max_size;
+ if (__builtin_add_overflow(addr, size, &max_size)) {
+ return false;
+ }
+
+ uint64_t real_size;
+ if (__builtin_add_overflow(start_, offset_, &real_size) ||
+ __builtin_add_overflow(real_size, size_, &real_size)) {
+ return false;
+ }
+
+ if (addr < start_ || max_size > real_size) {
return false;
}
memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
return true;
}
+
+bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
+ uint64_t max_read;
+ if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) {
+ return false;
+ }
+ // The check above guarantees that addr + begin_ will not overflow.
+ return memory_->Read(addr + begin_, dst, size);
+}
diff --git a/libunwindstack/Memory.h b/libunwindstack/Memory.h
index c5316a1..f9f6d56 100644
--- a/libunwindstack/Memory.h
+++ b/libunwindstack/Memory.h
@@ -17,6 +17,7 @@
#ifndef _LIBUNWINDSTACK_MEMORY_H
#define _LIBUNWINDSTACK_MEMORY_H
+#include <assert.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
@@ -33,9 +34,16 @@
virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;
- inline bool Read(uint64_t addr, void* start, void* field, size_t size) {
- return Read(addr + reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start),
- field, size);
+ inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) {
+ if (reinterpret_cast<uintptr_t>(field) < reinterpret_cast<uintptr_t>(start)) {
+ return false;
+ }
+ uint64_t offset = reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start);
+ if (__builtin_add_overflow(addr, offset, &offset)) {
+ return false;
+ }
+ // The read will check if offset + size overflows.
+ return Read(offset, field, size);
}
inline bool Read32(uint64_t addr, uint32_t* dst) {
@@ -103,6 +111,9 @@
pid_t pid() { return pid_; }
+ protected:
+ virtual bool PtraceRead(uint64_t addr, long* value);
+
private:
pid_t pid_;
};
@@ -118,15 +129,12 @@
class MemoryRange : public Memory {
public:
MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
- : memory_(memory), begin_(begin), length_(end - begin_) {}
+ : memory_(memory), begin_(begin), length_(end - begin) {
+ assert(end > begin);
+ }
virtual ~MemoryRange() { delete memory_; }
- inline bool Read(uint64_t addr, void* dst, size_t size) override {
- if (addr + size <= length_) {
- return memory_->Read(addr + begin_, dst, size);
- }
- return false;
- }
+ bool Read(uint64_t addr, void* dst, size_t size) override;
private:
Memory* memory_;
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/MemoryBuffer.cpp b/libunwindstack/tests/MemoryBuffer.cpp
new file mode 100644
index 0000000..af3d6b9
--- /dev/null
+++ b/libunwindstack/tests/MemoryBuffer.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+
+class MemoryBufferTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.reset(new MemoryBuffer);
+ }
+ std::unique_ptr<MemoryBuffer> memory_;
+};
+
+TEST_F(MemoryBufferTest, empty) {
+ ASSERT_EQ(0U, memory_->Size());
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_FALSE(memory_->Read(0, buffer.data(), 1));
+ ASSERT_EQ(nullptr, memory_->GetPtr(0));
+ ASSERT_EQ(nullptr, memory_->GetPtr(1));
+}
+
+TEST_F(MemoryBufferTest, write_read) {
+ memory_->Resize(256);
+ ASSERT_EQ(256U, memory_->Size());
+ ASSERT_TRUE(memory_->GetPtr(0) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(1) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(255) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(256) == nullptr);
+
+ uint8_t* data = memory_->GetPtr(0);
+ for (size_t i = 0; i < memory_->Size(); i++) {
+ data[i] = i;
+ }
+
+ std::vector<uint8_t> buffer(memory_->Size());
+ ASSERT_TRUE(memory_->Read(0, buffer.data(), buffer.size()));
+ for (size_t i = 0; i < buffer.size(); i++) {
+ ASSERT_EQ(i, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryBufferTest, read_failures) {
+ memory_->Resize(100);
+ std::vector<uint8_t> buffer(200);
+ ASSERT_FALSE(memory_->Read(0, buffer.data(), 101));
+ ASSERT_FALSE(memory_->Read(100, buffer.data(), 1));
+ ASSERT_FALSE(memory_->Read(101, buffer.data(), 2));
+ ASSERT_FALSE(memory_->Read(99, buffer.data(), 2));
+ ASSERT_TRUE(memory_->Read(99, buffer.data(), 1));
+}
+
+TEST_F(MemoryBufferTest, read_failure_overflow) {
+ memory_->Resize(100);
+ std::vector<uint8_t> buffer(200);
+
+ ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200));
+}
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index e05736b..70ef30a 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -75,4 +75,16 @@
}
};
+class MemoryFakeRemote : public MemoryRemote {
+ public:
+ MemoryFakeRemote() : MemoryRemote(0) {}
+ virtual ~MemoryFakeRemote() = default;
+
+ protected:
+ bool PtraceRead(uint64_t, long* value) override {
+ *value = 0;
+ return true;
+ }
+};
+
#endif // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index 870ca19..aa7a23a 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#include <string>
+#include <vector>
+
#include <android-base/test_utils.h>
#include <android-base/file.h>
#include <gtest/gtest.h>
@@ -39,7 +42,7 @@
TemporaryFile* tf_ = nullptr;
};
-TEST_F(MemoryFileTest, offset_0) {
+TEST_F(MemoryFileTest, init_offset_0) {
WriteTestData();
ASSERT_TRUE(memory_.Init(tf_->path, 0));
@@ -49,7 +52,7 @@
ASSERT_STREQ("0123456789", buffer.data());
}
-TEST_F(MemoryFileTest, offset_non_zero) {
+TEST_F(MemoryFileTest, init_offset_non_zero) {
WriteTestData();
ASSERT_TRUE(memory_.Init(tf_->path, 10));
@@ -59,7 +62,7 @@
ASSERT_STREQ("abcdefghij", buffer.data());
}
-TEST_F(MemoryFileTest, offset_non_zero_larger_than_pagesize) {
+TEST_F(MemoryFileTest, init_offset_non_zero_larger_than_pagesize) {
size_t pagesize = getpagesize();
std::string large_string;
for (size_t i = 0; i < pagesize; i++) {
@@ -75,7 +78,7 @@
ASSERT_STREQ("abcdefgh", buffer.data());
}
-TEST_F(MemoryFileTest, offset_pagesize_aligned) {
+TEST_F(MemoryFileTest, init_offset_pagesize_aligned) {
size_t pagesize = getpagesize();
std::string data;
for (size_t i = 0; i < 2 * pagesize; i++) {
@@ -96,7 +99,7 @@
ASSERT_STREQ(expected_str.c_str(), buffer.data());
}
-TEST_F(MemoryFileTest, offset_pagesize_aligned_plus_extra) {
+TEST_F(MemoryFileTest, init_offset_pagesize_aligned_plus_extra) {
size_t pagesize = getpagesize();
std::string data;
for (size_t i = 0; i < 2 * pagesize; i++) {
@@ -117,6 +120,23 @@
ASSERT_STREQ(expected_str.c_str(), buffer.data());
}
+TEST_F(MemoryFileTest, init_offset_greater_than_filesize) {
+ size_t pagesize = getpagesize();
+ std::string data;
+ uint64_t file_size = 2 * pagesize + pagesize / 2;
+ for (size_t i = 0; i < file_size; i++) {
+ data += static_cast<char>((i / pagesize) + '0');
+ }
+ ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+
+ // Check offset > file size fails and aligned_offset > file size.
+ ASSERT_FALSE(memory_.Init(tf_->path, file_size + 2 * pagesize));
+ // Check offset == filesize fails.
+ ASSERT_FALSE(memory_.Init(tf_->path, file_size));
+ // Check aligned_offset < filesize, but offset > filesize fails.
+ ASSERT_FALSE(memory_.Init(tf_->path, 2 * pagesize + pagesize / 2 + pagesize / 4));
+}
+
TEST_F(MemoryFileTest, read_error) {
std::string data;
for (size_t i = 0; i < 5000; i++) {
@@ -137,32 +157,9 @@
ASSERT_TRUE(memory_.Read(4990, buffer.data(), 10));
ASSERT_FALSE(memory_.Read(4999, buffer.data(), 2));
ASSERT_TRUE(memory_.Read(4999, buffer.data(), 1));
-}
-TEST_F(MemoryFileTest, read_string) {
- std::string value("name_in_file");
- ASSERT_TRUE(android::base::WriteFully(tf_->fd, value.c_str(), value.size() + 1));
-
- std::string name;
- ASSERT_TRUE(memory_.Init(tf_->path, 0));
- ASSERT_TRUE(memory_.ReadString(0, &name));
- ASSERT_EQ("name_in_file", name);
- ASSERT_TRUE(memory_.ReadString(5, &name));
- ASSERT_EQ("in_file", name);
-}
-
-TEST_F(MemoryFileTest, read_string_error) {
- std::vector<uint8_t> buffer = { 0x23, 0x32, 0x45 };
- ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size()));
-
- std::string name;
- ASSERT_TRUE(memory_.Init(tf_->path, 0));
-
- // Read from a non-existant address.
- ASSERT_FALSE(memory_.ReadString(100, &name));
-
- // This should fail because there is no terminating \0
- ASSERT_FALSE(memory_.ReadString(0, &name));
+ // Check that overflow fails properly.
+ ASSERT_FALSE(memory_.Read(UINT64_MAX - 100, buffer.data(), 200));
}
TEST_F(MemoryFileTest, read_past_file_within_mapping) {
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index 0ba5f1c..ab999da 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -47,25 +47,6 @@
}
}
-TEST(MemoryLocalTest, read_string) {
- std::string name("string_in_memory");
-
- MemoryLocal local;
-
- std::vector<uint8_t> dst(1024);
- std::string dst_name;
- ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(name.c_str()), &dst_name));
- ASSERT_EQ("string_in_memory", dst_name);
-
- ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name));
- ASSERT_EQ("in_memory", dst_name);
-
- ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 10));
- ASSERT_EQ("in_memory", dst_name);
-
- ASSERT_FALSE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 9));
-}
-
TEST(MemoryLocalTest, read_illegal) {
MemoryLocal local;
@@ -73,3 +54,13 @@
ASSERT_FALSE(local.Read(0, dst.data(), 1));
ASSERT_FALSE(local.Read(0, dst.data(), 100));
}
+
+TEST(MemoryLocalTest, read_overflow) {
+ MemoryLocal local;
+
+ // On 32 bit this test doesn't necessarily cause an overflow. The 64 bit
+ // version will always go through the overflow check.
+ std::vector<uint8_t> dst(100);
+ uint64_t value;
+ ASSERT_FALSE(local.Read(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX));
+}
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index d636ec4..ee5ba01 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -17,6 +17,7 @@
#include <stdint.h>
#include <string.h>
+#include <memory>
#include <vector>
#include <gtest/gtest.h>
@@ -65,35 +66,14 @@
ASSERT_FALSE(range.Read(1020, dst.data(), 5));
ASSERT_FALSE(range.Read(1024, dst.data(), 1));
ASSERT_FALSE(range.Read(1024, dst.data(), 1024));
+
+ // Verify that reading up to the end works.
+ ASSERT_TRUE(range.Read(1020, dst.data(), 4));
}
-TEST_F(MemoryRangeTest, read_string_past_end) {
- std::string name("0123456789");
- memory_->SetMemory(0, name);
+TEST_F(MemoryRangeTest, read_overflow) {
+ std::vector<uint8_t> buffer(100);
- // Verify a read past the range fails.
- MemoryRange range(memory_, 0, 5);
- std::string dst_name;
- ASSERT_FALSE(range.ReadString(0, &dst_name));
-}
-
-TEST_F(MemoryRangeTest, read_string_to_end) {
- std::string name("0123456789");
- memory_->SetMemory(30, name);
-
- // Verify the range going to the end of the string works.
- MemoryRange range(memory_, 30, 30 + name.size() + 1);
- std::string dst_name;
- ASSERT_TRUE(range.ReadString(0, &dst_name));
- ASSERT_EQ("0123456789", dst_name);
-}
-
-TEST_F(MemoryRangeTest, read_string_fencepost) {
- std::string name("0123456789");
- memory_->SetMemory(10, name);
-
- // Verify the range set to one byte less than the end of the string fails.
- MemoryRange range(memory_, 10, 10 + name.size());
- std::string dst_name;
- ASSERT_FALSE(range.ReadString(0, &dst_name));
+ std::unique_ptr<MemoryRange> overflow(new MemoryRange(new MemoryFakeAlwaysReadZero, 100, 200));
+ ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100));
}
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index 7664c3e..e48edf7 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -33,6 +33,8 @@
#include "Memory.h"
+#include "MemoryFake.h"
+
class MemoryRemoteTest : public ::testing::Test {
protected:
static uint64_t NanoTime() {
@@ -121,6 +123,9 @@
ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 1, dst.data(), 1));
ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 4, dst.data(), 8));
+ // Check overflow condition is caught properly.
+ ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
+
ASSERT_EQ(0, munmap(src, pagesize));
ASSERT_TRUE(Detach(pid));
@@ -128,6 +133,14 @@
kill(pid, SIGKILL);
}
+TEST_F(MemoryRemoteTest, read_overflow) {
+ MemoryFakeRemote remote;
+
+ // Check overflow condition is caught properly.
+ std::vector<uint8_t> dst(200);
+ ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
+}
+
TEST_F(MemoryRemoteTest, read_illegal) {
pid_t pid;
if ((pid = fork()) == 0) {
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
new file mode 100644
index 0000000..51b5d7d
--- /dev/null
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "MemoryFake.h"
+
+TEST(MemoryTest, read32) {
+ MemoryFakeAlwaysReadZero memory;
+
+ uint32_t data = 0xffffffff;
+ ASSERT_TRUE(memory.Read32(0, &data));
+ ASSERT_EQ(0U, data);
+}
+
+TEST(MemoryTest, read64) {
+ MemoryFakeAlwaysReadZero memory;
+
+ uint64_t data = 0xffffffffffffffffULL;
+ ASSERT_TRUE(memory.Read64(0, &data));
+ ASSERT_EQ(0U, data);
+}
+
+struct FakeStruct {
+ int one;
+ bool two;
+ uint32_t three;
+ uint64_t four;
+};
+
+TEST(MemoryTest, read_field) {
+ MemoryFakeAlwaysReadZero memory;
+
+ FakeStruct data;
+ memset(&data, 0xff, sizeof(data));
+ ASSERT_TRUE(memory.ReadField(0, &data, &data.one, sizeof(data.one)));
+ ASSERT_EQ(0, data.one);
+
+ memset(&data, 0xff, sizeof(data));
+ ASSERT_TRUE(memory.ReadField(0, &data, &data.two, sizeof(data.two)));
+ ASSERT_FALSE(data.two);
+
+ memset(&data, 0xff, sizeof(data));
+ ASSERT_TRUE(memory.ReadField(0, &data, &data.three, sizeof(data.three)));
+ ASSERT_EQ(0U, data.three);
+
+ memset(&data, 0xff, sizeof(data));
+ ASSERT_TRUE(memory.ReadField(0, &data, &data.four, sizeof(data.four)));
+ ASSERT_EQ(0U, data.four);
+}
+
+TEST(MemoryTest, read_field_fails) {
+ MemoryFakeAlwaysReadZero memory;
+
+ FakeStruct data;
+ memset(&data, 0xff, sizeof(data));
+
+ ASSERT_FALSE(memory.ReadField(UINT64_MAX, &data, &data.three, sizeof(data.three)));
+
+ // Field and start reversed, should fail.
+ ASSERT_FALSE(memory.ReadField(100, &data.two, &data, sizeof(data.two)));
+ ASSERT_FALSE(memory.ReadField(0, &data.two, &data, sizeof(data.two)));
+}
+
+TEST(MemoryTest, read_string) {
+ std::string name("string_in_memory");
+
+ MemoryFake memory;
+
+ memory.SetMemory(100, name.c_str(), name.size() + 1);
+
+ std::string dst_name;
+ ASSERT_TRUE(memory.ReadString(100, &dst_name));
+ ASSERT_EQ("string_in_memory", dst_name);
+
+ ASSERT_TRUE(memory.ReadString(107, &dst_name));
+ ASSERT_EQ("in_memory", dst_name);
+
+ // Set size greater than string.
+ ASSERT_TRUE(memory.ReadString(107, &dst_name, 10));
+ ASSERT_EQ("in_memory", dst_name);
+
+ ASSERT_FALSE(memory.ReadString(107, &dst_name, 9));
+}
+
+TEST(MemoryTest, read_string_error) {
+ std::string name("short");
+
+ MemoryFake memory;
+
+ // Save everything except the terminating '\0'.
+ memory.SetMemory(0, name.c_str(), name.size());
+
+ std::string dst_name;
+ // Read from a non-existant address.
+ ASSERT_FALSE(memory.ReadString(100, &dst_name));
+
+ // This should fail because there is no terminating '\0'.
+ ASSERT_FALSE(memory.ReadString(0, &dst_name));
+
+ // This should pass because there is a terminating '\0'.
+ memory.SetData8(name.size(), '\0');
+ ASSERT_TRUE(memory.ReadString(0, &dst_name));
+ ASSERT_EQ("short", dst_name);
+}
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/event.logtags b/logcat/event.logtags
index 909f8e2..9f05351 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -36,6 +36,8 @@
# These are used for testing, do not modify without updating
# tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+# system/core/liblog/tests/liblog_benchmark.cpp
+# system/core/liblog/tests/liblog_test.cpp
42 answer (to life the universe etc|3)
314 pi
2718 e
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 8134936..64d1d2f 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -882,6 +882,7 @@
{ "grep", required_argument, nullptr, 'e' },
// hidden and undocumented reserved alias for --max-count
{ "head", required_argument, nullptr, 'm' },
+ { "help", no_argument, nullptr, 'h' },
{ id_str, required_argument, nullptr, 0 },
{ "last", no_argument, nullptr, 'L' },
{ "max-count", required_argument, nullptr, 'm' },
@@ -900,9 +901,8 @@
};
// clang-format on
- ret = getopt_long_r(argc, argv,
- ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
- &option_index, &optctx);
+ ret = getopt_long_r(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
+ long_options, &option_index, &optctx);
if (ret < 0) break;
switch (ret) {
@@ -1304,6 +1304,11 @@
"Option -%c needs an argument\n", optctx.optopt);
goto exit;
+ case 'h':
+ show_help(context);
+ show_format_help(context);
+ goto exit;
+
default:
logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n",
optctx.optopt);
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 a3a0176..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,8 +1664,34 @@
}
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) {
+ logcat_define(ctx);
+ FILE* fp = logcat_popen(ctx, command);
+ if (!fp) return 0;
+
+ std::string ret;
+ if (!android::base::ReadFdToString(fileno(fp), &ret)) return 0;
+ if (logcat_pclose(ctx, fp) != 0) return 0;
+
+ return ret.size();
+}
+
+TEST(logcat, help) {
+ size_t logcatHelpTextSize = commandOutputSize(logcat_executable " -h 2>&1");
+ EXPECT_LT(4096UL, logcatHelpTextSize);
+ 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/LogBuffer.cpp b/logd/LogBuffer.cpp
index 86ea6b4..0c7019a 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -109,11 +109,11 @@
LogBuffer::LogBuffer(LastLogTimes* times)
: monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
- pthread_mutex_init(&mLogElementsLock, NULL);
+ pthread_mutex_init(&mLogElementsLock, nullptr);
log_id_for_each(i) {
- lastLoggedElements[i] = NULL;
- droppedElements[i] = NULL;
+ lastLoggedElements[i] = nullptr;
+ droppedElements[i] = nullptr;
}
init();
@@ -196,7 +196,7 @@
new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
if (log_id != LOG_ID_SECURITY) {
int prio = ANDROID_LOG_INFO;
- const char* tag = NULL;
+ const char* tag = nullptr;
if (log_id == LOG_ID_EVENTS) {
tag = tagToName(elem->getTag());
} else {
@@ -222,24 +222,24 @@
//
// State Init
// incoming:
- // dropped = NULL
- // currentLast = NULL;
+ // dropped = nullptr
+ // currentLast = nullptr;
// elem = incoming message
// outgoing:
- // dropped = NULL -> State 0
+ // dropped = nullptr -> State 0
// currentLast = copy of elem
// log elem
// State 0
// incoming:
// count = 0
- // dropped = NULL
+ // dropped = nullptr
// currentLast = copy of last message
// elem = incoming message
// outgoing: if match != DIFFERENT
// dropped = copy of first identical message -> State 1
// currentLast = reference to elem
// break: if match == DIFFERENT
- // dropped = NULL -> State 0
+ // dropped = nullptr -> State 0
// delete copy of last message (incoming currentLast)
// currentLast = copy of elem
// log elem
@@ -266,7 +266,7 @@
// currentLast = reference to elem, sum liblog.
// break: if match == DIFFERENT
// delete dropped
- // dropped = NULL -> State 0
+ // dropped = nullptr -> State 0
// log reference to last held-back (currentLast)
// currentLast = copy of elem
// log elem
@@ -285,7 +285,7 @@
// currentLast = reference to elem
// break: if match == DIFFERENT
// log dropped (chatty message)
- // dropped = NULL -> State 0
+ // dropped = nullptr -> State 0
// log reference to last held-back (currentLast)
// currentLast = copy of elem
// log elem
@@ -350,7 +350,7 @@
} else { // State 1
delete dropped;
}
- droppedElements[log_id] = NULL;
+ droppedElements[log_id] = nullptr;
log(currentLast); // report last message in the series
} else { // State 0
delete currentLast;
@@ -654,7 +654,7 @@
// mLogElementsLock must be held when this function is called.
//
bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
- LogTimeEntry* oldest = NULL;
+ LogTimeEntry* oldest = nullptr;
bool busy = false;
bool clearAll = pruneRows == ULONG_MAX;
@@ -1076,9 +1076,11 @@
return retval;
}
-log_time LogBuffer::flushTo(
- SocketClient* reader, const log_time& start, bool privileged, bool security,
- int (*filter)(const LogBufferElement* element, void* arg), void* arg) {
+log_time LogBuffer::flushTo(SocketClient* reader, const log_time& start,
+ pid_t* lastTid, bool privileged, bool security,
+ int (*filter)(const LogBufferElement* element,
+ void* arg),
+ void* arg) {
LogBufferElementCollection::iterator it;
uid_t uid = reader->getUid();
@@ -1089,8 +1091,10 @@
it = mLogElements.begin();
} else {
LogBufferElementCollection::iterator last;
- // 30 second limit to continue search for out-of-order entries.
- log_time min = start - log_time(30, 0);
+ // 3 second limit to continue search for out-of-order entries.
+ log_time min = start - log_time(3, 0);
+ // Cap to 300 iterations we look back for out-of-order entries.
+ size_t count = 300;
// Client wants to start from some specified time. Chances are
// we are better off starting from the end of the time sorted list.
for (last = it = mLogElements.end(); it != mLogElements.begin();
@@ -1099,7 +1103,7 @@
LogBufferElement* element = *it;
if (element->getRealTime() > start) {
last = it;
- } else if (element->getRealTime() < min) {
+ } else if (!--count || (element->getRealTime() < min)) {
break;
}
}
@@ -1107,9 +1111,6 @@
}
log_time max = start;
- // Help detect if the valid message before is from the same source so
- // we can differentiate chatty filter types.
- pid_t lastTid[LOG_ID_MAX] = { 0 };
for (; it != mLogElements.end(); ++it) {
LogBufferElement* element = *it;
@@ -1137,14 +1138,17 @@
}
}
- bool sameTid = lastTid[element->getLogId()] == element->getTid();
- // Dropped (chatty) immediately following a valid log from the
- // same source in the same log buffer indicates we have a
- // multiple identical squash. chatty that differs source
- // is due to spam filter. chatty to chatty of different
- // source is also due to spam filter.
- lastTid[element->getLogId()] =
- (element->getDropped() && !sameTid) ? 0 : element->getTid();
+ bool sameTid = false;
+ if (lastTid) {
+ sameTid = lastTid[element->getLogId()] == element->getTid();
+ // Dropped (chatty) immediately following a valid log from the
+ // same source in the same log buffer indicates we have a
+ // multiple identical squash. chatty that differs source
+ // is due to spam filter. chatty to chatty of different
+ // source is also due to spam filter.
+ lastTid[element->getLogId()] =
+ (element->getDropped() && !sameTid) ? 0 : element->getTid();
+ }
pthread_mutex_unlock(&mLogElementsLock);
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index fcf6b9a..19d11cb 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -115,11 +115,15 @@
int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
const char* msg, unsigned short len);
+ // lastTid is an optional context to help detect if the last previous
+ // valid message was from the same source so we can differentiate chatty
+ // filter types (identical or expired)
log_time flushTo(SocketClient* writer, const log_time& start,
+ pid_t* lastTid, // &lastTid[LOG_ID_MAX] or nullptr
bool privileged, bool security,
int (*filter)(const LogBufferElement* element,
- void* arg) = NULL,
- void* arg = NULL);
+ void* arg) = nullptr,
+ void* arg = nullptr);
bool clear(log_id_t id, uid_t uid = AID_ROOT);
unsigned long getSize(log_id_t id);
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 620d4d0..af19279 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -182,7 +182,7 @@
} logFindStart(pid, logMask, sequence,
logbuf().isMonotonic() && android::isMonotonic(start));
- logbuf().flushTo(cli, sequence, FlushCommand::hasReadLogs(cli),
+ logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli),
FlushCommand::hasSecurityLogs(cli),
logFindStart.callback, &logFindStart);
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 04e531f..ccc550a 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -15,6 +15,7 @@
*/
#include <errno.h>
+#include <string.h>
#include <sys/prctl.h>
#include <private/android_logger.h>
@@ -47,7 +48,8 @@
mEnd(log_time(android_log_clockid())) {
mTimeout.tv_sec = timeout / NS_PER_SEC;
mTimeout.tv_nsec = timeout % NS_PER_SEC;
- pthread_cond_init(&threadTriggeredCondition, NULL);
+ memset(mLastTid, 0, sizeof(mLastTid));
+ pthread_cond_init(&threadTriggeredCondition, nullptr);
cleanSkip_Locked();
}
@@ -98,7 +100,7 @@
it++;
}
- me->mClient = NULL;
+ me->mClient = nullptr;
reader.release(client);
}
@@ -122,7 +124,7 @@
SocketClient* client = me->mClient;
if (!client) {
me->error();
- return NULL;
+ return nullptr;
}
LogBuffer& logbuf = me->mReader.logbuf();
@@ -151,12 +153,12 @@
unlock();
if (me->mTail) {
- logbuf.flushTo(client, start, privileged, security, FilterFirstPass,
- me);
+ logbuf.flushTo(client, start, nullptr, privileged, security,
+ FilterFirstPass, me);
me->leadingDropped = true;
}
- start = logbuf.flushTo(client, start, privileged, security,
- FilterSecondPass, me);
+ start = logbuf.flushTo(client, start, me->mLastTid, privileged,
+ security, FilterSecondPass, me);
lock();
@@ -182,7 +184,7 @@
pthread_cleanup_pop(true);
- return NULL;
+ return nullptr;
}
// A first pass to count the number of elements
@@ -281,7 +283,5 @@
}
void LogTimeEntry::cleanSkip_Locked(void) {
- for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t)(i + 1)) {
- skipAhead[i] = 0;
- }
+ memset(skipAhead, 0, sizeof(skipAhead));
}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 9a3ddab..ec8252e 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -44,6 +44,7 @@
const unsigned int mLogMask;
const pid_t mPid;
unsigned int skipAhead[LOG_ID_MAX];
+ pid_t mLastTid[LOG_ID_MAX];
unsigned long mCount;
unsigned long mTail;
unsigned long mIndex;
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index ddff393..d0101ed 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -426,7 +426,7 @@
" BM_log_maximum_retry"
" BM_log_maximum"
" BM_clock_overhead"
- " BM_log_overhead"
+ " BM_log_print_overhead"
" BM_log_latency"
" BM_log_delay",
"r")));
@@ -434,13 +434,13 @@
char buffer[5120];
static const char* benchmarks[] = {
- "BM_log_maximum_retry ", "BM_log_maximum ", "BM_clock_overhead ",
- "BM_log_overhead ", "BM_log_latency ", "BM_log_delay "
+ "BM_log_maximum_retry ", "BM_log_maximum ", "BM_clock_overhead ",
+ "BM_log_print_overhead ", "BM_log_latency ", "BM_log_delay "
};
static const unsigned int log_maximum_retry = 0;
static const unsigned int log_maximum = 1;
static const unsigned int clock_overhead = 2;
- static const unsigned int log_overhead = 3;
+ static const unsigned int log_print_overhead = 3;
static const unsigned int log_latency = 4;
static const unsigned int log_delay = 5;
@@ -469,21 +469,23 @@
}
EXPECT_GE(200000UL, ns[log_maximum_retry]); // 104734 user
+ EXPECT_NE(0UL, ns[log_maximum_retry]); // failure to parse
EXPECT_GE(90000UL, ns[log_maximum]); // 46913 user
+ EXPECT_NE(0UL, ns[log_maximum]); // failure to parse
EXPECT_GE(4096UL, ns[clock_overhead]); // 4095
+ EXPECT_NE(0UL, ns[clock_overhead]); // failure to parse
- EXPECT_GE(250000UL, ns[log_overhead]); // 126886 user
+ EXPECT_GE(250000UL, ns[log_print_overhead]); // 126886 user
+ EXPECT_NE(0UL, ns[log_print_overhead]); // failure to parse
EXPECT_GE(10000000UL,
ns[log_latency]); // 1453559 user space (background cgroup)
+ EXPECT_NE(0UL, ns[log_latency]); // failure to parse
EXPECT_GE(20000000UL, ns[log_delay]); // 10500289 user
-
- for (unsigned i = 0; i < arraysize(ns); ++i) {
- EXPECT_NE(0UL, ns[i]);
- }
+ EXPECT_NE(0UL, ns[log_delay]); // failure to parse
alloc_statistics(&buf, &len);
diff --git a/qemu_pipe/Android.mk b/qemu_pipe/Android.mk
deleted file mode 100644
index 6e0144c..0000000
--- a/qemu_pipe/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright 2011 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-common_static_libraries := \
- libbase
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-LOCAL_SRC_FILES:= \
- qemu_pipe.cpp
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/include \
- system/base/include
-LOCAL_MODULE:= libqemu_pipe
-LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-include $(BUILD_STATIC_LIBRARY)
diff --git a/qemu_pipe/include/qemu_pipe.h b/qemu_pipe/include/qemu_pipe.h
deleted file mode 100644
index 0987498..0000000
--- a/qemu_pipe/include/qemu_pipe.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2011 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_CORE_INCLUDE_QEMU_PIPE_H
-#define ANDROID_CORE_INCLUDE_QEMU_PIPE_H
-
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-// Try to open a new Qemu fast-pipe. This function returns a file descriptor
-// that can be used to communicate with a named service managed by the
-// emulator.
-//
-// This file descriptor can be used as a standard pipe/socket descriptor.
-//
-// 'pipeName' is the name of the emulator service you want to connect to,
-// and should begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
-// For backward compatibility, the 'pipe:' prefix can be omitted, and in
-// that case, qemu_pipe_open will add it for you.
-
-// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
-//
-// EINVAL -> unknown/unsupported pipeName
-// ENOSYS -> fast pipes not available in this system.
-//
-// ENOSYS should never happen, except if you're trying to run within a
-// misconfigured emulator.
-//
-// You should be able to open several pipes to the same pipe service,
-// except for a few special cases (e.g. GSM modem), where EBUSY will be
-// returned if more than one client tries to connect to it.
-int qemu_pipe_open(const char* pipeName);
-
-// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
-// This really adds a 4-hexchar prefix describing the payload size.
-// Returns 0 on success, and -1 on error.
-int qemu_pipe_frame_send(int fd, const void* buff, size_t len);
-
-// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
-// If the framed message is larger than |len|, then this returns -1 and the
-// content is lost. Otherwise, this returns the size of the message. NOTE:
-// empty messages are possible in a framed wire protocol and do not mean
-// end-of-stream.
-int qemu_pipe_frame_recv(int fd, void* buff, size_t len);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ANDROID_CORE_INCLUDE_QEMU_PIPE_H */
diff --git a/qemu_pipe/qemu_pipe.cpp b/qemu_pipe/qemu_pipe.cpp
deleted file mode 100644
index beeccb0..0000000
--- a/qemu_pipe/qemu_pipe.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2011 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 "qemu_pipe.h"
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-
-#include <android-base/file.h>
-
-using android::base::ReadFully;
-using android::base::WriteFully;
-
-// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
-// occurs during pipe operations. The macro should simply take a printf-style
-// formatting string followed by optional arguments.
-#ifndef QEMU_PIPE_DEBUG
-# define QEMU_PIPE_DEBUG(...) (void)0
-#endif
-
-int qemu_pipe_open(const char* pipeName) {
- // Sanity check.
- if (!pipeName) {
- errno = EINVAL;
- return -1;
- }
-
- int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
- if (fd < 0) {
- QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
- strerror(errno));
- return -1;
- }
-
- // Write the pipe name, *including* the trailing zero which is necessary.
- size_t pipeNameLen = strlen(pipeName);
- if (WriteFully(fd, pipeName, pipeNameLen + 1U)) {
- return fd;
- }
-
- // now, add 'pipe:' prefix and try again
- // Note: host side will wait for the trailing '\0' to start
- // service lookup.
- const char pipe_prefix[] = "pipe:";
- if (WriteFully(fd, pipe_prefix, strlen(pipe_prefix)) &&
- WriteFully(fd, pipeName, pipeNameLen + 1U)) {
- return fd;
- }
- QEMU_PIPE_DEBUG("%s: Could not write to %s pipe service: %s",
- __FUNCTION__, pipeName, strerror(errno));
- close(fd);
- return -1;
-}
-
-int qemu_pipe_frame_send(int fd, const void* buff, size_t len) {
- char header[5];
- snprintf(header, sizeof(header), "%04zx", len);
- if (!WriteFully(fd, header, 4)) {
- QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
- return -1;
- }
- if (!WriteFully(fd, buff, len)) {
- QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
- return -1;
- }
- return 0;
-}
-
-int qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
- char header[5];
- if (!ReadFully(fd, header, 4)) {
- QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
- return -1;
- }
- header[4] = '\0';
- size_t size;
- if (sscanf(header, "%04zx", &size) != 1) {
- QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
- return -1;
- }
- if (size > len) {
- QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
- len);
- return -1;
- }
- if (!ReadFully(fd, buff, size)) {
- QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
- strerror(errno));
- return -1;
- }
- return size;
-}
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
new file mode 100644
index 0000000..81cf315
--- /dev/null
+++ b/shell_and_utilities/Android.bp
@@ -0,0 +1,13 @@
+phony {
+ name: "shell_and_utilities",
+ required: [
+ "bzip2",
+ "grep",
+ "gzip",
+ "mkshrc",
+ "reboot",
+ "sh",
+ "toolbox",
+ "toybox",
+ ],
+}