Merge "logd: Stop calling prctl(PR_SET_KEEPCAPS, 1)"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index dc45959..e6f8716 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -74,3 +74,6 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 11e14dd..32cc0cd 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -310,6 +310,7 @@
     {"kernel_panic,adsp", 165},
     {"kernel_panic,dsps", 166},
     {"kernel_panic,wcnss", 167},
+    {"kernel_panic,_sde_encoder_phys_cmd_handle_ppdone_timeout", 168},
 };
 
 // Converts a string value representing the reason the system booted to an
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6e9ffba..9856126 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1190,27 +1190,6 @@
     return 0;
 }
 
-int fs_mgr_unmount_all(struct fstab *fstab)
-{
-    int i = 0;
-    int ret = 0;
-
-    if (!fstab) {
-        return -1;
-    }
-
-    while (fstab->recs[i].blk_device) {
-        if (umount(fstab->recs[i].mount_point)) {
-            LERROR << "Cannot unmount filesystem at "
-                   << fstab->recs[i].mount_point;
-            ret = -1;
-        }
-        i++;
-    }
-
-    return ret;
-}
-
 /* This must be called after mount_all, because the mkswap command needs to be
  * available.
  */
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 5a9cb65..2020fa6 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -139,38 +139,23 @@
 };
 
 std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
-    std::string cmdline;
-    if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
-        PERROR << "Failed to read /proc/cmdline";
-        return nullptr;
-    }
-
     std::unique_ptr<FsManagerAvbVerifier> avb_verifier(new FsManagerAvbVerifier());
     if (!avb_verifier) {
         LERROR << "Failed to create unique_ptr<FsManagerAvbVerifier>";
         return nullptr;
     }
 
-    std::string digest;
-    std::string hash_alg;
-    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
-        std::vector<std::string> pieces = android::base::Split(entry, "=");
-        const std::string& key = pieces[0];
-        const std::string& value = pieces[1];
-
-        if (key == "androidboot.vbmeta.hash_alg") {
-            hash_alg = value;
-        } else if (key == "androidboot.vbmeta.size") {
-            if (!android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
-                return nullptr;
-            }
-        } else if (key == "androidboot.vbmeta.digest") {
-            digest = value;
-        }
+    std::string value;
+    if (!fs_mgr_get_boot_config("vbmeta.size", &value) ||
+        !android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
+        LERROR << "Invalid hash size: " << value.c_str();
+        return nullptr;
     }
 
     // Reads hash algorithm.
     size_t expected_digest_size = 0;
+    std::string hash_alg;
+    fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg);
     if (hash_alg == "sha256") {
         expected_digest_size = SHA256_DIGEST_LENGTH * 2;
         avb_verifier->hash_alg_ = kSHA256;
@@ -183,6 +168,8 @@
     }
 
     // Reads digest.
+    std::string digest;
+    fs_mgr_get_boot_config("vbmeta.digest", &digest);
     if (digest.size() != expected_digest_size) {
         LERROR << "Unexpected digest size: " << digest.size()
                << " (expected: " << expected_digest_size << ")";
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 9c5d3f3..733ad55 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
+#include <algorithm>
+#include <iterator>
 #include <string>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
@@ -23,46 +26,71 @@
 
 #include "fs_mgr_priv.h"
 
-// Tries to get the given boot config value from kernel cmdline.
-// Returns true if successfully found, false otherwise.
-bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline) {
+    static constexpr char quote = '"';
+
+    std::vector<std::pair<std::string, std::string>> result;
+    size_t base = 0;
+    while (true) {
+        // skip quoted spans
+        auto found = base;
+        while (((found = cmdline.find_first_of(" \"", found)) != cmdline.npos) &&
+               (cmdline[found] == quote)) {
+            // unbalanced quote is ok
+            if ((found = cmdline.find(quote, found + 1)) == cmdline.npos) break;
+            ++found;
+        }
+        std::string piece;
+        auto source = cmdline.substr(base, found - base);
+        std::remove_copy(source.begin(), source.end(),
+                         std::back_insert_iterator<std::string>(piece), quote);
+        auto equal_sign = piece.find('=');
+        if (equal_sign == piece.npos) {
+            if (!piece.empty()) {
+                // no difference between <key> and <key>=
+                result.emplace_back(std::move(piece), "");
+            }
+        } else {
+            result.emplace_back(piece.substr(0, equal_sign), piece.substr(equal_sign + 1));
+        }
+        if (found == cmdline.npos) break;
+        base = found + 1;
+    }
+
+    return result;
+}
+
+bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& android_key,
+                                        std::string* out_val) {
     FS_MGR_CHECK(out_val != nullptr);
 
-    std::string cmdline;
-    std::string cmdline_key("androidboot." + key);
-    if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
-        for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
-            std::vector<std::string> pieces = android::base::Split(entry, "=");
-            if (pieces.size() == 2) {
-                if (pieces[0] == cmdline_key) {
-                    *out_val = pieces[1];
-                    return true;
-                }
-            }
+    const std::string cmdline_key("androidboot." + android_key);
+    for (const auto& [key, value] : fs_mgr_parse_boot_config(cmdline)) {
+        if (key == cmdline_key) {
+            *out_val = value;
+            return true;
         }
     }
 
+    *out_val = "";
     return false;
 }
 
-// Tries to get the boot config value in properties, kernel cmdline and
-// device tree (in that order).  returns 'true' if successfully found, 'false'
-// otherwise
+// Tries to get the given boot config value from kernel cmdline.
+// Returns true if successfully found, false otherwise.
+bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
+    std::string cmdline;
+    if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) return false;
+    return fs_mgr_get_boot_config_from_kernel(cmdline, key, out_val);
+}
+
+// Tries to get the boot config value in device tree, properties and
+// kernel cmdline (in that order).  Returns 'true' if successfully
+// found, 'false' otherwise.
 bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
     FS_MGR_CHECK(out_val != nullptr);
 
-    // first check if we have "ro.boot" property already
-    *out_val = android::base::GetProperty("ro.boot." + key, "");
-    if (!out_val->empty()) {
-        return true;
-    }
-
-    // fallback to kernel cmdline, properties may not be ready yet
-    if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
-        return true;
-    }
-
-    // lastly, check the device tree
+    // firstly, check the device tree
     if (is_dt_compatible()) {
         std::string file_name = get_android_dt_dir() + "/" + key;
         if (android::base::ReadFileToString(file_name, out_val)) {
@@ -73,5 +101,16 @@
         }
     }
 
+    // next, check if we have "ro.boot" property already
+    *out_val = android::base::GetProperty("ro.boot." + key, "");
+    if (!out_val->empty()) {
+        return true;
+    }
+
+    // finally, fallback to kernel cmdline, properties may not be ready yet
+    if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
+        return true;
+    }
+
     return false;
 }
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index af4d6c1..a14dba3 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -679,7 +679,6 @@
     // We can't call fs_mgr_free_fstab because a->recs still references the
     // memory allocated by strdup.
     free(b->recs);
-    free(b->fstab_filename);
     free(b);
 
     a->num_entries = total_entries;
@@ -741,9 +740,7 @@
     }
 
     fstab = fs_mgr_read_fstab_file(fstab_file);
-    if (fstab) {
-        fstab->fstab_filename = strdup(fstab_path);
-    } else {
+    if (!fstab) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << fstab_path << "'";
     }
 
@@ -854,9 +851,6 @@
     /* Free the fstab_recs array created by calloc(3) */
     free(fstab->recs);
 
-    /* Free the fstab filename */
-    free(fstab->fstab_filename);
-
     /* Free fstab */
     free(fstab);
 }
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
index d98dc02..417fb38 100644
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -19,7 +19,13 @@
 
 #include <sys/cdefs.h>
 #include <string>
+#include <utility>
+#include <vector>
 
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline);
+
+bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& key,
+                                        std::string* out_val);
 bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
 bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
 
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 72f019e..c1b2ed9 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -70,7 +70,6 @@
                     char *tmp_mount_point);
 int fs_mgr_do_mount_one(struct fstab_rec *rec);
 int fs_mgr_do_tmpfs_mount(const char *n_name);
-int fs_mgr_unmount_all(struct fstab *fstab);
 struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
 void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
 bool fs_mgr_load_verity_state(int* mode);
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index d232cca..b1ee328 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -33,7 +33,6 @@
 struct fstab {
     int num_entries;
     struct fstab_rec* recs;
-    char* fstab_filename;
 };
 
 struct fstab_rec {
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
new file mode 100644
index 0000000..5497223
--- /dev/null
+++ b/fs_mgr/tests/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2018 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.
+
+cc_test {
+    name: "fs_mgr_unit_test",
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libfs_mgr",
+        "libfstab",
+    ],
+
+    srcs: [
+        "fs_mgr_test.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
new file mode 100644
index 0000000..2e76752
--- /dev/null
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 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 <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "../fs_mgr_priv_boot_config.h"
+
+namespace {
+
+const std::string cmdline =
+        "rcupdate.rcu_expedited=1 rootwait ro "
+        "init=/init androidboot.bootdevice=1d84000.ufshc "
+        "androidboot.baseband=sdy androidboot.keymaster=1  skip_initramfs "
+        "androidboot.serialno=BLAHBLAHBLAH androidboot.slot_suffix=_a "
+        "androidboot.hardware.platform=sdw813 androidboot.hardware=foo "
+        "androidboot.revision=EVT1.0 androidboot.bootloader=burp-0.1-7521 "
+        "androidboot.hardware.sku=mary androidboot.hardware.radio.subtype=0 "
+        "androidboot.dtbo_idx=2 androidboot.mode=normal "
+        "androidboot.hardware.ddr=1GB,combuchi,LPDDR4X "
+        "androidboot.ddr_info=combuchiandroidboot.ddr_size=2GB "
+        "androidboot.hardware.ufs=2GB,combushi "
+        "androidboot.boottime=0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123 "
+        "androidboot.ramdump=disabled "
+        "dm=\"1 vroot none ro 1,0 10416 verity 1 624684 fec_start 624684\" "
+        "root=/dev/dm-0 "
+        "androidboot.vbmeta.device=PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb "
+        "androidboot.vbmeta.avb_version=\"1.1\" "
+        "androidboot.vbmeta.device_state=unlocked "
+        "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=5248 "
+        "androidboot.vbmeta.digest="
+        "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860 "
+        "androidboot.vbmeta.invalidate_on_error=yes "
+        "androidboot.veritymode=enforcing androidboot.verifiedbootstate=orange "
+        "androidboot.space=\"sha256 5248 androidboot.nospace=nope\" "
+        "printk.devkmsg=on msm_rtb.filter=0x237 ehci-hcd.park=3 "
+        "\"string =\"\"string '\" "
+        "service_locator.enable=1 firmware_class.path=/vendor/firmware "
+        "cgroup.memory=nokmem lpm_levels.sleep_disabled=1 "
+        "buildvariant=userdebug  console=null "
+        "terminator=\"truncated";
+
+const std::vector<std::pair<std::string, std::string>> result_space = {
+        {"rcupdate.rcu_expedited", "1"},
+        {"rootwait", ""},
+        {"ro", ""},
+        {"init", "/init"},
+        {"androidboot.bootdevice", "1d84000.ufshc"},
+        {"androidboot.baseband", "sdy"},
+        {"androidboot.keymaster", "1"},
+        {"skip_initramfs", ""},
+        {"androidboot.serialno", "BLAHBLAHBLAH"},
+        {"androidboot.slot_suffix", "_a"},
+        {"androidboot.hardware.platform", "sdw813"},
+        {"androidboot.hardware", "foo"},
+        {"androidboot.revision", "EVT1.0"},
+        {"androidboot.bootloader", "burp-0.1-7521"},
+        {"androidboot.hardware.sku", "mary"},
+        {"androidboot.hardware.radio.subtype", "0"},
+        {"androidboot.dtbo_idx", "2"},
+        {"androidboot.mode", "normal"},
+        {"androidboot.hardware.ddr", "1GB,combuchi,LPDDR4X"},
+        {"androidboot.ddr_info", "combuchiandroidboot.ddr_size=2GB"},
+        {"androidboot.hardware.ufs", "2GB,combushi"},
+        {"androidboot.boottime", "0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123"},
+        {"androidboot.ramdump", "disabled"},
+        {"dm", "1 vroot none ro 1,0 10416 verity 1 624684 fec_start 624684"},
+        {"root", "/dev/dm-0"},
+        {"androidboot.vbmeta.device", "PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb"},
+        {"androidboot.vbmeta.avb_version", "1.1"},
+        {"androidboot.vbmeta.device_state", "unlocked"},
+        {"androidboot.vbmeta.hash_alg", "sha256"},
+        {"androidboot.vbmeta.size", "5248"},
+        {"androidboot.vbmeta.digest",
+         "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860"},
+        {"androidboot.vbmeta.invalidate_on_error", "yes"},
+        {"androidboot.veritymode", "enforcing"},
+        {"androidboot.verifiedbootstate", "orange"},
+        {"androidboot.space", "sha256 5248 androidboot.nospace=nope"},
+        {"printk.devkmsg", "on"},
+        {"msm_rtb.filter", "0x237"},
+        {"ehci-hcd.park", "3"},
+        {"string ", "string '"},
+        {"service_locator.enable", "1"},
+        {"firmware_class.path", "/vendor/firmware"},
+        {"cgroup.memory", "nokmem"},
+        {"lpm_levels.sleep_disabled", "1"},
+        {"buildvariant", "userdebug"},
+        {"console", "null"},
+        {"terminator", "truncated"},
+};
+
+}  // namespace
+
+TEST(fs_mgr, fs_mgr_parse_boot_config) {
+    EXPECT_EQ(result_space, fs_mgr_parse_boot_config(cmdline));
+}
+
+TEST(fs_mgr, fs_mgr_get_boot_config_from_kernel_cmdline) {
+    std::string content;
+    for (const auto& entry : result_space) {
+        static constexpr char androidboot[] = "androidboot.";
+        if (!android::base::StartsWith(entry.first, androidboot)) continue;
+        auto key = entry.first.substr(strlen(androidboot));
+        EXPECT_TRUE(fs_mgr_get_boot_config_from_kernel(cmdline, key, &content)) << " for " << key;
+        EXPECT_EQ(entry.second, content);
+    }
+    EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "vbmeta.avb_versio", &content));
+    EXPECT_TRUE(content.empty()) << content;
+    EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "nospace", &content));
+    EXPECT_TRUE(content.empty()) << content;
+}
diff --git a/init/init.cpp b/init/init.cpp
index 82648d9..b494bcc 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -604,47 +604,61 @@
     if (is_first_stage) {
         boot_clock::time_point start_time = boot_clock::now();
 
+        std::vector<std::pair<std::string, int>> errors;
+#define CHECKCALL(x) \
+    if (x != 0) errors.emplace_back(#x " failed", errno);
+
         // Clear the umask.
         umask(0);
 
-        clearenv();
-        setenv("PATH", _PATH_DEFPATH, 1);
+        CHECKCALL(clearenv());
+        CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
         // Get the basic filesystem setup we need put together in the initramdisk
         // on / and then we'll let the rc file figure out the rest.
-        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
-        mkdir("/dev/pts", 0755);
-        mkdir("/dev/socket", 0755);
-        mount("devpts", "/dev/pts", "devpts", 0, NULL);
-        #define MAKE_STR(x) __STRING(x)
-        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
+        CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
+        CHECKCALL(mkdir("/dev/pts", 0755));
+        CHECKCALL(mkdir("/dev/socket", 0755));
+        CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
+#define MAKE_STR(x) __STRING(x)
+        CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
+#undef MAKE_STR
         // Don't expose the raw commandline to unprivileged processes.
-        chmod("/proc/cmdline", 0440);
+        CHECKCALL(chmod("/proc/cmdline", 0440));
         gid_t groups[] = { AID_READPROC };
-        setgroups(arraysize(groups), groups);
-        mount("sysfs", "/sys", "sysfs", 0, NULL);
-        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
+        CHECKCALL(setgroups(arraysize(groups), groups));
+        CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
+        CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
 
-        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
+        CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
 
         if constexpr (WORLD_WRITABLE_KMSG) {
-            mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
+            CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
         }
 
-        mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
-        mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
+        CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
+        CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
 
         // Mount staging areas for devices managed by vold
         // See storage config details at http://source.android.com/devices/storage/
-        mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
-              "mode=0755,uid=0,gid=1000");
+        CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                        "mode=0755,uid=0,gid=1000"));
         // /mnt/vendor is used to mount vendor-specific partitions that can not be
         // part of the vendor partition, e.g. because they are mounted read-write.
-        mkdir("/mnt/vendor", 0755);
+        CHECKCALL(mkdir("/mnt/vendor", 0755));
+
+#undef CHECKCALL
 
         // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
         // talk to the outside world...
         InitKernelLogging(argv);
 
+        if (!errors.empty()) {
+            for (const auto& [error_string, error_errno] : errors) {
+                LOG(ERROR) << error_string << " " << strerror(error_errno);
+            }
+            LOG(FATAL) << "Init encountered errors starting first stage, aborting";
+        }
+
         LOG(INFO) << "init first stage started!";
 
         if (!DoFirstStageMount()) {
diff --git a/init/parser.cpp b/init/parser.cpp
index ee6ee06..4f1cac4 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -70,24 +70,23 @@
             case T_EOF:
                 end_section();
                 return;
-            case T_NEWLINE:
+            case T_NEWLINE: {
                 state.line++;
                 if (args.empty()) break;
                 // If we have a line matching a prefix we recognize, call its callback and unset any
                 // current section parsers.  This is meant for /sys/ and /dev/ line entries for
                 // uevent.
-                for (const auto& [prefix, callback] : line_callbacks_) {
-                    if (android::base::StartsWith(args[0], prefix)) {
-                        end_section();
+                auto line_callback = std::find_if(
+                    line_callbacks_.begin(), line_callbacks_.end(),
+                    [&args](const auto& c) { return android::base::StartsWith(args[0], c.first); });
+                if (line_callback != line_callbacks_.end()) {
+                    end_section();
 
-                        if (auto result = callback(std::move(args)); !result) {
-                            parse_error_count_++;
-                            LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
-                        }
-                        break;
+                    if (auto result = line_callback->second(std::move(args)); !result) {
+                        parse_error_count_++;
+                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                     }
-                }
-                if (section_parsers_.count(args[0])) {
+                } else if (section_parsers_.count(args[0])) {
                     end_section();
                     section_parser = section_parsers_[args[0]].get();
                     section_start_line = state.line;
@@ -111,6 +110,7 @@
                 }
                 args.clear();
                 break;
+            }
             case T_TEXT:
                 args.emplace_back(state.text);
                 break;
diff --git a/libcutils/OWNERS b/libcutils/OWNERS
new file mode 100644
index 0000000..c18ed51
--- /dev/null
+++ b/libcutils/OWNERS
@@ -0,0 +1,4 @@
+cferris@google.com
+enh@google.com
+jmgao@google.com
+tomcherry@google.com
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index d4cef7c..df5da65 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -223,6 +223,7 @@
         "tests/files/offline/art_quick_osr_stub_arm/*",
         "tests/files/offline/bad_eh_frame_hdr_arm64/*",
         "tests/files/offline/debug_frame_first_x86/*",
+        "tests/files/offline/debug_frame_load_bias_arm/*",
         "tests/files/offline/eh_frame_hdr_begin_x86_64/*",
         "tests/files/offline/jit_debug_arm/*",
         "tests/files/offline/jit_debug_x86/*",
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 6e397e3..818f5d1 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -31,6 +31,8 @@
 
 namespace unwindstack {
 
+static constexpr uint8_t LOG_CFA_REG = 64;
+
 void ArmExidx::LogRawData() {
   std::string log_str("Raw Data:");
   for (const uint8_t data : data_) {
@@ -63,8 +65,10 @@
   if (data == 1) {
     // This is a CANT UNWIND entry.
     status_ = ARM_STATUS_NO_UNWIND;
-    if (log_) {
-      log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+      }
       log(log_indent_, "[cantunwind]");
     }
     return false;
@@ -86,7 +90,7 @@
       // If this didn't end with a finish op, add one.
       data_.push_back(ARM_OP_FINISH);
     }
-    if (log_) {
+    if (log_type_ == ARM_LOG_FULL) {
       LogRawData();
     }
     return true;
@@ -163,7 +167,7 @@
     data_.push_back(ARM_OP_FINISH);
   }
 
-  if (log_) {
+  if (log_type_ == ARM_LOG_FULL) {
     LogRawData();
   }
   return true;
@@ -190,32 +194,45 @@
   registers |= byte;
   if (registers == 0) {
     // 10000000 00000000: Refuse to unwind
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Refuse to unwind");
     }
     status_ = ARM_STATUS_NO_UNWIND;
     return false;
   }
   // 1000iiii iiiiiiii: Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
-  if (log_) {
-    bool add_comma = false;
-    std::string msg = "pop {";
-    for (size_t i = 0; i < 12; i++) {
-      if (registers & (1 << i)) {
-        if (add_comma) {
-          msg += ", ";
+  registers <<= 4;
+
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      bool add_comma = false;
+      std::string msg = "pop {";
+      for (size_t reg = 4; reg < 16; reg++) {
+        if (registers & (1 << reg)) {
+          if (add_comma) {
+            msg += ", ";
+          }
+          msg += android::base::StringPrintf("r%zu", reg);
+          add_comma = true;
         }
-        msg += android::base::StringPrintf("r%zu", i + 4);
-        add_comma = true;
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      uint32_t cfa_offset = __builtin_popcount(registers) * 4;
+      log_cfa_offset_ += cfa_offset;
+      for (size_t reg = 4; reg < 16; reg++) {
+        if (registers & (1 << reg)) {
+          log_regs_[reg] = cfa_offset;
+          cfa_offset -= 4;
+        }
       }
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
   }
 
-  registers <<= 4;
   for (size_t reg = 4; reg < 16; reg++) {
     if (registers & (1 << reg)) {
       if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
@@ -246,15 +263,20 @@
   if (bits == 13 || bits == 15) {
     // 10011101: Reserved as prefix for ARM register to register moves
     // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "[Reserved]");
     }
     status_ = ARM_STATUS_RESERVED;
     return false;
   }
   // 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
-  if (log_) {
-    log(log_indent_, "vsp = r%d", bits);
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      log(log_indent_, "vsp = r%d", bits);
+    } else {
+      log_regs_[LOG_CFA_REG] = bits;
+    }
+
     if (log_skip_execution_) {
       return true;
     }
@@ -270,17 +292,36 @@
 
   // 10100nnn: Pop r4-r[4+nnn]
   // 10101nnn: Pop r4-r[4+nnn], r14
-  if (log_) {
-    std::string msg = "pop {r4";
+  if (log_type_ != ARM_LOG_NONE) {
     uint8_t end_reg = byte & 0x7;
-    if (end_reg) {
-      msg += android::base::StringPrintf("-r%d", 4 + end_reg);
-    }
-    if (byte & 0x8) {
-      log(log_indent_, "%s, r14}", msg.c_str());
+    if (log_type_ == ARM_LOG_FULL) {
+      std::string msg = "pop {r4";
+      if (end_reg) {
+        msg += android::base::StringPrintf("-r%d", 4 + end_reg);
+      }
+      if (byte & 0x8) {
+        log(log_indent_, "%s, r14}", msg.c_str());
+      } else {
+        log(log_indent_, "%s}", msg.c_str());
+      }
     } else {
-      log(log_indent_, "%s}", msg.c_str());
+      end_reg += 4;
+      uint32_t cfa_offset = (end_reg - 3) * 4;
+      if (byte & 0x8) {
+        cfa_offset += 4;
+      }
+      log_cfa_offset_ += cfa_offset;
+
+      for (uint8_t reg = 4; reg <= end_reg; reg++) {
+        log_regs_[reg] = cfa_offset;
+        cfa_offset -= 4;
+      }
+
+      if (byte & 0x8) {
+        log_regs_[14] = cfa_offset;
+      }
     }
+
     if (log_skip_execution_) {
       return true;
     }
@@ -307,8 +348,11 @@
 
 inline bool ArmExidx::DecodePrefix_10_11_0000() {
   // 10110000: Finish
-  if (log_) {
-    log(log_indent_, "finish");
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      log(log_indent_, "finish");
+    }
+
     if (log_skip_execution_) {
       status_ = ARM_STATUS_FINISH;
       return false;
@@ -326,7 +370,7 @@
 
   if (byte == 0) {
     // 10110001 00000000: Spare
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
@@ -334,7 +378,7 @@
   }
   if (byte >> 4) {
     // 10110001 xxxxyyyy: Spare (xxxx != 0000)
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
@@ -342,19 +386,32 @@
   }
 
   // 10110001 0000iiii: Pop integer registers under mask {r3, r2, r1, r0}
-  if (log_) {
-    bool add_comma = false;
-    std::string msg = "pop {";
-    for (size_t i = 0; i < 4; i++) {
-      if (byte & (1 << i)) {
-        if (add_comma) {
-          msg += ", ";
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      bool add_comma = false;
+      std::string msg = "pop {";
+      for (size_t i = 0; i < 4; i++) {
+        if (byte & (1 << i)) {
+          if (add_comma) {
+            msg += ", ";
+          }
+          msg += android::base::StringPrintf("r%zu", i);
+          add_comma = true;
         }
-        msg += android::base::StringPrintf("r%zu", i);
-        add_comma = true;
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      byte &= 0xf;
+      uint32_t cfa_offset = __builtin_popcount(byte) * 4;
+      log_cfa_offset_ += cfa_offset;
+      for (size_t reg = 0; reg < 4; reg++) {
+        if (byte & (1 << reg)) {
+          log_regs_[reg] = cfa_offset;
+          cfa_offset -= 4;
+        }
       }
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
@@ -373,6 +430,15 @@
   return true;
 }
 
+inline void ArmExidx::AdjustRegisters(int32_t offset) {
+  for (auto& entry : log_regs_) {
+    if (entry.first >= LOG_CFA_REG) {
+      break;
+    }
+    entry.second += offset;
+  }
+}
+
 inline bool ArmExidx::DecodePrefix_10_11_0010() {
   // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
   uint32_t result = 0;
@@ -387,8 +453,15 @@
     shift += 7;
   } while (byte & 0x80);
   result <<= 2;
-  if (log_) {
-    log(log_indent_, "vsp = vsp + %d", 0x204 + result);
+  if (log_type_ != ARM_LOG_NONE) {
+    int32_t cfa_offset = 0x204 + result;
+    if (log_type_ == ARM_LOG_FULL) {
+      log(log_indent_, "vsp = vsp + %d", cfa_offset);
+    } else {
+      log_cfa_offset_ += cfa_offset;
+    }
+    AdjustRegisters(cfa_offset);
+
     if (log_skip_execution_) {
       return true;
     }
@@ -404,14 +477,20 @@
     return false;
   }
 
-  if (log_) {
+  if (log_type_ != ARM_LOG_NONE) {
     uint8_t start_reg = byte >> 4;
-    std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
     uint8_t end_reg = start_reg + (byte & 0xf);
-    if (end_reg) {
-      msg += android::base::StringPrintf("-d%d", end_reg);
+
+    if (log_type_ == ARM_LOG_FULL) {
+      std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+      if (end_reg) {
+        msg += android::base::StringPrintf("-d%d", end_reg);
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      log(log_indent_, "Unsupported DX register display");
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
@@ -422,7 +501,7 @@
 
 inline bool ArmExidx::DecodePrefix_10_11_01nn() {
   // 101101nn: Spare
-  if (log_) {
+  if (log_type_ != ARM_LOG_NONE) {
     log(log_indent_, "Spare");
   }
   status_ = ARM_STATUS_SPARE;
@@ -433,13 +512,18 @@
   CHECK((byte & ~0x07) == 0xb8);
 
   // 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
-  if (log_) {
-    std::string msg = "pop {d8";
-    uint8_t last_reg = (byte & 0x7);
-    if (last_reg) {
-      msg += android::base::StringPrintf("-d%d", last_reg + 8);
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      uint8_t last_reg = (byte & 0x7);
+      std::string msg = "pop {d8";
+      if (last_reg) {
+        msg += android::base::StringPrintf("-d%d", last_reg + 8);
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      log(log_indent_, "Unsupported DX register display");
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
@@ -489,14 +573,19 @@
     }
 
     // 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
-    if (log_) {
-      uint8_t start_reg = byte >> 4;
-      std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
-      uint8_t end_reg = byte & 0xf;
-      if (end_reg) {
-        msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        uint8_t start_reg = byte >> 4;
+        std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
+        uint8_t end_reg = byte & 0xf;
+        if (end_reg) {
+          msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
+        }
+        log(log_indent_, "%s}", msg.c_str());
+      } else {
+        log(log_indent_, "Unsupported wRX register display");
       }
-      log(log_indent_, "%s}", msg.c_str());
+
       if (log_skip_execution_) {
         return true;
       }
@@ -510,32 +599,40 @@
 
     if (byte == 0) {
       // 11000111 00000000: Spare
-      if (log_) {
+      if (log_type_ != ARM_LOG_NONE) {
         log(log_indent_, "Spare");
       }
       status_ = ARM_STATUS_SPARE;
       return false;
     } else if ((byte >> 4) == 0) {
       // 11000111 0000iiii: Intel Wireless MMX pop wCGR registers {wCGR0,1,2,3}
-      if (log_) {
-        bool add_comma = false;
-        std::string msg = "pop {";
-        for (size_t i = 0; i < 4; i++) {
-          if (byte & (1 << i)) {
-            if (add_comma) {
-              msg += ", ";
+      if (log_type_ != ARM_LOG_NONE) {
+        if (log_type_ == ARM_LOG_FULL) {
+          bool add_comma = false;
+          std::string msg = "pop {";
+          for (size_t i = 0; i < 4; i++) {
+            if (byte & (1 << i)) {
+              if (add_comma) {
+                msg += ", ";
+              }
+              msg += android::base::StringPrintf("wCGR%zu", i);
+              add_comma = true;
             }
-            msg += android::base::StringPrintf("wCGR%zu", i);
-            add_comma = true;
           }
+          log(log_indent_, "%s}", msg.c_str());
+        } else {
+          log(log_indent_, "Unsupported wCGR register display");
         }
-        log(log_indent_, "%s}", msg.c_str());
+
+        if (log_skip_execution_) {
+          return true;
+        }
       }
       // Only update the cfa.
       cfa_ += __builtin_popcount(byte) * 4;
     } else {
       // 11000111 xxxxyyyy: Spare (xxxx != 0000)
-      if (log_) {
+      if (log_type_ != ARM_LOG_NONE) {
         log(log_indent_, "Spare");
       }
       status_ = ARM_STATUS_SPARE;
@@ -543,13 +640,18 @@
     }
   } else {
     // 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
-    if (log_) {
-      std::string msg = "pop {wR10";
-      uint8_t nnn = byte & 0x7;
-      if (nnn) {
-        msg += android::base::StringPrintf("-wR%d", 10 + nnn);
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        std::string msg = "pop {wR10";
+        uint8_t nnn = byte & 0x7;
+        if (nnn) {
+          msg += android::base::StringPrintf("-wR%d", 10 + nnn);
+        }
+        log(log_indent_, "%s}", msg.c_str());
+      } else {
+        log(log_indent_, "Unsupported wRX register display");
       }
-      log(log_indent_, "%s}", msg.c_str());
+
       if (log_skip_execution_) {
         return true;
       }
@@ -570,14 +672,19 @@
       return false;
     }
 
-    if (log_) {
-      uint8_t start_reg = byte >> 4;
-      std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
-      uint8_t end_reg = byte & 0xf;
-      if (end_reg) {
-        msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        uint8_t start_reg = byte >> 4;
+        std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
+        uint8_t end_reg = byte & 0xf;
+        if (end_reg) {
+          msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
+        }
+        log(log_indent_, "%s}", msg.c_str());
+      } else {
+        log(log_indent_, "Unsupported DX register display");
       }
-      log(log_indent_, "%s}", msg.c_str());
+
       if (log_skip_execution_) {
         return true;
       }
@@ -590,14 +697,19 @@
       return false;
     }
 
-    if (log_) {
-      uint8_t start_reg = byte >> 4;
-      std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
-      uint8_t end_reg = byte & 0xf;
-      if (end_reg) {
-        msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        uint8_t start_reg = byte >> 4;
+        std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+        uint8_t end_reg = byte & 0xf;
+        if (end_reg) {
+          msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
+        }
+        log(log_indent_, "%s}", msg.c_str());
+      } else {
+        log(log_indent_, "Unsupported DX register display");
       }
-      log(log_indent_, "%s}", msg.c_str());
+
       if (log_skip_execution_) {
         return true;
       }
@@ -606,7 +718,7 @@
     cfa_ += (byte & 0xf) * 8 + 8;
   } else {
     // 11001yyy: Spare (yyy != 000, 001)
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
@@ -619,13 +731,18 @@
   CHECK((byte & ~0x07) == 0xd0);
 
   // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
-  if (log_) {
-    std::string msg = "pop {d8";
-    uint8_t end_reg = byte & 0x7;
-    if (end_reg) {
-      msg += android::base::StringPrintf("-d%d", 8 + end_reg);
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      std::string msg = "pop {d8";
+      uint8_t end_reg = byte & 0x7;
+      if (end_reg) {
+        msg += android::base::StringPrintf("-d%d", 8 + end_reg);
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      log(log_indent_, "Unsupported DX register display");
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
@@ -646,7 +763,7 @@
     return DecodePrefix_11_010(byte);
   default:
     // 11xxxyyy: Spare (xxx != 000, 001, 010)
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
@@ -664,8 +781,15 @@
   switch (byte >> 6) {
   case 0:
     // 00xxxxxx: vsp = vsp + (xxxxxxx << 2) + 4
-    if (log_) {
-      log(log_indent_, "vsp = vsp + %d", ((byte & 0x3f) << 2) + 4);
+    if (log_type_ != ARM_LOG_NONE) {
+      int32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
+      if (log_type_ == ARM_LOG_FULL) {
+        log(log_indent_, "vsp = vsp + %d", cfa_offset);
+      } else {
+        log_cfa_offset_ += cfa_offset;
+      }
+      AdjustRegisters(cfa_offset);
+
       if (log_skip_execution_) {
         break;
       }
@@ -674,8 +798,15 @@
     break;
   case 1:
     // 01xxxxxx: vsp = vsp - (xxxxxxx << 2) + 4
-    if (log_) {
-      log(log_indent_, "vsp = vsp - %d", ((byte & 0x3f) << 2) + 4);
+    if (log_type_ != ARM_LOG_NONE) {
+      uint32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
+      if (log_type_ == ARM_LOG_FULL) {
+        log(log_indent_, "vsp = vsp - %d", cfa_offset);
+      } else {
+        log_cfa_offset_ -= cfa_offset;
+      }
+      AdjustRegisters(-cfa_offset);
+
       if (log_skip_execution_) {
         break;
       }
@@ -696,4 +827,36 @@
   return status_ == ARM_STATUS_FINISH;
 }
 
+void ArmExidx::LogByReg() {
+  if (log_type_ != ARM_LOG_BY_REG) {
+    return;
+  }
+
+  uint8_t cfa_reg;
+  if (log_regs_.count(LOG_CFA_REG) == 0) {
+    cfa_reg = 13;
+  } else {
+    cfa_reg = log_regs_[LOG_CFA_REG];
+  }
+
+  if (log_cfa_offset_ != 0) {
+    char sign = (log_cfa_offset_ > 0) ? '+' : '-';
+    log(log_indent_, "cfa = r%zu %c %d", cfa_reg, sign, abs(log_cfa_offset_));
+  } else {
+    log(log_indent_, "cfa = r%zu", cfa_reg);
+  }
+
+  for (const auto& entry : log_regs_) {
+    if (entry.first >= LOG_CFA_REG) {
+      break;
+    }
+    if (entry.second == 0) {
+      log(log_indent_, "r%zu = [cfa]", entry.first);
+    } else {
+      char sign = (entry.second > 0) ? '-' : '+';
+      log(log_indent_, "r%zu = [cfa %c %d]", entry.first, sign, abs(entry.second));
+    }
+  }
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
index 96756a0..d9fc371 100644
--- a/libunwindstack/ArmExidx.h
+++ b/libunwindstack/ArmExidx.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include <deque>
+#include <map>
 
 namespace unwindstack {
 
@@ -44,6 +45,12 @@
   ARM_OP_FINISH = 0xb0,
 };
 
+enum ArmLogType : uint8_t {
+  ARM_LOG_NONE,
+  ARM_LOG_FULL,
+  ARM_LOG_BY_REG,
+};
+
 class ArmExidx {
  public:
   ArmExidx(RegsArm* regs, Memory* elf_memory, Memory* process_memory)
@@ -52,6 +59,8 @@
 
   void LogRawData();
 
+  void LogByReg();
+
   bool ExtractEntryData(uint32_t entry_offset);
 
   bool Eval();
@@ -71,12 +80,13 @@
   bool pc_set() { return pc_set_; }
   void set_pc_set(bool pc_set) { pc_set_ = pc_set; }
 
-  void set_log(bool log) { log_ = log; }
+  void set_log(ArmLogType log_type) { log_type_ = log_type; }
   void set_log_skip_execution(bool skip_execution) { log_skip_execution_ = skip_execution; }
   void set_log_indent(uint8_t indent) { log_indent_ = indent; }
 
  private:
   bool GetByte(uint8_t* byte);
+  void AdjustRegisters(int32_t offset);
 
   bool DecodePrefix_10_00(uint8_t byte);
   bool DecodePrefix_10_01(uint8_t byte);
@@ -103,10 +113,12 @@
   Memory* elf_memory_;
   Memory* process_memory_;
 
-  bool log_ = false;
+  ArmLogType log_type_ = ARM_LOG_NONE;
   uint8_t log_indent_ = 0;
   bool log_skip_execution_ = false;
   bool pc_set_ = false;
+  int32_t log_cfa_offset_ = 0;
+  std::map<uint8_t, int32_t> log_regs_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 6ecedce..cd9ef61 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -264,8 +264,8 @@
 }
 
 template <typename AddressType>
-bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t load_bias,
-                                uint64_t start_offset, uint64_t end_offset) {
+bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t start_offset,
+                                uint64_t end_offset) {
   memory_->set_cur_offset(start_offset);
   uint64_t cfa_offset;
   uint64_t cur_pc = fde_->pc_start;
@@ -301,8 +301,8 @@
         break;
     }
     if (cur_pc != old_pc) {
-      log(indent, "");
-      log(indent, "PC 0x%" PRIx64, cur_pc + load_bias);
+      log(0, "");
+      log(indent, "PC 0x%" PRIx64, cur_pc);
     }
     old_pc = cur_pc;
   }
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index 16c66e2..c5ffb8e 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -71,8 +71,7 @@
   bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
                        dwarf_loc_regs_t* loc_regs);
 
-  bool Log(uint32_t indent, uint64_t pc, uint64_t load_bias, uint64_t start_offset,
-           uint64_t end_offset);
+  bool Log(uint32_t indent, uint64_t pc, uint64_t start_offset, uint64_t end_offset);
 
   const DwarfErrorData& last_error() { return last_error_; }
   DwarfErrorCode LastErrorCode() { return last_error_.code; }
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index 9a49013..fd6a457 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -22,12 +22,18 @@
 
 #include "Check.h"
 #include "DwarfEhFrameWithHdr.h"
+#include "DwarfEncoding.h"
 
 namespace unwindstack {
 
+static inline bool IsEncodingRelative(uint8_t encoding) {
+  encoding >>= 4;
+  return encoding > 0 && encoding <= DW_EH_PE_funcrel;
+}
+
 template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size) {
-  uint8_t data[4];
+bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
+  load_bias_ = load_bias;
 
   memory_.clear_func_offset();
   memory_.clear_text_offset();
@@ -35,6 +41,7 @@
   memory_.set_cur_offset(offset);
 
   // Read the first four bytes all at once.
+  uint8_t data[4];
   if (!memory_.ReadBytes(data, 4)) {
     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
     last_error_.address = memory_.cur_offset();
@@ -100,7 +107,7 @@
 
   memory_.set_data_offset(entries_data_offset_);
   memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
-  memory_.set_pc_offset(memory_.cur_offset());
+  memory_.set_pc_offset(0);
   uint64_t value;
   if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
       !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
@@ -109,6 +116,11 @@
     fde_info_.erase(index);
     return nullptr;
   }
+
+  // Relative encodings require adding in the load bias.
+  if (IsEncodingRelative(table_encoding_)) {
+    value += load_bias_;
+  }
   info->pc = value;
   return info;
 }
@@ -174,27 +186,27 @@
 
   memory_.set_data_offset(entries_data_offset_);
   memory_.set_cur_offset(cur_entries_offset_);
+  memory_.set_pc_offset(0);
   cur_entries_offset_ = 0;
 
   FdeInfo* prev_info = nullptr;
   for (size_t current = fde_info_.size();
        current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
-    memory_.set_pc_offset(memory_.cur_offset());
-    uint64_t value;
-    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
-      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-      last_error_.address = memory_.cur_offset();
-      return false;
-    }
-
     FdeInfo* info = &fde_info_[current];
-    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+    uint64_t value;
+    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
+        !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
       fde_info_.erase(current);
       last_error_.code = DWARF_ERROR_MEMORY_INVALID;
       last_error_.address = memory_.cur_offset();
       return false;
     }
-    info->pc = value + 4;
+
+    // Relative encodings require adding in the load bias.
+    if (IsEncodingRelative(table_encoding_)) {
+      value += load_bias_;
+    }
+    info->pc = value;
 
     if (pc < info->pc) {
       if (prev_info == nullptr) {
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index 3571166..d16dd10 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -38,6 +38,7 @@
   using DwarfSectionImpl<AddressType>::entries_offset_;
   using DwarfSectionImpl<AddressType>::entries_end_;
   using DwarfSectionImpl<AddressType>::last_error_;
+  using DwarfSectionImpl<AddressType>::load_bias_;
 
   struct FdeInfo {
     AddressType pc;
@@ -47,7 +48,7 @@
   DwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrame<AddressType>(memory) {}
   virtual ~DwarfEhFrameWithHdr() = default;
 
-  bool Init(uint64_t offset, uint64_t size) override;
+  bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
 
   bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
 
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 65eec65..eb83949 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -420,6 +420,7 @@
           last_error_.address = memory_.cur_offset();
           return false;
         }
+        memory_.set_pc_offset(pc_offset_);
         if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
           last_error_.code = DWARF_ERROR_MEMORY_INVALID;
           last_error_.address = memory_.cur_offset();
@@ -521,19 +522,19 @@
   }
   memory_.set_cur_offset(cur_offset);
 
-  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_start)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
+  // The load bias only applies to the start.
+  memory_.set_pc_offset(load_bias_);
+  bool valid = memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_start);
   fde->pc_start = AdjustPcFromFde(fde->pc_start);
 
-  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_end)) {
+  memory_.set_pc_offset(0);
+  if (!valid || !memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_end)) {
     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
     last_error_.address = memory_.cur_offset();
     return false;
   }
   fde->pc_end += fde->pc_start;
+
   if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
     // Augmentation Size
     uint64_t aug_length;
@@ -544,6 +545,7 @@
     }
     uint64_t cur_offset = memory_.cur_offset();
 
+    memory_.set_pc_offset(pc_offset_);
     if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
       last_error_.code = DWARF_ERROR_MEMORY_INVALID;
       last_error_.address = memory_.cur_offset();
@@ -582,17 +584,16 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, uint64_t load_bias,
-                                        const DwarfFde* fde) {
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) {
   DwarfCfa<AddressType> cfa(&memory_, fde);
 
   // Always print the cie information.
   const DwarfCie* cie = fde->cie;
-  if (!cfa.Log(indent, pc, load_bias, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
+  if (!cfa.Log(indent, pc, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
     last_error_ = cfa.last_error();
     return false;
   }
-  if (!cfa.Log(indent, pc, load_bias, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
+  if (!cfa.Log(indent, pc, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
     last_error_ = cfa.last_error();
     return false;
   }
@@ -600,15 +601,16 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size) {
+bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
+  load_bias_ = load_bias;
   entries_offset_ = offset;
   entries_end_ = offset + size;
 
   memory_.clear_func_offset();
   memory_.clear_text_offset();
-  memory_.set_data_offset(offset);
   memory_.set_cur_offset(offset);
-  memory_.set_pc_offset(offset);
+  memory_.set_data_offset(offset);
+  pc_offset_ = offset;
 
   return CreateSortedFdeList();
 }
@@ -717,6 +719,7 @@
         return false;
       }
       uint64_t value;
+      memory_.set_pc_offset(pc_offset_);
       if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
         last_error_.code = DWARF_ERROR_MEMORY_INVALID;
         last_error_.address = memory_.cur_offset();
@@ -737,15 +740,13 @@
   }
 
   uint64_t start;
-  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
-    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
-    last_error_.address = memory_.cur_offset();
-    return false;
-  }
+  memory_.set_pc_offset(load_bias_);
+  bool valid = memory_.template ReadEncodedValue<AddressType>(encoding, &start);
   start = AdjustPcFromFde(start);
 
   uint64_t length;
-  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
+  memory_.set_pc_offset(0);
+  if (!valid || !memory_.template ReadEncodedValue<AddressType>(encoding, &length)) {
     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
     last_error_.address = memory_.cur_offset();
     return false;
@@ -877,7 +878,7 @@
   while (first < last) {
     size_t current = (first + last) / 2;
     const FdeInfo* info = &fdes_[current];
-    if (pc >= info->start && pc <= info->end) {
+    if (pc >= info->start && pc < info->end) {
       *fde_offset = info->offset;
       return true;
     }
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 3762107..4723606 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -53,7 +53,7 @@
 
   valid_ = interface_->Init(&load_bias_);
   if (valid_) {
-    interface_->InitHeaders();
+    interface_->InitHeaders(load_bias_);
     if (init_gnu_debugdata) {
       InitGnuDebugdata();
     } else {
@@ -83,7 +83,7 @@
   // is in the uncompressed data.
   uint64_t load_bias;
   if (gnu->Init(&load_bias)) {
-    gnu->InitHeaders();
+    gnu->InitHeaders(load_bias);
     interface_->SetGnuDebugdataInterface(gnu);
   } else {
     // Free all of the memory associated with the gnu_debugdata section.
@@ -103,9 +103,9 @@
 
 bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
   std::lock_guard<std::mutex> guard(lock_);
-  return valid_ && (interface_->GetFunctionName(addr, load_bias_, name, func_offset) ||
-                    (gnu_debugdata_interface_ && gnu_debugdata_interface_->GetFunctionName(
-                                                     addr, load_bias_, name, func_offset)));
+  return valid_ && (interface_->GetFunctionName(addr, name, func_offset) ||
+                    (gnu_debugdata_interface_ &&
+                     gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
 }
 
 bool Elf::GetGlobalVariable(const std::string& name, uint64_t* memory_address) {
@@ -174,7 +174,7 @@
 
   // Lock during the step which can update information in the object.
   std::lock_guard<std::mutex> guard(lock_);
-  return interface_->Step(adjusted_rel_pc, load_bias_, regs, process_memory, finished);
+  return interface_->Step(adjusted_rel_pc, regs, process_memory, finished);
 }
 
 bool Elf::IsValidElf(Memory* memory) {
@@ -220,7 +220,6 @@
   if (!valid_ || pc < load_bias_) {
     return false;
   }
-  pc -= load_bias_;
 
   if (interface_->IsValidPc(pc)) {
     return true;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 4c05a1b..954a821 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -124,10 +124,10 @@
 }
 
 template <typename AddressType>
-void ElfInterface::InitHeadersWithTemplate() {
+void ElfInterface::InitHeadersWithTemplate(uint64_t load_bias) {
   if (eh_frame_hdr_offset_ != 0) {
     eh_frame_.reset(new DwarfEhFrameWithHdr<AddressType>(memory_));
-    if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_)) {
+    if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, load_bias)) {
       eh_frame_.reset(nullptr);
     }
   }
@@ -136,7 +136,7 @@
     // If there is an eh_frame section without an eh_frame_hdr section,
     // or using the frame hdr object failed to init.
     eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
-    if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) {
+    if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, load_bias)) {
       eh_frame_.reset(nullptr);
     }
   }
@@ -150,7 +150,7 @@
 
   if (debug_frame_offset_ != 0) {
     debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
-    if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_)) {
+    if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_, load_bias)) {
       debug_frame_.reset(nullptr);
       debug_frame_offset_ = 0;
       debug_frame_size_ = static_cast<uint64_t>(-1);
@@ -441,14 +441,14 @@
 }
 
 template <typename SymType>
-bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
+bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
                                                uint64_t* func_offset) {
   if (symbols_.empty()) {
     return false;
   }
 
   for (const auto symbol : symbols_) {
-    if (symbol->GetName<SymType>(addr, load_bias, memory_, name, func_offset)) {
+    if (symbol->GetName<SymType>(addr, memory_, name, func_offset)) {
       return true;
     }
   }
@@ -469,34 +469,25 @@
   return false;
 }
 
-bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                        bool* finished) {
+bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
   last_error_.code = ERROR_NONE;
   last_error_.address = 0;
 
-  // Adjust the load bias to get the real relative pc.
-  if (pc < load_bias) {
-    last_error_.code = ERROR_UNWIND_INFO;
-    return false;
-  }
-  uint64_t adjusted_pc = pc - load_bias;
-
   // Try the debug_frame first since it contains the most specific unwind
   // information.
   DwarfSection* debug_frame = debug_frame_.get();
-  if (debug_frame != nullptr && debug_frame->Step(adjusted_pc, regs, process_memory, finished)) {
+  if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory, finished)) {
     return true;
   }
 
   // Try the eh_frame next.
   DwarfSection* eh_frame = eh_frame_.get();
-  if (eh_frame != nullptr && eh_frame->Step(adjusted_pc, regs, process_memory, finished)) {
+  if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) {
     return true;
   }
 
-  // Finally try the gnu_debugdata interface, but always use a zero load bias.
   if (gnu_debugdata_interface_ != nullptr &&
-      gnu_debugdata_interface_->Step(pc, 0, regs, process_memory, finished)) {
+      gnu_debugdata_interface_->Step(pc, regs, process_memory, finished)) {
     return true;
   }
 
@@ -559,8 +550,8 @@
 }
 
 // Instantiate all of the needed template functions.
-template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
-template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
+template void ElfInterface::InitHeadersWithTemplate<uint32_t>(uint64_t);
+template void ElfInterface::InitHeadersWithTemplate<uint64_t>(uint64_t);
 
 template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(uint64_t*);
 template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(uint64_t*);
@@ -574,9 +565,9 @@
 template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
 template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
 
-template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, uint64_t, std::string*,
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
                                                                    uint64_t*);
-template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, uint64_t, std::string*,
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
                                                                    uint64_t*);
 
 template bool ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(const std::string&, uint64_t*);
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index f93baeb..9b61599 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -26,6 +26,14 @@
 
 namespace unwindstack {
 
+bool ElfInterfaceArm::Init(uint64_t* load_bias) {
+  if (!ElfInterface32::Init(load_bias)) {
+    return false;
+  }
+  load_bias_ = *load_bias;
+  return true;
+}
+
 bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) {
   if (start_offset_ == 0 || total_entries_ == 0) {
     last_error_.code = ERROR_UNWIND_INFO;
@@ -96,24 +104,22 @@
   return true;
 }
 
-bool ElfInterfaceArm::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                           bool* finished) {
+bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
   // 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, load_bias, regs, process_memory, finished) ||
-         StepExidx(pc, load_bias, regs, process_memory, finished);
+  return ElfInterface32::Step(pc, regs, process_memory, finished) ||
+         StepExidx(pc, regs, process_memory, finished);
 }
 
-bool ElfInterfaceArm::StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                                bool* finished) {
+bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
   // Adjust the load bias to get the real relative pc.
-  if (pc < load_bias) {
+  if (pc < load_bias_) {
     last_error_.code = ERROR_UNWIND_INFO;
     return false;
   }
-  pc -= load_bias;
+  pc -= load_bias_;
 
   RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
   uint64_t entry_offset;
@@ -167,13 +173,12 @@
   return return_value;
 }
 
-bool ElfInterfaceArm::GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                                      uint64_t* offset) {
+bool ElfInterfaceArm::GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) {
   // For ARM, thumb function symbols have bit 0 set, but the address passed
   // in here might not have this bit set and result in a failure to find
   // the thumb function names. Adjust the address and offset to account
   // for this possible case.
-  if (ElfInterface32::GetFunctionName(addr | 1, load_bias, name, offset)) {
+  if (ElfInterface32::GetFunctionName(addr | 1, name, offset)) {
     *offset &= ~1;
     return true;
   }
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index c1597ce..18efb6c 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -64,28 +64,30 @@
   iterator begin() { return iterator(this, 0); }
   iterator end() { return iterator(this, total_entries_); }
 
+  bool Init(uint64_t* load_bias) override;
+
   bool GetPrel31Addr(uint32_t offset, uint32_t* addr);
 
   bool FindEntry(uint32_t pc, uint64_t* entry_offset);
 
   bool HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) override;
 
-  bool Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-            bool* finished) override;
+  bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
 
-  bool StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                 bool* finished);
+  bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
 
-  bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                       uint64_t* offset) override;
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) override;
 
   uint64_t start_offset() { return start_offset_; }
 
   size_t total_entries() { return total_entries_; }
 
+  void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
+
  protected:
   uint64_t start_offset_ = 0;
   size_t total_entries_ = 0;
+  uint64_t load_bias_ = 0;
 
   std::unordered_map<size_t, uint32_t> addrs_;
 };
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index 25def40..14ebdbb 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -54,10 +54,7 @@
 }
 
 template <typename SymType>
-bool Symbols::GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
-                      uint64_t* func_offset) {
-  addr += load_bias;
-
+bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
   if (symbols_.size() != 0) {
     const Info* info = GetInfoFromCache(addr);
     if (info) {
@@ -81,9 +78,6 @@
     if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
       // Treat st_value as virtual address.
       uint64_t start_offset = entry.st_value;
-      if (entry.st_shndx != SHN_ABS) {
-        start_offset += load_bias;
-      }
       uint64_t end_offset = start_offset + entry.st_size;
 
       // Cache the value.
@@ -134,8 +128,8 @@
 }
 
 // Instantiate all of the needed template functions.
-template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
-template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf32_Sym>(uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf64_Sym>(uint64_t, Memory*, std::string*, uint64_t*);
 
 template bool Symbols::GetGlobal<Elf32_Sym>(Memory*, const std::string&, uint64_t*);
 template bool Symbols::GetGlobal<Elf64_Sym>(Memory*, const std::string&, uint64_t*);
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 7d239c1..7fcd067 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -44,8 +44,7 @@
   const Info* GetInfoFromCache(uint64_t addr);
 
   template <typename SymType>
-  bool GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
-               uint64_t* func_offset);
+  bool GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset);
 
   template <typename SymType>
   bool GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address);
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 209c54a..847f382 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -78,13 +78,13 @@
   DwarfErrorCode LastErrorCode() { return last_error_.code; }
   uint64_t LastErrorAddress() { return last_error_.address; }
 
-  virtual bool Init(uint64_t offset, uint64_t size) = 0;
+  virtual bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) = 0;
 
   virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
 
   virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
 
-  virtual bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) = 0;
+  virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) = 0;
 
   virtual const DwarfFde* GetFdeFromIndex(size_t index) = 0;
 
@@ -131,7 +131,7 @@
   DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
   virtual ~DwarfSectionImpl() = default;
 
-  bool Init(uint64_t offset, uint64_t size) override;
+  bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
 
   bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
 
@@ -150,7 +150,7 @@
 
   bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
 
-  bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) override;
+  bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
 
  protected:
   bool EvalExpression(const DwarfLocation& loc, Memory* regular_memory, AddressType* value,
@@ -162,6 +162,9 @@
 
   bool CreateSortedFdeList();
 
+  uint64_t load_bias_ = 0;
+  uint64_t pc_offset_ = 0;
+
   std::vector<FdeInfo> fdes_;
   uint64_t entries_offset_;
   uint64_t entries_end_;
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 3a221bc..4d25c40 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -54,17 +54,15 @@
 
   virtual bool Init(uint64_t* load_bias) = 0;
 
-  virtual void InitHeaders() = 0;
+  virtual void InitHeaders(uint64_t load_bias) = 0;
 
   virtual bool GetSoname(std::string* name) = 0;
 
-  virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                               uint64_t* offset) = 0;
+  virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
 
   virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
 
-  virtual bool Step(uint64_t rel_pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
-                    bool* finished);
+  virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
 
   virtual bool IsValidPc(uint64_t pc);
 
@@ -100,7 +98,7 @@
 
  protected:
   template <typename AddressType>
-  void InitHeadersWithTemplate();
+  void InitHeadersWithTemplate(uint64_t load_bias);
 
   template <typename EhdrType, typename PhdrType, typename ShdrType>
   bool ReadAllHeaders(uint64_t* load_bias);
@@ -115,8 +113,7 @@
   bool GetSonameWithTemplate(std::string* soname);
 
   template <typename SymType>
-  bool GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
-                                   uint64_t* func_offset);
+  bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
 
   template <typename SymType>
   bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
@@ -169,15 +166,16 @@
     return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(load_bias);
   }
 
-  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
+  void InitHeaders(uint64_t load_bias) override {
+    ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
+  }
 
   bool GetSoname(std::string* soname) override {
     return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
   }
 
-  bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                       uint64_t* func_offset) override {
-    return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, load_bias, name, func_offset);
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+    return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
   }
 
   bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
@@ -198,15 +196,16 @@
     return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(load_bias);
   }
 
-  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
+  void InitHeaders(uint64_t load_bias) override {
+    ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
+  }
 
   bool GetSoname(std::string* soname) override {
     return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
   }
 
-  bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
-                       uint64_t* func_offset) override {
-    return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, load_bias, name, func_offset);
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+    return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
   }
 
   bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 8d6d00d..5f3d1ea 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -36,8 +36,6 @@
 class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
  protected:
   void Init(Memory* process_memory = nullptr) {
-    TearDown();
-
     if (process_memory == nullptr) {
       process_memory = &process_memory_;
     }
@@ -50,8 +48,8 @@
     regs_arm_->set_sp(0);
 
     exidx_.reset(new ArmExidx(regs_arm_.get(), &elf_memory_, process_memory));
-    if (log_) {
-      exidx_->set_log(true);
+    if (log_ != ARM_LOG_NONE) {
+      exidx_->set_log(log_);
       exidx_->set_log_indent(0);
       exidx_->set_log_skip_execution(false);
     }
@@ -60,14 +58,20 @@
   }
 
   void SetUp() override {
-    if (GetParam() != "no_logging") {
-      log_ = false;
+    if (GetParam() == "no_logging") {
+      log_ = ARM_LOG_NONE;
+    } else if (GetParam() == "register_logging") {
+      log_ = ARM_LOG_BY_REG;
     } else {
-      log_ = true;
+      log_ = ARM_LOG_FULL;
     }
-    ResetLogs();
     elf_memory_.Clear();
     process_memory_.Clear();
+    ResetExidx();
+  }
+
+  void ResetExidx() {
+    ResetLogs();
     Init();
   }
 
@@ -77,7 +81,7 @@
 
   MemoryFake elf_memory_;
   MemoryFake process_memory_;
-  bool log_;
+  ArmLogType log_;
 };
 
 TEST_P(ArmExidxDecodeTest, vsp_incr) {
@@ -86,38 +90,59 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 4\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->clear();
   data_->push_back(0x01);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 8\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1000cU, exidx_->cfa());
+  ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->clear();
   data_->push_back(0x3f);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 256\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1010cU, exidx_->cfa());
+  ASSERT_EQ(0x10100U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, vsp_decr) {
@@ -126,38 +151,59 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 - 4\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0xfffcU, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->clear();
   data_->push_back(0x41);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 - 8\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0xfff4U, exidx_->cfa());
+  ASSERT_EQ(0xfff8U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->clear();
   data_->push_back(0x7f);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 - 256\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0xfef4U, exidx_->cfa());
+  ASSERT_EQ(0xff00U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, refuse_unwind) {
@@ -166,10 +212,14 @@
   data_->push_back(0x00);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
 }
@@ -182,29 +232,60 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_TRUE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r15}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r15}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 4\n"
+          "4 unwind r15 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
   ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0x8f);
   data_->push_back(0xff);
   for (size_t i = 0; i < 12; i++) {
-    process_memory_.SetData32(0x10004 + i * 4, i + 0x20);
+    process_memory_.SetData32(0x10000 + i * 4, i + 0x20);
   }
   exidx_->set_pc_set(false);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_TRUE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
-              GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
+                GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 48\n"
+          "4 unwind r4 = [cfa - 48]\n"
+          "4 unwind r5 = [cfa - 44]\n"
+          "4 unwind r6 = [cfa - 40]\n"
+          "4 unwind r7 = [cfa - 36]\n"
+          "4 unwind r8 = [cfa - 32]\n"
+          "4 unwind r9 = [cfa - 28]\n"
+          "4 unwind r10 = [cfa - 24]\n"
+          "4 unwind r11 = [cfa - 20]\n"
+          "4 unwind r12 = [cfa - 16]\n"
+          "4 unwind r13 = [cfa - 12]\n"
+          "4 unwind r14 = [cfa - 8]\n"
+          "4 unwind r15 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   // Popping r13 results in a modified cfa.
   ASSERT_EQ(0x29U, exidx_->cfa());
@@ -222,7 +303,7 @@
   ASSERT_EQ(0x2aU, (*exidx_->regs())[14]);
   ASSERT_EQ(0x2bU, (*exidx_->regs())[15]);
 
-  ResetLogs();
+  ResetExidx();
   exidx_->set_cfa(0x10034);
   data_->push_back(0x81);
   data_->push_back(0x28);
@@ -233,10 +314,22 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 12\n"
+          "4 unwind r7 = [cfa - 12]\n"
+          "4 unwind r9 = [cfa - 8]\n"
+          "4 unwind r12 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10040U, exidx_->cfa());
   ASSERT_EQ(0x11U, (*exidx_->regs())[7]);
@@ -255,34 +348,63 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r0\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(1U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
+  exidx_->set_cfa(0x100);
+  for (size_t i = 0; i < 15; i++) {
+    (*regs_arm_)[i] = i + 1;
+  }
   data_->push_back(0x93);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r3\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(4U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
+  exidx_->set_cfa(0x100);
+  for (size_t i = 0; i < 15; i++) {
+    (*regs_arm_)[i] = i + 1;
+  }
   data_->push_back(0x9e);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r14\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(15U, exidx_->cfa());
 }
@@ -292,22 +414,30 @@
   data_->push_back(0x9d);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
 
   // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0x9f);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
 }
@@ -319,53 +449,93 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 4\n"
+          "4 unwind r4 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
   ASSERT_EQ(0x14U, (*exidx_->regs())[4]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xa3);
-  process_memory_.SetData32(0x10004, 0x20);
-  process_memory_.SetData32(0x10008, 0x30);
-  process_memory_.SetData32(0x1000c, 0x40);
-  process_memory_.SetData32(0x10010, 0x50);
+  process_memory_.SetData32(0x10000, 0x20);
+  process_memory_.SetData32(0x10004, 0x30);
+  process_memory_.SetData32(0x10008, 0x40);
+  process_memory_.SetData32(0x1000c, 0x50);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 16\n"
+          "4 unwind r4 = [cfa - 16]\n"
+          "4 unwind r5 = [cfa - 12]\n"
+          "4 unwind r6 = [cfa - 8]\n"
+          "4 unwind r7 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10014U, exidx_->cfa());
+  ASSERT_EQ(0x10010U, exidx_->cfa());
   ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
   ASSERT_EQ(0x30U, (*exidx_->regs())[5]);
   ASSERT_EQ(0x40U, (*exidx_->regs())[6]);
   ASSERT_EQ(0x50U, (*exidx_->regs())[7]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xa7);
-  process_memory_.SetData32(0x10014, 0x41);
-  process_memory_.SetData32(0x10018, 0x51);
-  process_memory_.SetData32(0x1001c, 0x61);
-  process_memory_.SetData32(0x10020, 0x71);
-  process_memory_.SetData32(0x10024, 0x81);
-  process_memory_.SetData32(0x10028, 0x91);
-  process_memory_.SetData32(0x1002c, 0xa1);
-  process_memory_.SetData32(0x10030, 0xb1);
+  process_memory_.SetData32(0x10000, 0x41);
+  process_memory_.SetData32(0x10004, 0x51);
+  process_memory_.SetData32(0x10008, 0x61);
+  process_memory_.SetData32(0x1000c, 0x71);
+  process_memory_.SetData32(0x10010, 0x81);
+  process_memory_.SetData32(0x10014, 0x91);
+  process_memory_.SetData32(0x10018, 0xa1);
+  process_memory_.SetData32(0x1001c, 0xb1);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 32\n"
+          "4 unwind r4 = [cfa - 32]\n"
+          "4 unwind r5 = [cfa - 28]\n"
+          "4 unwind r6 = [cfa - 24]\n"
+          "4 unwind r7 = [cfa - 20]\n"
+          "4 unwind r8 = [cfa - 16]\n"
+          "4 unwind r9 = [cfa - 12]\n"
+          "4 unwind r10 = [cfa - 8]\n"
+          "4 unwind r11 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10034U, exidx_->cfa());
+  ASSERT_EQ(0x10020U, exidx_->cfa());
   ASSERT_EQ(0x41U, (*exidx_->regs())[4]);
   ASSERT_EQ(0x51U, (*exidx_->regs())[5]);
   ASSERT_EQ(0x61U, (*exidx_->regs())[6]);
@@ -384,57 +554,100 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 8\n"
+          "4 unwind r4 = [cfa - 8]\n"
+          "4 unwind r14 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
   ASSERT_EQ(0x12U, (*exidx_->regs())[4]);
   ASSERT_EQ(0x22U, (*exidx_->regs())[14]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xab);
-  process_memory_.SetData32(0x10008, 0x1);
-  process_memory_.SetData32(0x1000c, 0x2);
-  process_memory_.SetData32(0x10010, 0x3);
-  process_memory_.SetData32(0x10014, 0x4);
-  process_memory_.SetData32(0x10018, 0x5);
+  process_memory_.SetData32(0x10000, 0x1);
+  process_memory_.SetData32(0x10004, 0x2);
+  process_memory_.SetData32(0x10008, 0x3);
+  process_memory_.SetData32(0x1000c, 0x4);
+  process_memory_.SetData32(0x10010, 0x5);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 20\n"
+          "4 unwind r4 = [cfa - 20]\n"
+          "4 unwind r5 = [cfa - 16]\n"
+          "4 unwind r6 = [cfa - 12]\n"
+          "4 unwind r7 = [cfa - 8]\n"
+          "4 unwind r14 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1001cU, exidx_->cfa());
+  ASSERT_EQ(0x10014U, exidx_->cfa());
   ASSERT_EQ(0x1U, (*exidx_->regs())[4]);
   ASSERT_EQ(0x2U, (*exidx_->regs())[5]);
   ASSERT_EQ(0x3U, (*exidx_->regs())[6]);
   ASSERT_EQ(0x4U, (*exidx_->regs())[7]);
   ASSERT_EQ(0x5U, (*exidx_->regs())[14]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xaf);
-  process_memory_.SetData32(0x1001c, 0x1a);
-  process_memory_.SetData32(0x10020, 0x2a);
-  process_memory_.SetData32(0x10024, 0x3a);
-  process_memory_.SetData32(0x10028, 0x4a);
-  process_memory_.SetData32(0x1002c, 0x5a);
-  process_memory_.SetData32(0x10030, 0x6a);
-  process_memory_.SetData32(0x10034, 0x7a);
-  process_memory_.SetData32(0x10038, 0x8a);
-  process_memory_.SetData32(0x1003c, 0x9a);
+  process_memory_.SetData32(0x10000, 0x1a);
+  process_memory_.SetData32(0x10004, 0x2a);
+  process_memory_.SetData32(0x10008, 0x3a);
+  process_memory_.SetData32(0x1000c, 0x4a);
+  process_memory_.SetData32(0x10010, 0x5a);
+  process_memory_.SetData32(0x10014, 0x6a);
+  process_memory_.SetData32(0x10018, 0x7a);
+  process_memory_.SetData32(0x1001c, 0x8a);
+  process_memory_.SetData32(0x10020, 0x9a);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 36\n"
+          "4 unwind r4 = [cfa - 36]\n"
+          "4 unwind r5 = [cfa - 32]\n"
+          "4 unwind r6 = [cfa - 28]\n"
+          "4 unwind r7 = [cfa - 24]\n"
+          "4 unwind r8 = [cfa - 20]\n"
+          "4 unwind r9 = [cfa - 16]\n"
+          "4 unwind r10 = [cfa - 12]\n"
+          "4 unwind r11 = [cfa - 8]\n"
+          "4 unwind r14 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10040U, exidx_->cfa());
+  ASSERT_EQ(0x10024U, exidx_->cfa());
   ASSERT_EQ(0x1aU, (*exidx_->regs())[4]);
   ASSERT_EQ(0x2aU, (*exidx_->regs())[5]);
   ASSERT_EQ(0x3aU, (*exidx_->regs())[6]);
@@ -451,10 +664,17 @@
   data_->push_back(0xb0);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10000U, exidx_->cfa());
   ASSERT_EQ(ARM_STATUS_FINISH, exidx_->status());
@@ -466,10 +686,14 @@
   data_->push_back(0x00);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10000U, exidx_->cfa());
   ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -477,15 +701,19 @@
   // 10110001 xxxxyyyy: Spare (xxxx != 0000)
   for (size_t x = 1; x < 16; x++) {
     for (size_t y = 0; y < 16; y++) {
-      ResetLogs();
+      ResetExidx();
       data_->push_back(0xb1);
       data_->push_back((x << 4) | y);
       ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
       ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
-      if (log_) {
-        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
-      } else {
-        ASSERT_EQ("", GetFakeLogPrint());
+      switch (log_) {
+        case ARM_LOG_NONE:
+          ASSERT_EQ("", GetFakeLogPrint());
+          break;
+        case ARM_LOG_FULL:
+        case ARM_LOG_BY_REG:
+          ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+          break;
       }
       ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
       ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -494,29 +722,37 @@
 
   // 101101nn: Spare
   for (size_t n = 0; n < 4; n++) {
-    ResetLogs();
+    ResetExidx();
     data_->push_back(0xb4 | n);
     ASSERT_FALSE(exidx_->Decode()) << "n = " << n;
     ASSERT_EQ("", GetFakeLogBuf()) << "n = " << n;
-    if (log_) {
-      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
-    } else {
-      ASSERT_EQ("", GetFakeLogPrint());
+    switch (log_) {
+      case ARM_LOG_NONE:
+        ASSERT_EQ("", GetFakeLogPrint());
+        break;
+      case ARM_LOG_FULL:
+      case ARM_LOG_BY_REG:
+        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
+        break;
     }
     ASSERT_EQ(0x10000U, exidx_->cfa()) << "n = " << n;
     ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
   }
 
   // 11000111 00000000: Spare
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc7);
   data_->push_back(0x00);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10000U, exidx_->cfa());
   ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -524,15 +760,19 @@
   // 11000111 xxxxyyyy: Spare (xxxx != 0000)
   for (size_t x = 1; x < 16; x++) {
     for (size_t y = 0; y < 16; y++) {
-      ResetLogs();
+      ResetExidx();
       data_->push_back(0xc7);
       data_->push_back(0x10);
       ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
       ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
-      if (log_) {
-        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
-      } else {
-        ASSERT_EQ("", GetFakeLogPrint());
+      switch (log_) {
+        case ARM_LOG_NONE:
+          ASSERT_EQ("", GetFakeLogPrint());
+          break;
+        case ARM_LOG_FULL:
+        case ARM_LOG_BY_REG:
+          ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+          break;
       }
       ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
       ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -541,14 +781,18 @@
 
   // 11001yyy: Spare (yyy != 000, 001)
   for (size_t y = 2; y < 8; y++) {
-    ResetLogs();
+    ResetExidx();
     data_->push_back(0xc8 | y);
     ASSERT_FALSE(exidx_->Decode()) << "y = " << y;
     ASSERT_EQ("", GetFakeLogBuf()) << "y = " << y;
-    if (log_) {
-      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
-    } else {
-      ASSERT_EQ("", GetFakeLogPrint());
+    switch (log_) {
+      case ARM_LOG_NONE:
+        ASSERT_EQ("", GetFakeLogPrint());
+        break;
+      case ARM_LOG_FULL:
+      case ARM_LOG_BY_REG:
+        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
+        break;
     }
     ASSERT_EQ(0x10000U, exidx_->cfa()) << "y = " << y;
     ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -557,14 +801,18 @@
   // 11xxxyyy: Spare (xxx != 000, 001, 010)
   for (size_t x = 3; x < 8; x++) {
     for (size_t y = 0; y < 8; y++) {
-      ResetLogs();
+      ResetExidx();
       data_->push_back(0xc0 | (x << 3) | y);
       ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
       ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
-      if (log_) {
-        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
-      } else {
-        ASSERT_EQ("", GetFakeLogPrint());
+      switch (log_) {
+        case ARM_LOG_NONE:
+          ASSERT_EQ("", GetFakeLogPrint());
+          break;
+        case ARM_LOG_FULL:
+        case ARM_LOG_BY_REG:
+          ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+          break;
       }
       ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
       ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -580,47 +828,81 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 4\n"
+          "4 unwind r0 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
   ASSERT_EQ(0x45U, (*exidx_->regs())[0]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb1);
   data_->push_back(0x0a);
-  process_memory_.SetData32(0x10004, 0x23);
-  process_memory_.SetData32(0x10008, 0x24);
+  process_memory_.SetData32(0x10000, 0x23);
+  process_memory_.SetData32(0x10004, 0x24);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 8\n"
+          "4 unwind r1 = [cfa - 8]\n"
+          "4 unwind r3 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1000cU, exidx_->cfa());
+  ASSERT_EQ(0x10008U, exidx_->cfa());
   ASSERT_EQ(0x23U, (*exidx_->regs())[1]);
   ASSERT_EQ(0x24U, (*exidx_->regs())[3]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb1);
   data_->push_back(0x0f);
-  process_memory_.SetData32(0x1000c, 0x65);
-  process_memory_.SetData32(0x10010, 0x54);
-  process_memory_.SetData32(0x10014, 0x43);
-  process_memory_.SetData32(0x10018, 0x32);
+  process_memory_.SetData32(0x10000, 0x65);
+  process_memory_.SetData32(0x10004, 0x54);
+  process_memory_.SetData32(0x10008, 0x43);
+  process_memory_.SetData32(0x1000c, 0x32);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 16\n"
+          "4 unwind r0 = [cfa - 16]\n"
+          "4 unwind r1 = [cfa - 12]\n"
+          "4 unwind r2 = [cfa - 8]\n"
+          "4 unwind r3 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1001cU, exidx_->cfa());
+  ASSERT_EQ(0x10010U, exidx_->cfa());
   ASSERT_EQ(0x65U, (*exidx_->regs())[0]);
   ASSERT_EQ(0x54U, (*exidx_->regs())[1]);
   ASSERT_EQ(0x43U, (*exidx_->regs())[2]);
@@ -634,28 +916,42 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 1024\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10400U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb2);
   data_->push_back(0xff);
   data_->push_back(0x02);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 2048\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10c00U, exidx_->cfa());
+  ASSERT_EQ(0x10800U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb2);
   data_->push_back(0xff);
   data_->push_back(0x82);
@@ -663,12 +959,19 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 3147776\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x311400U, exidx_->cfa());
+  ASSERT_EQ(0x310800U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp_fstmfdx) {
@@ -678,25 +981,37 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x1000cU, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb3);
   data_->push_back(0x48);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10058U, exidx_->cfa());
+  ASSERT_EQ(0x1004cU, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp8_fstmfdx) {
@@ -705,36 +1020,54 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x1000cU, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xbb);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10030U, exidx_->cfa());
+  ASSERT_EQ(0x10024U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xbf);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10074U, exidx_->cfa());
+  ASSERT_EQ(0x10044U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_mmx_wr10) {
@@ -743,36 +1076,54 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc2);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10020U, exidx_->cfa());
+  ASSERT_EQ(0x10018U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc5);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10050U, exidx_->cfa());
+  ASSERT_EQ(0x10030U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_mmx_wr) {
@@ -782,38 +1133,56 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc6);
   data_->push_back(0x25);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10038U, exidx_->cfa());
+  ASSERT_EQ(0x10030U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc6);
   data_->push_back(0xff);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x100b8U, exidx_->cfa());
+  ASSERT_EQ(0x10080U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_mmx_wcgr) {
@@ -823,38 +1192,56 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc7);
   data_->push_back(0x0a);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1000cU, exidx_->cfa());
+  ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc7);
   data_->push_back(0x0f);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1001cU, exidx_->cfa());
+  ASSERT_EQ(0x10010U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp16_vpush) {
@@ -864,38 +1251,56 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc8);
   data_->push_back(0x14);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10030U, exidx_->cfa());
+  ASSERT_EQ(0x10028U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc8);
   data_->push_back(0xff);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x100b0U, exidx_->cfa());
+  ASSERT_EQ(0x10080U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp_vpush) {
@@ -905,38 +1310,56 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc9);
   data_->push_back(0x23);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10028U, exidx_->cfa());
+  ASSERT_EQ(0x10020U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc9);
   data_->push_back(0xff);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x100a8U, exidx_->cfa());
+  ASSERT_EQ(0x10080U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp8_vpush) {
@@ -945,36 +1368,54 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xd2);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10020U, exidx_->cfa());
+  ASSERT_EQ(0x10018U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xd7);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10060U, exidx_->cfa());
+  ASSERT_EQ(0x10040U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, expect_truncated) {
@@ -1047,32 +1488,147 @@
 TEST_P(ArmExidxDecodeTest, eval_multiple_decodes) {
   // vsp = vsp + 4
   data_->push_back(0x00);
-  // vsp = vsp + 8
+  // vsp = vsp + 12
   data_->push_back(0x02);
   // Finish
   data_->push_back(0xb0);
 
   ASSERT_TRUE(exidx_->Eval());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 4\n"
-              "4 unwind vsp = vsp + 12\n"
-              "4 unwind finish\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind vsp = vsp + 4\n"
+          "4 unwind vsp = vsp + 12\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 16\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10010U, exidx_->cfa());
   ASSERT_FALSE(exidx_->pc_set());
 }
 
+TEST_P(ArmExidxDecodeTest, eval_vsp_add_after_pop) {
+  // Pop {r15}
+  data_->push_back(0x88);
+  data_->push_back(0x00);
+  // vsp = vsp + 12
+  data_->push_back(0x02);
+  // Finish
+  data_->push_back(0xb0);
+  process_memory_.SetData32(0x10000, 0x10);
+
+  ASSERT_TRUE(exidx_->Eval());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind pop {r15}\n"
+          "4 unwind vsp = vsp + 12\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 16\n"
+          "4 unwind r15 = [cfa - 16]\n",
+          GetFakeLogPrint());
+      break;
+  }
+  ASSERT_EQ(0x10010U, exidx_->cfa());
+  ASSERT_TRUE(exidx_->pc_set());
+  ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+TEST_P(ArmExidxDecodeTest, eval_vsp_add_large_after_pop) {
+  // Pop {r15}
+  data_->push_back(0x88);
+  data_->push_back(0x00);
+  // vsp = vsp + 1024
+  data_->push_back(0xb2);
+  data_->push_back(0x7f);
+  // Finish
+  data_->push_back(0xb0);
+  process_memory_.SetData32(0x10000, 0x10);
+
+  ASSERT_TRUE(exidx_->Eval());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind pop {r15}\n"
+          "4 unwind vsp = vsp + 1024\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 1028\n"
+          "4 unwind r15 = [cfa - 1028]\n",
+          GetFakeLogPrint());
+      break;
+  }
+  ASSERT_EQ(0x10404U, exidx_->cfa());
+  ASSERT_TRUE(exidx_->pc_set());
+  ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+TEST_P(ArmExidxDecodeTest, eval_vsp_sub_after_pop) {
+  // Pop {r15}
+  data_->push_back(0x88);
+  data_->push_back(0x00);
+  // vsp = vsp - 4
+  data_->push_back(0x41);
+  // Finish
+  data_->push_back(0xb0);
+  process_memory_.SetData32(0x10000, 0x10);
+
+  ASSERT_TRUE(exidx_->Eval());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind pop {r15}\n"
+          "4 unwind vsp = vsp - 8\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 - 4\n"
+          "4 unwind r15 = [cfa + 4]\n",
+          GetFakeLogPrint());
+      break;
+  }
+  ASSERT_EQ(0xfffcU, exidx_->cfa());
+  ASSERT_TRUE(exidx_->pc_set());
+  ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
 TEST_P(ArmExidxDecodeTest, eval_pc_set) {
   // vsp = vsp + 4
   data_->push_back(0x00);
-  // vsp = vsp + 8
+  // vsp = vsp + 12
   data_->push_back(0x02);
   // Pop {r15}
   data_->push_back(0x88);
   data_->push_back(0x00);
-  // vsp = vsp + 8
+  // vsp = vsp + 12
   data_->push_back(0x02);
   // Finish
   data_->push_back(0xb0);
@@ -1080,20 +1636,33 @@
   process_memory_.SetData32(0x10010, 0x10);
 
   ASSERT_TRUE(exidx_->Eval());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 4\n"
-              "4 unwind vsp = vsp + 12\n"
-              "4 unwind pop {r15}\n"
-              "4 unwind vsp = vsp + 12\n"
-              "4 unwind finish\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind vsp = vsp + 4\n"
+          "4 unwind vsp = vsp + 12\n"
+          "4 unwind pop {r15}\n"
+          "4 unwind vsp = vsp + 12\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 32\n"
+          "4 unwind r15 = [cfa - 16]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10020U, exidx_->cfa());
   ASSERT_TRUE(exidx_->pc_set());
   ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
 }
 
-INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging"));
+INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest,
+                        ::testing::Values("logging", "register_logging", "no_logging"));
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
index 8d0f0e5..79c799c 100644
--- a/libunwindstack/tests/ArmExidxExtractTest.cpp
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -301,7 +301,7 @@
   elf_memory_.SetData32(0x1000, 0x7fff2340);
   elf_memory_.SetData32(0x1004, 1);
 
-  exidx_->set_log(true);
+  exidx_->set_log(ARM_LOG_FULL);
   exidx_->set_log_indent(0);
   exidx_->set_log_skip_execution(false);
 
@@ -316,7 +316,7 @@
   elf_memory_.SetData32(0x4000, 0x7ffa3000);
   elf_memory_.SetData32(0x4004, 0x80a8b0b0);
 
-  exidx_->set_log(true);
+  exidx_->set_log(ARM_LOG_FULL);
   exidx_->set_log_indent(0);
   exidx_->set_log_skip_execution(false);
 
@@ -330,7 +330,7 @@
   elf_memory_.SetData32(0x6234, 0x2);
   elf_memory_.SetData32(0x6238, 0x00112233);
 
-  exidx_->set_log(true);
+  exidx_->set_log(ARM_LOG_FULL);
   exidx_->set_log_indent(0);
   exidx_->set_log_skip_execution(false);
 
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index b17ca33..bb2e8f0 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -79,7 +79,7 @@
     this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
 
     ResetLogs();
-    ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+    ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
     std::string expected = "4 unwind Illegal\n";
     expected += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", i);
     ASSERT_EQ(expected, GetFakeLogPrint());
@@ -90,7 +90,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_nop) {
   this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
   std::string expected =
       "4 unwind DW_CFA_nop\n"
       "4 unwind Raw Data: 0x00\n";
@@ -101,7 +101,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_offset) {
   this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2002));
   std::string expected =
       "4 unwind DW_CFA_offset register(3) 4\n"
       "4 unwind Raw Data: 0x83 0x04\n";
@@ -111,7 +111,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2100, 0x2103));
   expected =
       "4 unwind DW_CFA_offset register(3) 132\n"
       "4 unwind Raw Data: 0x83 0x84 0x01\n";
@@ -122,7 +122,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended) {
   this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
   std::string expected =
       "4 unwind DW_CFA_offset_extended register(3) 2\n"
       "4 unwind Raw Data: 0x05 0x03 0x02\n";
@@ -132,7 +132,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
   expected =
       "4 unwind DW_CFA_offset_extended register(129) 2306\n"
       "4 unwind Raw Data: 0x05 0x81 0x01 0x82 0x12\n";
@@ -143,7 +143,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended_sf) {
   this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
   std::string expected =
       "4 unwind DW_CFA_offset_extended_sf register(5) 16\n"
       "4 unwind Raw Data: 0x11 0x05 0x10\n";
@@ -154,7 +154,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
   expected =
       "4 unwind DW_CFA_offset_extended_sf register(134) -1\n"
       "4 unwind Raw Data: 0x11 0x86 0x01 0xff 0x7f\n";
@@ -165,7 +165,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_restore) {
   this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
   std::string expected =
       "4 unwind DW_CFA_restore register(2)\n"
       "4 unwind Raw Data: 0xc2\n";
@@ -175,7 +175,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3003));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x3000, 0x3003));
   expected =
       "4 unwind DW_CFA_offset register(2) 4\n"
       "4 unwind Raw Data: 0x82 0x04\n"
@@ -188,7 +188,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_restore_extended) {
   this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4000, 0x4002));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4000, 0x4002));
   std::string expected =
       "4 unwind DW_CFA_restore_extended register(8)\n"
       "4 unwind Raw Data: 0x06 0x08\n";
@@ -198,7 +198,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5007));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x5000, 0x5007));
   expected =
       "4 unwind DW_CFA_offset_extended register(258) 4\n"
       "4 unwind Raw Data: 0x05 0x82 0x02 0x04\n"
@@ -228,7 +228,7 @@
   this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
   ResetLogs();
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam)));
   std::string expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
   expected += "4 unwind " + raw_data + "\n";
   expected += "4 unwind \n";
@@ -240,7 +240,7 @@
   ResetLogs();
   this->fde_.pc_start = address + 0x10;
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam)));
   expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
   expected += "4 unwind " + raw_data + "\n";
   expected += "4 unwind \n";
@@ -252,7 +252,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc) {
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x44});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x201));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x201));
   std::string expected =
       "4 unwind DW_CFA_advance_loc 4\n"
       "4 unwind Raw Data: 0x44\n"
@@ -260,22 +260,12 @@
       "4 unwind PC 0x2010\n";
   ASSERT_EQ(expected, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
-
-  ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x200, 0x201));
-  expected =
-      "4 unwind DW_CFA_advance_loc 4\n"
-      "4 unwind Raw Data: 0x44\n"
-      "4 unwind \n"
-      "4 unwind PC 0x2110\n";
-  ASSERT_EQ(expected, GetFakeLogPrint());
-  ASSERT_EQ("", GetFakeLogBuf());
 }
 
 TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc1) {
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x202));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x202));
   std::string expected =
       "4 unwind DW_CFA_advance_loc1 4\n"
       "4 unwind Raw Data: 0x02 0x04\n"
@@ -283,22 +273,12 @@
       "4 unwind PC 0x2004\n";
   ASSERT_EQ(expected, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
-
-  ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x10, 0x200, 0x202));
-  expected =
-      "4 unwind DW_CFA_advance_loc1 4\n"
-      "4 unwind Raw Data: 0x02 0x04\n"
-      "4 unwind \n"
-      "4 unwind PC 0x2014\n";
-  ASSERT_EQ(expected, GetFakeLogPrint());
-  ASSERT_EQ("", GetFakeLogBuf());
 }
 
 TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc2) {
   this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x600, 0x603));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x600, 0x603));
   std::string expected =
       "4 unwind DW_CFA_advance_loc2 772\n"
       "4 unwind Raw Data: 0x03 0x04 0x03\n"
@@ -306,22 +286,12 @@
       "4 unwind PC 0x2304\n";
   ASSERT_EQ(expected, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
-
-  ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1000, 0x600, 0x603));
-  expected =
-      "4 unwind DW_CFA_advance_loc2 772\n"
-      "4 unwind Raw Data: 0x03 0x04 0x03\n"
-      "4 unwind \n"
-      "4 unwind PC 0x3304\n";
-  ASSERT_EQ(expected, GetFakeLogPrint());
-  ASSERT_EQ("", GetFakeLogBuf());
 }
 
 TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc4) {
   this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x505));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x505));
   std::string expected =
       "4 unwind DW_CFA_advance_loc4 16909060\n"
       "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
@@ -329,22 +299,12 @@
       "4 unwind PC 0x1022304\n";
   ASSERT_EQ(expected, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
-
-  ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x500, 0x505));
-  expected =
-      "4 unwind DW_CFA_advance_loc4 16909060\n"
-      "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
-      "4 unwind \n"
-      "4 unwind PC 0x1024304\n";
-  ASSERT_EQ(expected, GetFakeLogPrint());
-  ASSERT_EQ("", GetFakeLogBuf());
 }
 
 TYPED_TEST_P(DwarfCfaLogTest, cfa_undefined) {
   this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa02));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xa02));
   std::string expected =
       "4 unwind DW_CFA_undefined register(9)\n"
       "4 unwind Raw Data: 0x07 0x09\n";
@@ -355,7 +315,7 @@
   dwarf_loc_regs_t cie_loc_regs;
   this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1a00, 0x1a03));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1a00, 0x1a03));
   expected =
       "4 unwind DW_CFA_undefined register(129)\n"
       "4 unwind Raw Data: 0x07 0x81 0x01\n";
@@ -366,7 +326,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_same) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
   std::string expected =
       "4 unwind DW_CFA_same_value register(127)\n"
       "4 unwind Raw Data: 0x08 0x7f\n";
@@ -376,7 +336,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2100, 0x2103));
   expected =
       "4 unwind DW_CFA_same_value register(255)\n"
       "4 unwind Raw Data: 0x08 0xff 0x01\n";
@@ -387,7 +347,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_register) {
   this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x303));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x303));
   std::string expected =
       "4 unwind DW_CFA_register register(2) register(1)\n"
       "4 unwind Raw Data: 0x09 0x02 0x01\n";
@@ -397,7 +357,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4305));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4300, 0x4305));
   expected =
       "4 unwind DW_CFA_register register(255) register(511)\n"
       "4 unwind Raw Data: 0x09 0xff 0x01 0xff 0x03\n";
@@ -408,7 +368,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_state) {
   this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x301));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x301));
 
   std::string expected =
       "4 unwind DW_CFA_remember_state\n"
@@ -419,7 +379,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4301));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4300, 0x4301));
 
   expected =
       "4 unwind DW_CFA_restore_state\n"
@@ -431,7 +391,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_state_cfa_offset_restore) {
   this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3004));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x3000, 0x3004));
 
   std::string expected =
       "4 unwind DW_CFA_remember_state\n"
@@ -447,7 +407,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa register(127) 116\n"
@@ -458,7 +418,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x205));
 
   expected =
       "4 unwind DW_CFA_def_cfa register(383) 628\n"
@@ -470,7 +430,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_sf) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_sf register(48) 37\n"
@@ -482,7 +442,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x205));
 
   expected =
       "4 unwind DW_CFA_def_cfa_sf register(163) -6\n"
@@ -494,7 +454,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_register) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_register register(114)\n"
@@ -505,7 +465,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
 
   expected =
       "4 unwind DW_CFA_def_cfa_register register(4217)\n"
@@ -517,7 +477,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_offset 89\n"
@@ -526,7 +486,7 @@
   ASSERT_EQ("", GetFakeLogBuf());
 
   ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   expected =
       "4 unwind DW_CFA_def_cfa_offset 89\n"
@@ -537,7 +497,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
 
   expected =
       "4 unwind DW_CFA_def_cfa_offset 1364\n"
@@ -549,7 +509,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset_sf) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
@@ -558,7 +518,7 @@
   ASSERT_EQ("", GetFakeLogBuf());
 
   ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   expected =
       "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
@@ -570,7 +530,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
 
   expected =
       "4 unwind DW_CFA_def_cfa_offset_sf -10\n"
@@ -582,7 +542,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_expression) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x04, 0x05});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x106));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x106));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_expression 4\n"
@@ -614,7 +574,7 @@
   }
   expected += '\n';
   this->memory_.SetMemory(0x200, ops);
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x284));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x284));
 
   expected = "4 unwind DW_CFA_def_cfa_expression 129\n" + expected;
   ASSERT_EQ(expected + op_string, GetFakeLogPrint());
@@ -624,7 +584,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_expression) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0xc0, 0xc1});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x105));
 
   std::string expected =
       "4 unwind DW_CFA_expression register(4) 2\n"
@@ -652,7 +612,7 @@
   expected = "4 unwind DW_CFA_expression register(255) 130\n" + expected + "\n";
 
   this->memory_.SetMemory(0x200, ops);
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x287));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x287));
 
   ASSERT_EQ(expected + op_string, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
@@ -661,7 +621,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
 
   std::string expected =
       "4 unwind DW_CFA_val_offset register(69) 84\n"
@@ -672,7 +632,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x400, 0x405));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x400, 0x405));
 
   expected =
       "4 unwind DW_CFA_val_offset register(290) 692\n"
@@ -684,7 +644,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset_sf) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
 
   std::string expected =
       "4 unwind DW_CFA_val_offset_sf register(86) 18\n"
@@ -696,7 +656,7 @@
   ResetLogs();
   this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa05));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xa05));
 
   expected =
       "4 unwind DW_CFA_val_offset_sf register(255) -64\n"
@@ -708,7 +668,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_val_expression) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0xb0, 0xb1});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x105));
 
   std::string expected =
       "4 unwind DW_CFA_val_expression register(5) 2\n"
@@ -737,7 +697,7 @@
 
   this->memory_.SetMemory(0xa00, ops);
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xaad));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xaad));
 
   ASSERT_EQ(expected + op_string, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
@@ -746,7 +706,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_args_size) {
   this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2002));
 
   std::string expected =
       "4 unwind DW_CFA_GNU_args_size 4\n"
@@ -757,7 +717,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5004));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x5000, 0x5004));
 
   expected =
       "4 unwind DW_CFA_GNU_args_size 65572\n"
@@ -769,7 +729,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_negative_offset_extended) {
   this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
 
   std::string expected =
       "4 unwind DW_CFA_GNU_negative_offset_extended register(8) 16\n"
@@ -780,7 +740,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
 
   expected =
       "4 unwind DW_CFA_GNU_negative_offset_extended register(257) 255\n"
@@ -792,7 +752,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_register_override) {
   this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x306));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x306));
 
   std::string expected =
       "4 unwind DW_CFA_register register(2) register(1)\n"
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index c28a41e..3a52044 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -103,7 +103,7 @@
   this->memory_.SetData32(0x5508, 0x4500);
   this->memory_.SetData32(0x550c, 0x500);
 
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
   ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
 
   typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -142,7 +142,7 @@
   this->memory_.SetData32(0x5108, 0x1500);
   this->memory_.SetData32(0x510c, 0x200);
 
-  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600, 0));
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->LastErrorCode());
 }
 
@@ -181,7 +181,7 @@
   this->memory_.SetData32(0x5508, 0x4500);
   this->memory_.SetData32(0x550c, 0x500);
 
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
   ASSERT_EQ(2U, this->debug_frame_->TestGetFdeCount());
 }
 
@@ -226,7 +226,7 @@
   this->memory_.SetData64(0x5514, 0x4500);
   this->memory_.SetData64(0x551c, 0x500);
 
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
   ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
 
   typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -267,7 +267,7 @@
   this->memory_.SetData64(0x5114, 0x1500);
   this->memory_.SetData64(0x511c, 0x200);
 
-  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600, 0));
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->LastErrorCode());
 }
 
@@ -312,10 +312,48 @@
   this->memory_.SetData64(0x5514, 0x4500);
   this->memory_.SetData64(0x551c, 0x500);
 
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
   ASSERT_EQ(2U, this->debug_frame_->TestGetFdeCount());
 }
 
+TYPED_TEST_P(DwarfDebugFrameTest, Init_non_zero_load_bias) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 1);
+  this->memory_.SetData8(0x5009, 'z');
+  this->memory_.SetData8(0x500a, 'R');
+  this->memory_.SetData8(0x500b, '\0');
+  this->memory_.SetData8(0x500c, 0);
+  this->memory_.SetData8(0x500d, 0);
+  this->memory_.SetData8(0x500e, 0);
+  this->memory_.SetData8(0x500f, 0);
+  this->memory_.SetData8(0x5010, 0x1b);
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0);
+  this->memory_.SetData32(0x5108, 0x1500);
+  this->memory_.SetData32(0x510c, 0x200);
+  this->memory_.SetData8(0x5110, 0);
+  this->memory_.SetData8(0x5111, 0);
+
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0x1000));
+  ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+
+  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+  this->debug_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x2500U, info.start);
+  EXPECT_EQ(0x2700U, info.end);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x2504);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+  EXPECT_EQ(0x2700U, fde->pc_end);
+}
+
 TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) {
   // CIE 32 information.
   this->memory_.SetData32(0x5000, 0xfc);
@@ -340,7 +378,7 @@
   this->memory_.SetData16(0x5108, 0x1500);
   this->memory_.SetData16(0x510a, 0x200);
 
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0));
   ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
 
   typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -383,7 +421,7 @@
   this->memory_.SetData16(0x5108, 0x1500);
   this->memory_.SetData16(0x510a, 0x200);
 
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0));
   ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
 
   typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -538,8 +576,8 @@
 REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie,
                            Init32_do_not_fail_on_bad_next_entry, Init64,
                            Init64_do_not_fail_on_bad_next_entry, Init64_fde_not_following_cie,
-                           Init_version1, Init_version4, GetFdeOffsetFromPc, GetCieFde32,
-                           GetCieFde64);
+                           Init_non_zero_load_bias, Init_version1, Init_version4,
+                           GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index a73db65..e8d53e6 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -103,7 +103,7 @@
   this->memory_.SetData32(0x5508, 0x4500);
   this->memory_.SetData32(0x550c, 0x500);
 
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
+  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600, 0));
   ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
 
   typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -142,7 +142,7 @@
   this->memory_.SetData32(0x5108, 0x1500);
   this->memory_.SetData32(0x510c, 0x200);
 
-  ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
+  ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600, 0));
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
 }
 
@@ -187,7 +187,7 @@
   this->memory_.SetData64(0x5514, 0x4500);
   this->memory_.SetData64(0x551c, 0x500);
 
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
+  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600, 0));
   ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
 
   typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -228,10 +228,48 @@
   this->memory_.SetData64(0x5114, 0x1500);
   this->memory_.SetData64(0x511c, 0x200);
 
-  ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
+  ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600, 0));
   ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
 }
 
+TYPED_TEST_P(DwarfEhFrameTest, Init_non_zero_load_bias) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0);
+  this->memory_.SetData8(0x5008, 1);
+  this->memory_.SetData8(0x5009, 'z');
+  this->memory_.SetData8(0x500a, 'R');
+  this->memory_.SetData8(0x500b, '\0');
+  this->memory_.SetData8(0x500c, 0);
+  this->memory_.SetData8(0x500d, 0);
+  this->memory_.SetData8(0x500e, 0);
+  this->memory_.SetData8(0x500f, 0);
+  this->memory_.SetData8(0x5010, 0x1b);
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0x104);
+  this->memory_.SetData32(0x5108, 0x1500);
+  this->memory_.SetData32(0x510c, 0x200);
+  this->memory_.SetData8(0x5110, 0);
+  this->memory_.SetData8(0x5111, 0);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200, 0x2000));
+  ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
+
+  typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+  this->eh_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x8608U, info.start);
+  EXPECT_EQ(0x8808U, info.end);
+
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x8700);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x8608U, fde->pc_start);
+  EXPECT_EQ(0x8808U, fde->pc_end);
+}
+
 TYPED_TEST_P(DwarfEhFrameTest, Init_version1) {
   // CIE 32 information.
   this->memory_.SetData32(0x5000, 0xfc);
@@ -256,7 +294,7 @@
   this->memory_.SetData16(0x5108, 0x1500);
   this->memory_.SetData16(0x510a, 0x200);
 
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
+  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200, 0));
   ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
 
   typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -299,7 +337,7 @@
   this->memory_.SetData16(0x5108, 0x1500);
   this->memory_.SetData16(0x510a, 0x200);
 
-  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
+  ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200, 0));
   ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
 
   typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -450,8 +488,8 @@
 }
 
 REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init32, Init32_fde_not_following_cie, Init64,
-                           Init64_fde_not_following_cie, Init_version1, Init_version4,
-                           GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+                           Init64_fde_not_following_cie, Init_non_zero_load_bias, Init_version1,
+                           Init_version4, GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 4240419..19c7b98 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -83,7 +83,7 @@
   this->memory_.SetData16(0x1004, 0x500);
   this->memory_.SetData32(0x1006, 126);
 
-  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
   EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
   EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
   EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
@@ -97,19 +97,66 @@
 
   // Verify a zero fde count fails to init.
   this->memory_.SetData32(0x1006, 0);
-  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
   ASSERT_EQ(DWARF_ERROR_NO_FDES, this->eh_frame_->LastErrorCode());
 
   // Verify an unexpected version will cause a fail.
   this->memory_.SetData32(0x1006, 126);
   this->memory_.SetData8(0x1000, 0);
-  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
   ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
   this->memory_.SetData8(0x1000, 2);
-  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
   ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
 }
 
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, Init_non_zero_load_bias) {
+  this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+                                                       DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+  this->memory_.SetData16(0x1004, 0x500);
+  this->memory_.SetData32(0x1006, 1);
+  this->memory_.SetData32(0x100a, 0x2500);
+  this->memory_.SetData32(0x100e, 0x1400);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x1300, 0xfc);
+  this->memory_.SetData32(0x1304, 0);
+  this->memory_.SetData8(0x1308, 1);
+  this->memory_.SetData8(0x1309, 'z');
+  this->memory_.SetData8(0x130a, 'R');
+  this->memory_.SetData8(0x130b, '\0');
+  this->memory_.SetData8(0x130c, 0);
+  this->memory_.SetData8(0x130d, 0);
+  this->memory_.SetData8(0x130e, 0);
+  this->memory_.SetData8(0x130f, 0);
+  this->memory_.SetData8(0x1310, 0x1b);
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x1400, 0xfc);
+  this->memory_.SetData32(0x1404, 0x104);
+  this->memory_.SetData32(0x1408, 0x10f8);
+  this->memory_.SetData32(0x140c, 0x200);
+  this->memory_.SetData8(0x1410, 0);
+  this->memory_.SetData8(0x1411, 0);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0x2000));
+  EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+  EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
+  EXPECT_EQ(0x1b, this->eh_frame_->TestGetTableEncoding());
+  EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+  EXPECT_EQ(1U, this->eh_frame_->TestGetFdeCount());
+  EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
+  EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
+  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+  EXPECT_EQ(0x4700U, fde->pc_end);
+}
+
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_expect_cache_fail) {
   this->eh_frame_->TestSetTableEntrySize(0x10);
   this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
@@ -123,6 +170,7 @@
   EXPECT_EQ(0x1000U, this->eh_frame_->LastErrorAddress());
 }
 
+// We are assuming that pc rel, is really relative to the load_bias.
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_pcrel) {
   this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
   this->eh_frame_->TestSetEntriesOffset(0x1000);
@@ -134,8 +182,8 @@
 
   auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
   ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x1380U, info->pc);
-  EXPECT_EQ(0x1540U, info->offset);
+  EXPECT_EQ(0x340U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
 }
 
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_datarel) {
@@ -430,14 +478,14 @@
   ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, GetFdeInfoFromIndex_expect_cache_fail,
-                           GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel,
-                           GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify,
-                           GetFdeOffsetBinary_index_fail, GetFdeOffsetSequential,
-                           GetFdeOffsetSequential_last_element, GetFdeOffsetSequential_end_check,
-                           GetFdeOffsetFromPc_fail_fde_count, GetFdeOffsetFromPc_binary_search,
-                           GetFdeOffsetFromPc_sequential_search, GetCieFde32, GetCieFde64,
-                           GetFdeFromPc_fde_not_found);
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias,
+                           GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
+                           GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
+                           GetFdeOffsetBinary_verify, GetFdeOffsetBinary_index_fail,
+                           GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
+                           GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
+                           GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
+                           GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index c340291..414f2f2 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -36,7 +36,7 @@
   MockDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
   virtual ~MockDwarfSectionImpl() = default;
 
-  MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+  MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
 
   MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
 
@@ -884,7 +884,7 @@
 
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
   this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
-  ASSERT_TRUE(this->section_->Log(2, 0x1000, 0x1000, &fde));
+  ASSERT_TRUE(this->section_->Log(2, 0x1000, &fde));
 
   ASSERT_EQ(
       "4 unwind     DW_CFA_nop\n"
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 071d2df..2c6c879 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -30,13 +30,13 @@
   MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
   virtual ~MockDwarfSection() = default;
 
-  MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
+  MOCK_METHOD3(Log, bool(uint8_t, uint64_t, const DwarfFde*));
 
   MOCK_METHOD5(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*));
 
   MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
 
-  MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+  MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
 
   MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
 
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index 66207db..3d5ddd6 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -32,7 +32,7 @@
 std::deque<FunctionData> ElfInterfaceFake::functions_;
 std::deque<StepData> ElfInterfaceFake::steps_;
 
-bool ElfInterfaceFake::GetFunctionName(uint64_t, uint64_t, std::string* name, uint64_t* offset) {
+bool ElfInterfaceFake::GetFunctionName(uint64_t, std::string* name, uint64_t* offset) {
   if (functions_.empty()) {
     return false;
   }
@@ -52,7 +52,7 @@
   return true;
 }
 
-bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) {
+bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
   if (steps_.empty()) {
     return false;
   }
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index e232986..a3bf5ce 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -67,13 +67,13 @@
   virtual ~ElfInterfaceFake() = default;
 
   bool Init(uint64_t*) override { return false; }
-  void InitHeaders() override {}
+  void InitHeaders(uint64_t) override {}
   bool GetSoname(std::string*) override { return false; }
 
-  bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override;
+  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
   bool GetGlobalVariable(const std::string&, uint64_t*) override;
 
-  bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override;
+  bool Step(uint64_t, Regs*, Memory*, bool*) override;
 
   void FakeSetGlobalVariable(const std::string& global, uint64_t offset) {
     globals_[global] = offset;
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 70a52ad..5f1c2ac 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -302,7 +302,7 @@
 
   // FindEntry fails.
   bool finished;
-  ASSERT_FALSE(interface.StepExidx(0x7000, 0, nullptr, nullptr, &finished));
+  ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 
   // ExtractEntry should fail.
@@ -316,18 +316,18 @@
   regs[ARM_REG_LR] = 0x20000;
   regs.set_sp(regs[ARM_REG_SP]);
   regs.set_pc(0x1234);
-  ASSERT_FALSE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_MEMORY_INVALID, interface.LastErrorCode());
   EXPECT_EQ(0x1004U, interface.LastErrorAddress());
 
   // Eval should fail.
   memory_.SetData32(0x1004, 0x81000000);
-  ASSERT_FALSE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 
   // Everything should pass.
   memory_.SetData32(0x1004, 0x80b0b0b0);
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
   ASSERT_FALSE(finished);
   ASSERT_EQ(0x1000U, regs.sp());
@@ -336,11 +336,13 @@
   ASSERT_EQ(0x20000U, regs[ARM_REG_PC]);
 
   // Load bias is non-zero.
-  ASSERT_TRUE(interface.StepExidx(0x8000, 0x1000, &regs, &process_memory_, &finished));
+  interface.set_load_bias(0x1000);
+  ASSERT_TRUE(interface.StepExidx(0x8000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 
   // Pc too small.
-  ASSERT_FALSE(interface.StepExidx(0x8000, 0x9000, &regs, &process_memory_, &finished));
+  interface.set_load_bias(0x9000);
+  ASSERT_FALSE(interface.StepExidx(0x8000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 }
 
@@ -362,7 +364,7 @@
 
   // Everything should pass.
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_FALSE(finished);
   ASSERT_EQ(0x10004U, regs.sp());
@@ -386,7 +388,7 @@
   regs.set_pc(0x1234);
 
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0x10000U, regs.sp());
@@ -409,7 +411,7 @@
   regs.set_pc(0x1234);
 
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0x10000U, regs.sp());
@@ -436,7 +438,7 @@
   regs.set_pc(0x1234);
 
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0U, regs.pc());
@@ -449,7 +451,7 @@
   regs.set_sp(regs[ARM_REG_SP]);
   regs.set_pc(0x1234);
 
-  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
   EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
   ASSERT_TRUE(finished);
   ASSERT_EQ(0U, regs.pc());
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index bf97e30..4008e9b 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -647,7 +647,7 @@
   memory_.SetData32(0x10004, 0x500);
   memory_.SetData32(0x10008, 250);
 
-  elf.InitHeaders();
+  elf.InitHeaders(0);
 
   EXPECT_FALSE(elf.eh_frame() == nullptr);
   EXPECT_TRUE(elf.debug_frame() == nullptr);
@@ -680,7 +680,7 @@
   memory_.SetData32(0x5108, 0x1500);
   memory_.SetData32(0x510c, 0x200);
 
-  elf.InitHeaders();
+  elf.InitHeaders(0);
 
   EXPECT_TRUE(elf.eh_frame() == nullptr);
   EXPECT_FALSE(elf.debug_frame() == nullptr);
@@ -703,7 +703,7 @@
   elf.FakeSetDebugFrameOffset(0);
   elf.FakeSetDebugFrameSize(0);
 
-  elf.InitHeaders();
+  elf.InitHeaders(0);
 
   EXPECT_TRUE(elf.eh_frame() == nullptr);
   EXPECT_EQ(0U, elf.eh_frame_offset());
@@ -728,7 +728,7 @@
   elf.FakeSetDebugFrameOffset(0x1000);
   elf.FakeSetDebugFrameSize(0x100);
 
-  elf.InitHeaders();
+  elf.InitHeaders(0);
 
   EXPECT_TRUE(elf.eh_frame() == nullptr);
   EXPECT_TRUE(elf.debug_frame() == nullptr);
@@ -833,10 +833,10 @@
   // Look in the first symbol table.
   std::string name;
   uint64_t name_offset;
-  ASSERT_TRUE(elf->GetFunctionName(0x90010, 0, &name, &name_offset));
+  ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset));
   EXPECT_EQ("function_one", name);
   EXPECT_EQ(16U, name_offset);
-  ASSERT_TRUE(elf->GetFunctionName(0xd0020, 0, &name, &name_offset));
+  ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset));
   EXPECT_EQ("function_two", name);
   EXPECT_EQ(32U, name_offset);
 }
@@ -1065,7 +1065,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  elf->InitHeaders();
+  elf->InitHeaders(0);
   EXPECT_EQ(0U, load_bias);
   EXPECT_FALSE(elf->IsValidPc(0));
   EXPECT_FALSE(elf->IsValidPc(0x20ff));
@@ -1128,7 +1128,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  elf->InitHeaders();
+  elf->InitHeaders(0);
   EXPECT_EQ(0U, load_bias);
   EXPECT_FALSE(elf->IsValidPc(0));
   EXPECT_FALSE(elf->IsValidPc(0x27ff));
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index aecbf6d..55fe16f 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -297,16 +297,11 @@
   elf.FakeSetInterface(interface);
 
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
   MapInfo map_info(0x1000, 0x2000);
 
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 
-  elf.FakeSetLoadBias(0x3000);
-  ASSERT_EQ(0x3101U, elf.GetRelPc(0x1101, &map_info));
-
   elf.FakeSetValid(false);
-  elf.FakeSetLoadBias(0);
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 }
 
@@ -328,7 +323,6 @@
   }
 
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
   bool finished;
   ASSERT_TRUE(elf.Step(0x3000, 0x1000, &regs, &process_memory, &finished));
   EXPECT_FALSE(finished);
@@ -342,11 +336,11 @@
   virtual ~ElfInterfaceMock() = default;
 
   bool Init(uint64_t*) override { return false; }
-  void InitHeaders() override {}
+  void InitHeaders(uint64_t) override {}
   bool GetSoname(std::string*) override { return false; }
-  bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; }
+  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
 
-  MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*));
+  MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
   MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
   MOCK_METHOD1(IsValidPc, bool(uint64_t));
 
@@ -358,7 +352,6 @@
 TEST_F(ElfTest, step_in_interface) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   RegsArm regs;
 
@@ -367,30 +360,12 @@
   MemoryFake process_memory;
 
   bool finished;
-  EXPECT_CALL(*interface, Step(0x1000, 0, &regs, &process_memory, &finished))
+  EXPECT_CALL(*interface, Step(0x1000, &regs, &process_memory, &finished))
       .WillOnce(::testing::Return(true));
 
   ASSERT_TRUE(elf.Step(0x1004, 0x1000, &regs, &process_memory, &finished));
 }
 
-TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
-  ElfFake elf(memory_);
-  elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0x4000);
-
-  RegsArm regs;
-
-  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
-  elf.FakeSetInterface(interface);
-  MemoryFake process_memory;
-
-  bool finished;
-  EXPECT_CALL(*interface, Step(0x7300, 0x4000, &regs, &process_memory, &finished))
-      .WillOnce(::testing::Return(true));
-
-  ASSERT_TRUE(elf.Step(0x7304, 0x7300, &regs, &process_memory, &finished));
-}
-
 TEST_F(ElfTest, get_global_invalid_elf) {
   ElfFake elf(memory_);
   elf.FakeSetValid(false);
@@ -403,7 +378,6 @@
 TEST_F(ElfTest, get_global_valid_not_in_interface) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
@@ -431,10 +405,26 @@
   ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
 }
 
+TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0x100);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+  EXPECT_EQ(0x200U, offset);
+}
+
 TEST_F(ElfTest, get_global_valid_dynamic_zero) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
@@ -456,7 +446,6 @@
 TEST_F(ElfTest, get_global_valid_in_gnu_debugdata_dynamic_zero) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
@@ -470,27 +459,9 @@
   EXPECT_EQ(0x300U, offset);
 }
 
-TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) {
-  ElfFake elf(memory_);
-  elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0x100);
-
-  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
-  elf.FakeSetInterface(interface);
-
-  uint64_t offset;
-  std::string global("something");
-  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
-      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
-
-  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
-  EXPECT_EQ(0x200U, offset);
-}
-
 TEST_F(ElfTest, get_global_valid_dynamic_adjust_negative) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   interface->MockSetDynamicOffset(0x400);
@@ -510,7 +481,6 @@
 TEST_F(ElfTest, get_global_valid_dynamic_adjust_positive) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   interface->MockSetDynamicOffset(0x1000);
@@ -530,7 +500,6 @@
 TEST_F(ElfTest, is_valid_pc_elf_invalid) {
   ElfFake elf(memory_);
   elf.FakeSetValid(false);
-  elf.FakeSetLoadBias(0);
 
   EXPECT_FALSE(elf.IsValidPc(0x100));
   EXPECT_FALSE(elf.IsValidPc(0x200));
@@ -539,7 +508,6 @@
 TEST_F(ElfTest, is_valid_pc_interface) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
@@ -549,25 +517,9 @@
   EXPECT_TRUE(elf.IsValidPc(0x1500));
 }
 
-TEST_F(ElfTest, is_valid_pc_non_zero_load_bias) {
-  ElfFake elf(memory_);
-  elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0x1000);
-
-  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
-  elf.FakeSetInterface(interface);
-
-  EXPECT_CALL(*interface, IsValidPc(0x500)).WillOnce(::testing::Return(true));
-
-  EXPECT_FALSE(elf.IsValidPc(0x100));
-  EXPECT_FALSE(elf.IsValidPc(0x200));
-  EXPECT_TRUE(elf.IsValidPc(0x1500));
-}
-
 TEST_F(ElfTest, is_valid_pc_from_gnu_debugdata) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index 45a7b58..b40a253 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -70,18 +70,18 @@
 
   std::string name;
   uint64_t func_offset;
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
   ASSERT_EQ("fake_function", name);
   ASSERT_EQ(0U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, &this->memory_, &name, &func_offset));
   ASSERT_EQ("fake_function", name);
   ASSERT_EQ(0xfU, func_offset);
 
   // Check one before and one after the function.
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, 0, &this->memory_, &name, &func_offset));
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, &this->memory_, &name, &func_offset));
 }
 
 TYPED_TEST_P(SymbolsTest, no_symbol) {
@@ -98,7 +98,7 @@
   // First verify that we can get the name.
   std::string name;
   uint64_t func_offset;
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
   ASSERT_EQ("fake_function", name);
   ASSERT_EQ(0U, func_offset);
 
@@ -107,7 +107,7 @@
   this->memory_.SetMemory(offset, &sym, sizeof(sym));
   // Clear the cache to force the symbol data to be re-read.
   symbols.ClearCache();
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
 
   // Set the function back, and set the shndx to UNDEF.
   sym.st_info = STT_FUNC;
@@ -115,7 +115,7 @@
   this->memory_.SetMemory(offset, &sym, sizeof(sym));
   // Clear the cache to force the symbol data to be re-read.
   symbols.ClearCache();
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
 }
 
 TYPED_TEST_P(SymbolsTest, multiple_entries) {
@@ -144,34 +144,34 @@
 
   std::string name;
   uint64_t func_offset;
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_two", name);
   ASSERT_EQ(1U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_one", name);
   ASSERT_EQ(4U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_three", name);
   ASSERT_EQ(1U, func_offset);
 
   // Reget some of the others to verify getting one function name doesn't
   // affect any of the next calls.
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_one", name);
   ASSERT_EQ(8U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_two", name);
   ASSERT_EQ(4U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_three", name);
   ASSERT_EQ(0xaU, func_offset);
 }
@@ -203,47 +203,21 @@
 
   std::string name;
   uint64_t func_offset;
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_two", name);
   ASSERT_EQ(1U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_one", name);
   ASSERT_EQ(4U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_three", name);
   ASSERT_EQ(1U, func_offset);
 }
 
-TYPED_TEST_P(SymbolsTest, load_bias) {
-  Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
-
-  TypeParam sym;
-  this->InitSym(&sym, 0x5000, 0x10, 0x40);
-  uint64_t offset = 0x1000;
-  this->memory_.SetMemory(offset, &sym, sizeof(sym));
-
-  std::string fake_name("fake_function");
-  this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
-
-  // Set a non-zero load_bias that should be a valid function offset.
-  std::string name;
-  uint64_t func_offset;
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
-  ASSERT_EQ("fake_function", name);
-  ASSERT_EQ(4U, func_offset);
-
-  // Set a flag that should cause the load_bias to be ignored.
-  sym.st_shndx = SHN_ABS;
-  this->memory_.SetMemory(offset, &sym, sizeof(sym));
-  // Clear the cache to force the symbol data to be re-read.
-  symbols.ClearCache();
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
-}
-
 TYPED_TEST_P(SymbolsTest, symtab_value_out_of_bounds) {
   Symbols symbols_end_at_100(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x100);
   Symbols symbols_end_at_200(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x200);
@@ -265,18 +239,16 @@
   std::string name;
   uint64_t func_offset;
   // Verify that we can get the function name properly for both entries.
-  ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
   ASSERT_EQ("fake_function", name);
   ASSERT_EQ(0U, func_offset);
-  ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function", name);
   ASSERT_EQ(0U, func_offset);
 
   // Now use the symbol table that ends at 0x100.
-  ASSERT_FALSE(
-      symbols_end_at_100.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
-  ASSERT_FALSE(
-      symbols_end_at_100.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols_end_at_100.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols_end_at_100.GetName<TypeParam>(0x3000, &this->memory_, &name, &func_offset));
 }
 
 // Verify the entire func table is cached.
@@ -302,9 +274,9 @@
   // Do call that should cache all of the entries (except the string data).
   std::string name;
   uint64_t func_offset;
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
   this->memory_.Clear();
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
 
   // Clear the memory and only put the symbol data string data in memory.
   this->memory_.Clear();
@@ -317,15 +289,15 @@
   fake_name = "third_entry";
   this->memory_.SetMemory(0xa300, fake_name.c_str(), fake_name.size() + 1);
 
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, &this->memory_, &name, &func_offset));
   ASSERT_EQ("first_entry", name);
   ASSERT_EQ(1U, func_offset);
 
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, &this->memory_, &name, &func_offset));
   ASSERT_EQ("second_entry", name);
   ASSERT_EQ(2U, func_offset);
 
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, &this->memory_, &name, &func_offset));
   ASSERT_EQ("third_entry", name);
   ASSERT_EQ(3U, func_offset);
 }
@@ -381,17 +353,17 @@
   EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_1", &offset));
 
   std::string name;
-  EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, 0, &this->memory_, &name, &offset));
+  EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, &this->memory_, &name, &offset));
   EXPECT_EQ("function_0", name);
   EXPECT_EQ(2U, offset);
 
-  EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, 0, &this->memory_, &name, &offset));
+  EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, &this->memory_, &name, &offset));
   EXPECT_EQ("function_1", name);
   EXPECT_EQ(4U, offset);
 }
 
 REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
-                           multiple_entries_nonstandard_size, load_bias, symtab_value_out_of_bounds,
+                           multiple_entries_nonstandard_size, symtab_value_out_of_bounds,
                            symtab_read_cached, get_global);
 
 typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 285fc9e..a65c077 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1200,4 +1200,43 @@
   EXPECT_EQ(0xffcc1558U, unwinder.frames()[18].sp);
 }
 
+// Test using a non-zero load bias library that has the fde entries
+// encoded as 0xb, which is not set as pc relative.
+TEST_F(UnwindOfflineTest, debug_frame_load_bias_arm) {
+  ASSERT_NO_FATAL_FAILURE(Init("debug_frame_load_bias_arm/", ARCH_ARM));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(8U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0005138c  libc.so (__ioctl+8)\n"
+      "  #01 pc 0002140f  libc.so (ioctl+30)\n"
+      "  #02 pc 00039535  libbinder.so (_ZN7android14IPCThreadState14talkWithDriverEb+204)\n"
+      "  #03 pc 00039633  libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+10)\n"
+      "  #04 pc 00039b57  libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+38)\n"
+      "  #05 pc 00000c21  mediaserver (main+104)\n"
+      "  #06 pc 00084b89  libc.so (__libc_init+48)\n"
+      "  #07 pc 00000b77  mediaserver (_start_main+38)\n",
+      frame_info);
+
+  EXPECT_EQ(0xf0be238cU, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xffd4a638U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xf0bb240fU, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xffd4a638U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0xf1a75535U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xffd4a650U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0xf1a75633U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xffd4a6b0U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0xf1a75b57U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0xffd4a6d0U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x8d1cc21U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0xffd4a6e8U, unwinder.frames()[5].sp);
+  EXPECT_EQ(0xf0c15b89U, unwinder.frames()[6].pc);
+  EXPECT_EQ(0xffd4a700U, unwinder.frames()[6].sp);
+  EXPECT_EQ(0x8d1cb77U, unwinder.frames()[7].pc);
+  EXPECT_EQ(0xffd4a718U, unwinder.frames()[7].sp);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
new file mode 100644
index 0000000..4b7bf44
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
new file mode 100644
index 0000000..013858e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
new file mode 100644
index 0000000..10f1325
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
@@ -0,0 +1,3 @@
+8d1c000-8d1f000 r-xp 0 00:00 0   mediaserver
+f0b91000-f0c2c000 r-xp 0 00:00 0   libc.so
+f1a41000-f1a97000 r-xp 0 00:00 0   libbinder.so
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
new file mode 100644
index 0000000..9e4a83f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
new file mode 100644
index 0000000..f147247
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: 3
+r1: c0306201
+r2: ffd4a658
+r3: 0
+r4: f0c36d8c
+r5: ffd4a658
+r6: f0168000
+r7: 36
+r8: ffd4a678
+r9: f016802c
+r10: ffd4a660
+r11: 0
+ip: 0
+sp: ffd4a638
+lr: f0bb2413
+pc: f0be238c
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
new file mode 100644
index 0000000..847c819
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 5a8edfd..266a6db 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -37,7 +37,7 @@
 
 namespace unwindstack {
 
-void DumpArm(ElfInterfaceArm* interface) {
+void DumpArm(Elf* elf, ElfInterfaceArm* interface) {
   if (interface == nullptr) {
     printf("No ARM Unwind Information.\n\n");
     return;
@@ -48,12 +48,11 @@
     uint64_t load_bias = entry.second.table_offset;
     printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
            entry.second.table_size + load_bias);
-    for (auto addr : *interface) {
+    for (auto pc : *interface) {
       std::string name;
-      printf("  PC 0x%" PRIx64, addr + load_bias);
+      printf("  PC 0x%" PRIx64, pc + load_bias);
       uint64_t func_offset;
-      uint64_t pc = addr + load_bias;
-      if (interface->GetFunctionName(pc, load_bias, &name, &func_offset) && !name.empty()) {
+      if (elf->GetFunctionName(pc + load_bias, &name, &func_offset) && !name.empty()) {
         printf(" <%s>", name.c_str());
       }
       printf("\n");
@@ -63,7 +62,7 @@
         continue;
       }
       ArmExidx arm(nullptr, interface->memory(), nullptr);
-      arm.set_log(true);
+      arm.set_log(ARM_LOG_FULL);
       arm.set_log_skip_execution(true);
       arm.set_log_indent(2);
       if (!arm.ExtractEntryData(entry)) {
@@ -82,21 +81,21 @@
   printf("\n");
 }
 
-void DumpDwarfSection(ElfInterface* interface, DwarfSection* section, uint64_t load_bias) {
+void DumpDwarfSection(Elf* elf, DwarfSection* section, uint64_t) {
   for (const DwarfFde* fde : *section) {
     // Sometimes there are entries that have empty length, skip those since
     // they don't contain any interesting information.
     if (fde == nullptr || fde->pc_start == fde->pc_end) {
       continue;
     }
-    printf("\n  PC 0x%" PRIx64, fde->pc_start + load_bias);
+    printf("\n  PC 0x%" PRIx64 "-0x%" PRIx64, fde->pc_start, fde->pc_end);
     std::string name;
     uint64_t func_offset;
-    if (interface->GetFunctionName(fde->pc_start, load_bias, &name, &func_offset) && !name.empty()) {
+    if (elf->GetFunctionName(fde->pc_start, &name, &func_offset) && !name.empty()) {
       printf(" <%s>", name.c_str());
     }
     printf("\n");
-    if (!section->Log(2, UINT64_MAX, load_bias, fde)) {
+    if (!section->Log(2, UINT64_MAX, fde)) {
       printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
     }
   }
@@ -126,13 +125,13 @@
 
   ElfInterface* interface = elf.interface();
   if (elf.machine_type() == EM_ARM) {
-    DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
+    DumpArm(&elf, reinterpret_cast<ElfInterfaceArm*>(interface));
     printf("\n");
   }
 
   if (interface->eh_frame() != nullptr) {
     printf("eh_frame information:\n");
-    DumpDwarfSection(interface, interface->eh_frame(), elf.GetLoadBias());
+    DumpDwarfSection(&elf, interface->eh_frame(), elf.GetLoadBias());
     printf("\n");
   } else {
     printf("\nno eh_frame information\n");
@@ -140,7 +139,7 @@
 
   if (interface->debug_frame() != nullptr) {
     printf("\ndebug_frame information:\n");
-    DumpDwarfSection(interface, interface->debug_frame(), elf.GetLoadBias());
+    DumpDwarfSection(&elf, interface->debug_frame(), elf.GetLoadBias());
     printf("\n");
   } else {
     printf("\nno debug_frame information\n");
@@ -151,12 +150,12 @@
   if (gnu_debugdata_interface != nullptr) {
     if (gnu_debugdata_interface->eh_frame() != nullptr) {
       printf("\ngnu_debugdata (eh_frame):\n");
-      DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->eh_frame(), 0);
+      DumpDwarfSection(&elf, gnu_debugdata_interface->eh_frame(), 0);
       printf("\n");
     }
     if (gnu_debugdata_interface->debug_frame() != nullptr) {
       printf("\ngnu_debugdata (debug_frame):\n");
-      DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->debug_frame(), 0);
+      DumpDwarfSection(&elf, gnu_debugdata_interface->debug_frame(), 0);
       printf("\n");
     }
   } else {
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 47a4f91..0f01566 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -34,7 +34,9 @@
 #include <unwindstack/ElfInterface.h>
 #include <unwindstack/Log.h>
 
+#include "ArmExidx.h"
 #include "DwarfOp.h"
+#include "ElfInterfaceArm.h"
 
 namespace unwindstack {
 
@@ -136,6 +138,32 @@
   }
 }
 
+void PrintArmRegInformation(ElfInterfaceArm* interface, uint64_t pc) {
+  printf("\nArm exidx:\n");
+  uint64_t entry_offset;
+  if (!interface->FindEntry(pc, &entry_offset)) {
+    return;
+  }
+
+  ArmExidx arm(nullptr, interface->memory(), nullptr);
+
+  log_to_stdout(true);
+  arm.set_log(ARM_LOG_BY_REG);
+  arm.set_log_skip_execution(true);
+  arm.set_log_indent(1);
+  if (!arm.ExtractEntryData(entry_offset)) {
+    if (arm.status() != ARM_STATUS_NO_UNWIND) {
+      printf("  Error trying to extract data.\n");
+    }
+    return;
+  }
+  if (arm.data()->size() != 0 && arm.Eval()) {
+    arm.LogByReg();
+  } else {
+    printf("  Error tring to evaluate exidx data.\n");
+  }
+}
+
 int GetInfo(const char* file, uint64_t pc) {
   MemoryFileAtOffset* memory = new MemoryFileAtOffset;
   if (!memory->Init(file, 0)) {
@@ -162,12 +190,22 @@
     printf("Soname: %s\n\n", soname.c_str());
   }
 
-  printf("PC 0x%" PRIx64 ":\n", pc);
+  printf("PC 0x%" PRIx64, pc);
+  std::string function_name;
+  uint64_t function_offset;
+  if (elf.GetFunctionName(pc, &function_name, &function_offset)) {
+    printf(" (%s)", function_name.c_str());
+  }
+  printf(":\n");
+
+  if (elf.machine_type() == EM_ARM) {
+    PrintArmRegInformation(reinterpret_cast<ElfInterfaceArm*>(interface), pc - load_bias);
+  }
 
   DwarfSection* section = interface->eh_frame();
   if (section != nullptr) {
     printf("\neh_frame:\n");
-    PrintRegInformation(section, memory, pc - load_bias, elf.class_type());
+    PrintRegInformation(section, memory, pc, elf.class_type());
   } else {
     printf("\nno eh_frame information\n");
   }
@@ -175,7 +213,7 @@
   section = interface->debug_frame();
   if (section != nullptr) {
     printf("\ndebug_frame:\n");
-    PrintRegInformation(section, memory, pc - load_bias, elf.class_type());
+    PrintRegInformation(section, memory, pc, elf.class_type());
     printf("\n");
   } else {
     printf("\nno debug_frame information\n");
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 086dffe..f8e3e92 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -95,7 +95,6 @@
   }
 
   std::string name;
-  uint64_t load_bias = elf.GetLoadBias();
   if (argc == 3) {
     std::string cur_name;
     uint64_t func_offset;
@@ -113,8 +112,8 @@
 
   // This is a crude way to get the symbols in order.
   for (const auto& entry : elf.interface()->pt_loads()) {
-    uint64_t start = entry.second.offset + load_bias;
-    uint64_t end = entry.second.table_size + load_bias;
+    uint64_t start = entry.second.offset;
+    uint64_t end = entry.second.table_size;
     for (uint64_t addr = start; addr < end; addr += 4) {
       std::string cur_name;
       uint64_t func_offset;
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index 678b853..b15be1f 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -18,11 +18,9 @@
 (a) didn't stand out given all the other systems-level changes and (b)
 in Marshmallow we changed direction and started the move to toybox.
 
-Not everything is provided by toybox, though. We currently still use
-the BSD dd and grep (because the toybox versions are still unfinished),
-and for the bzip2 command-line tools we use the ones that are part of
-the bzip2 distribution. The awk added in Android P is Brian Kernighan's
-"one true" awk.
+Not everything is provided by toybox, though. For the bzip2 command-line tools
+we use the ones that are part of the bzip2 distribution. The awk added in
+Android P is Brian Kernighan's "one true" awk.
 
 The lists below show what tools were provided and where they came from in
 each release starting with Gingerbread. This doesn't tell the full story,