Merge "Modernize fastboot output format."
diff --git a/adb/Android.mk b/adb/Android.mk
new file mode 100644
index 0000000..8b2d558
--- /dev/null
+++ b/adb/Android.mk
@@ -0,0 +1,8 @@
+LOCAL_PATH := $(call my-dir)
+
+# Archive adb, adb.exe.
+$(call dist-for-goals,dist_files sdk win_sdk,$(HOST_OUT_EXECUTABLES)/adb)
+
+ifdef HOST_CROSS_OS
+$(call dist-for-goals,dist_files sdk win_sdk,$(ALL_MODULES.host_cross_adb.BUILT))
+endif
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 17c7eba..401c99c 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -334,8 +334,18 @@
         // processes, so we need to manually reset back to SIG_DFL here (http://b/35209888).
         signal(SIGPIPE, SIG_DFL);
 
+        // Increase oom_score_adj from -1000, so that the child is visible to the OOM-killer.
+        // Don't treat failure as an error, because old Android kernels explicitly disabled this.
+        int oom_score_adj_fd = adb_open("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC);
+        if (oom_score_adj_fd != -1) {
+            const char* oom_score_adj_value = "-950";
+            TEMP_FAILURE_RETRY(
+                adb_write(oom_score_adj_fd, oom_score_adj_value, strlen(oom_score_adj_value)));
+        }
+
         if (command_.empty()) {
-            execle(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr, cenv.data());
+            // Spawn a login shell if we don't have a command.
+            execle(_PATH_BSHELL, "-" _PATH_BSHELL, nullptr, cenv.data());
         } else {
             execle(_PATH_BSHELL, _PATH_BSHELL, "-c", command_.c_str(), nullptr, cenv.data());
         }
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 6b40056..f587fdb 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -42,7 +42,9 @@
 
 class LocalSocketTest : public FdeventTest {};
 
-constexpr auto SLEEP_FOR_FDEVENT = 100ms;
+static void WaitForFdeventLoop() {
+    std::this_thread::sleep_for(100ms);
+}
 
 TEST_F(LocalSocketTest, smoke) {
     // Join two socketpairs with a chain of intermediate socketpairs.
@@ -98,7 +100,7 @@
     ASSERT_EQ(0, adb_close(last[1]));
 
     // Wait until the local sockets are closed.
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    WaitForFdeventLoop();
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
     TerminateThread(thread);
 }
@@ -144,13 +146,15 @@
 
     PrepareThread();
     std::thread thread(CloseWithPacketThreadFunc, &arg);
-    // Wait until the fdevent_loop() starts.
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+
+    WaitForFdeventLoop();
     ASSERT_EQ(0, adb_close(cause_close_fd[0]));
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+
+    WaitForFdeventLoop();
     EXPECT_EQ(1u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
     ASSERT_EQ(0, adb_close(socket_fd[0]));
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+
+    WaitForFdeventLoop();
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
     TerminateThread(thread);
 }
@@ -167,10 +171,11 @@
 
     PrepareThread();
     std::thread thread(CloseWithPacketThreadFunc, &arg);
-    // Wait until the fdevent_loop() starts.
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+
+    WaitForFdeventLoop();
     ASSERT_EQ(0, adb_close(cause_close_fd[0]));
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+
+    WaitForFdeventLoop();
     EXPECT_EQ(1u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
 
     // Verify if we can read successfully.
@@ -179,7 +184,7 @@
     ASSERT_EQ(true, ReadFdExactly(socket_fd[0], buf.data(), buf.size()));
     ASSERT_EQ(0, adb_close(socket_fd[0]));
 
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    WaitForFdeventLoop();
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
     TerminateThread(thread);
 }
@@ -199,12 +204,12 @@
 
     PrepareThread();
     std::thread thread(CloseWithPacketThreadFunc, &arg);
-    // Wait until the fdevent_loop() starts.
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+
+    WaitForFdeventLoop();
     EXPECT_EQ(2u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
     ASSERT_EQ(0, adb_close(socket_fd[0]));
 
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    WaitForFdeventLoop();
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
     TerminateThread(thread);
 }
@@ -242,8 +247,7 @@
     adb_close(head_fd[0]);
     adb_close(tail_fd[0]);
 
-    // Wait until the local sockets are closed.
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    WaitForFdeventLoop();
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
     TerminateThread(thread);
 }
@@ -285,14 +289,13 @@
     PrepareThread();
     std::thread thread(CloseRdHupSocketThreadFunc, &arg);
 
-    // Wait until the fdevent_loop() starts.
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    WaitForFdeventLoop();
     EXPECT_EQ(1u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
 
     // Wait until the client closes its socket.
     client_thread.join();
 
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    WaitForFdeventLoop();
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
     TerminateThread(thread);
 }
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
index fd19882..79cebe6 100644
--- a/adb/sysdeps_test.cpp
+++ b/adb/sysdeps_test.cpp
@@ -121,10 +121,13 @@
     adb_pollfd pfd[3] = {};
     pfd[0].fd = fds[0];
     pfd[0].events = POLLRDNORM;
+    pfd[0].revents = ~0;
     pfd[1].fd = INT_MAX;
     pfd[1].events = POLLRDNORM;
+    pfd[1].revents = ~0;
     pfd[2].fd = fds[1];
     pfd[2].events = POLLWRNORM;
+    pfd[2].revents = ~0;
 
     ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
 
@@ -136,6 +139,17 @@
     EXPECT_EQ(POLLRDNORM, pfd[0].revents);
     EXPECT_EQ(POLLNVAL, pfd[1].revents);
     EXPECT_EQ(POLLWRNORM, pfd[2].revents);
+
+    // Make sure that we return immediately if an invalid FD is given.
+    pfd[0].fd = fds[0];
+    pfd[0].events = POLLRDNORM;
+    pfd[0].revents = ~0;
+    pfd[1].fd = INT_MAX;
+    pfd[1].events = POLLRDNORM;
+    pfd[1].revents = ~0;
+    EXPECT_EQ(2, adb_poll(pfd, 2, -1));
+    EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+    EXPECT_EQ(POLLNVAL, pfd[1].revents);
 }
 
 TEST_F(sysdeps_poll, duplicate_fd) {
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 62f4ac8..7d35fb6 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -529,6 +529,7 @@
     int skipped = 0;
     std::vector<WSAPOLLFD> sockets;
     std::vector<adb_pollfd*> original;
+
     for (size_t i = 0; i < nfds; ++i) {
         FH fh = _fh_from_int(fds[i].fd, __func__);
         if (!fh || !fh->used || fh->clazz != &_fh_socket_class) {
@@ -549,6 +550,11 @@
         return skipped;
     }
 
+    // If we have any invalid FDs in our FD set, make sure to return immediately.
+    if (skipped > 0) {
+        timeout = 0;
+    }
+
     int result = WSAPoll(sockets.data(), sockets.size(), timeout);
     if (result == SOCKET_ERROR) {
         _socket_set_errno(WSAGetLastError());
@@ -560,7 +566,7 @@
         original[i]->revents = sockets[i].revents;
     }
 
-    // WSAPoll appears to return the number of unique FDs with avaiable events, instead of how many
+    // WSAPoll appears to return the number of unique FDs with available events, instead of how many
     // of the pollfd elements have a non-zero revents field, which is what it and poll are specified
     // to do. Ignore its result and calculate the proper return value.
     result = 0;
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index f5bcc26..944b00b 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -22,7 +22,8 @@
 
 LOCAL_C_INCLUDES := \
   $(LOCAL_PATH)/../adb \
-  $(LOCAL_PATH)/../mkbootimg \
+
+LOCAL_HEADER_LIBRARIES := bootimg_headers
 
 LOCAL_SRC_FILES := \
     bootimg_utils.cpp \
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 72a65d2..3fbef9f 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -116,17 +116,21 @@
 #define EM_ICE          2
 #define EM_AES_256_CTS  3
 #define EM_AES_256_HEH  4
+#define EM_SPECK_128_256_XTS 5
+#define EM_SPECK_128_256_CTS 6
 
 static const struct flag_list file_contents_encryption_modes[] = {
     {"aes-256-xts", EM_AES_256_XTS},
+    {"speck128/256-xts", EM_SPECK_128_256_XTS},
     {"software", EM_AES_256_XTS}, /* alias for backwards compatibility */
-    {"ice", EM_ICE}, /* hardware-specific inline cryptographic engine */
+    {"ice", EM_ICE},              /* hardware-specific inline cryptographic engine */
     {0, 0},
 };
 
 static const struct flag_list file_names_encryption_modes[] = {
     {"aes-256-cts", EM_AES_256_CTS},
     {"aes-256-heh", EM_AES_256_HEH},
+    {"speck128/256-cts", EM_SPECK_128_256_CTS},
     {0, 0},
 };
 
diff --git a/init/Android.bp b/init/Android.bp
index 31c8efb..70a4ac6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -127,9 +127,7 @@
         "watchdogd.cpp",
     ],
     whole_static_libs: ["libcap"],
-    include_dirs: [
-        "system/core/mkbootimg",
-    ],
+    header_libs: ["bootimg_headers"],
     proto: {
         type: "lite",
         export_proto_headers: true,
diff --git a/init/stable_properties.h b/init/stable_properties.h
index c8bdaa4..4714b57 100644
--- a/init/stable_properties.h
+++ b/init/stable_properties.h
@@ -35,10 +35,12 @@
     "persist.bluetooth.btsnoopenable",
     "persist.sys.crash_rcu",
     "persist.sys.zram_enabled",
+    "ro.board.platform",
     "ro.bootmode",
     "ro.build.type",
     "ro.debuggable",
     "sys.boot_completed",
+    "sys.boot_from_charger_mode",
     "sys.retaildemo.enabled",
     "sys.shutdown.requested",
     "sys.usb.config",
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 5ea981f..f6f7128 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -199,8 +199,9 @@
 
     // Support wifi_hal_legacy administering a network interface.
     { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
-                                           CAP_MASK_LONG(CAP_NET_RAW),
-                                              "vendor/bin/hw/android.hardware.wifi@1.0-service" },
+                                           CAP_MASK_LONG(CAP_NET_RAW) |
+                                           CAP_MASK_LONG(CAP_SYS_MODULE),
+                                           "vendor/bin/hw/android.hardware.wifi@1.0-service" },
 
     // generic defaults
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index aa8cd3a..6ecedce 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -50,7 +50,17 @@
   memory_->set_cur_offset(start_offset);
   uint64_t cfa_offset;
   cur_pc_ = fde_->pc_start;
-  while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc_ <= pc) {
+  loc_regs->pc_start = cur_pc_;
+  while (true) {
+    if (cur_pc_ > pc) {
+      loc_regs->pc_end = cur_pc_;
+      return true;
+    }
+    if ((cfa_offset = memory_->cur_offset()) >= end_offset) {
+      loc_regs->pc_end = fde_->pc_end;
+      return true;
+    }
+    loc_regs->pc_start = cur_pc_;
     operands_.clear();
     // Read the cfa information.
     uint8_t cfa_value;
@@ -129,7 +139,6 @@
       }
     }
   }
-  return true;
 }
 
 template <typename AddressType>
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 5586e72..65eec65 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -55,21 +55,29 @@
 }
 
 bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
-  last_error_.code = DWARF_ERROR_NONE;
-  const DwarfFde* fde = GetFdeFromPc(pc);
-  if (fde == nullptr || fde->cie == nullptr) {
-    last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
-    return false;
-  }
+  // Lookup the pc in the cache.
+  auto it = loc_regs_.upper_bound(pc);
+  if (it == loc_regs_.end() || pc < it->second.pc_start) {
+    last_error_.code = DWARF_ERROR_NONE;
+    const DwarfFde* fde = GetFdeFromPc(pc);
+    if (fde == nullptr || fde->cie == nullptr) {
+      last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+      return false;
+    }
 
-  // Now get the location information for this pc.
-  dwarf_loc_regs_t loc_regs;
-  if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
-    return false;
+    // Now get the location information for this pc.
+    dwarf_loc_regs_t loc_regs;
+    if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
+      return false;
+    }
+    loc_regs.cie = fde->cie;
+
+    // Store it in the cache.
+    it = loc_regs_.emplace(loc_regs.pc_end, std::move(loc_regs)).first;
   }
 
   // Now eval the actual registers.
-  return Eval(fde->cie, process_memory, loc_regs, regs, finished);
+  return Eval(it->second.cie, process_memory, it->second, regs, finished);
 }
 
 template <typename AddressType>
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
index e2a9cb0..27cab43 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -51,13 +51,23 @@
 }
 
 uint64_t RegsArm::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+  if (!elf->valid()) {
+    return 2;
+  }
+
   uint64_t load_bias = elf->GetLoadBias();
   if (rel_pc < load_bias) {
-    return 0;
+    if (rel_pc < 2) {
+      return 0;
+    }
+    return 2;
   }
   uint64_t adjusted_rel_pc = rel_pc - load_bias;
   if (adjusted_rel_pc < 5) {
-    return 0;
+    if (adjusted_rel_pc < 2) {
+      return 0;
+    }
+    return 2;
   }
 
   if (adjusted_rel_pc & 1) {
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index fe24c80..4a2a6c4 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -51,8 +51,8 @@
   regs_[ARM64_REG_SP] = sp;
 }
 
-uint64_t RegsArm64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid() || rel_pc < 4) {
+uint64_t RegsArm64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+  if (rel_pc < 4) {
     return 0;
   }
   return 4;
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
index 0b10e21..c87e69b 100644
--- a/libunwindstack/RegsMips.cpp
+++ b/libunwindstack/RegsMips.cpp
@@ -51,8 +51,8 @@
   regs_[MIPS_REG_SP] = static_cast<uint32_t>(sp);
 }
 
-uint64_t RegsMips::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid() || rel_pc < 8) {
+uint64_t RegsMips::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+  if (rel_pc < 8) {
     return 0;
   }
   // For now, just assume no compact branches
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
index 8848e3b..236a922 100644
--- a/libunwindstack/RegsMips64.cpp
+++ b/libunwindstack/RegsMips64.cpp
@@ -51,8 +51,8 @@
   regs_[MIPS64_REG_SP] = sp;
 }
 
-uint64_t RegsMips64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid() || rel_pc < 8) {
+uint64_t RegsMips64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+  if (rel_pc < 8) {
     return 0;
   }
   // For now, just assume no compact branches
diff --git a/libunwindstack/RegsX86.cpp b/libunwindstack/RegsX86.cpp
index bb95a13..f7e0614 100644
--- a/libunwindstack/RegsX86.cpp
+++ b/libunwindstack/RegsX86.cpp
@@ -50,8 +50,8 @@
   regs_[X86_REG_SP] = static_cast<uint32_t>(sp);
 }
 
-uint64_t RegsX86::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid() || rel_pc == 0) {
+uint64_t RegsX86::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+  if (rel_pc == 0) {
     return 0;
   }
   return 1;
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index e57e2bc..7d6ad86 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -50,8 +50,8 @@
   regs_[X86_64_REG_SP] = sp;
 }
 
-uint64_t RegsX86_64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid() || rel_pc == 0) {
+uint64_t RegsX86_64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+  if (rel_pc == 0) {
     return 0;
   }
   return 1;
diff --git a/libunwindstack/include/unwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
index 0881182..3d50ccf 100644
--- a/libunwindstack/include/unwindstack/DwarfLocation.h
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -23,6 +23,8 @@
 
 namespace unwindstack {
 
+struct DwarfCie;
+
 enum DwarfLocationEnum : uint8_t {
   DWARF_LOCATION_INVALID = 0,
   DWARF_LOCATION_UNDEFINED,
@@ -38,7 +40,13 @@
   uint64_t values[2];
 };
 
-typedef std::unordered_map<uint32_t, DwarfLocation> dwarf_loc_regs_t;
+struct DwarfLocations : public std::unordered_map<uint32_t, DwarfLocation> {
+  const DwarfCie* cie;
+  // The range of PCs where the locations are valid (end is exclusive).
+  uint64_t pc_start = 0;
+  uint64_t pc_end = 0;
+};
+typedef DwarfLocations dwarf_loc_regs_t;
 
 }  // namespace unwindstack
 
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index da91fd0..209c54a 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include <iterator>
+#include <map>
 #include <unordered_map>
 
 #include <unwindstack/DwarfError.h>
@@ -112,6 +113,7 @@
   std::unordered_map<uint64_t, DwarfFde> fde_entries_;
   std::unordered_map<uint64_t, DwarfCie> cie_entries_;
   std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
+  std::map<uint64_t, dwarf_loc_regs_t> loc_regs_;  // Single row indexed by pc_end.
 };
 
 template <typename AddressType>
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index c85764c..c340291 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -853,7 +853,8 @@
   fde.cfa_instructions_offset = 0x6000;
   fde.cfa_instructions_end = 0x6002;
 
-  dwarf_loc_regs_t cie_loc_regs{{6, {DWARF_LOCATION_REGISTER, {4, 0}}}};
+  dwarf_loc_regs_t cie_loc_regs;
+  cie_loc_regs[6] = DwarfLocation{DWARF_LOCATION_REGISTER, {4, 0}};
   this->section_->TestSetCachedCieLocRegs(0x8000, cie_loc_regs);
   this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
 
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 3fcd2b6..071d2df 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -165,4 +165,72 @@
   ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
 }
 
+static bool MockGetCfaLocationInfo(::testing::Unused, const DwarfFde* fde,
+                                   dwarf_loc_regs_t* loc_regs) {
+  loc_regs->pc_start = fde->pc_start;
+  loc_regs->pc_end = fde->pc_end;
+  return true;
+}
+
+TEST_F(DwarfSectionTest, Step_cache) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfCie cie{};
+  DwarfFde fde{};
+  fde.pc_start = 0x500;
+  fde.pc_end = 0x2000;
+  fde.cie = &cie;
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+      .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+  MemoryFake process;
+  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+      .WillRepeatedly(::testing::Return(true));
+
+  bool finished;
+  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(mock_section.Step(0x1500, nullptr, &process, &finished));
+}
+
+TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfCie cie{};
+  DwarfFde fde0{};
+  fde0.pc_start = 0x1000;
+  fde0.pc_end = 0x2000;
+  fde0.cie = &cie;
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde0));
+  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
+      .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+  MemoryFake process;
+  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+      .WillRepeatedly(::testing::Return(true));
+
+  bool finished;
+  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+
+  DwarfFde fde1{};
+  fde1.pc_start = 0x500;
+  fde1.pc_end = 0x800;
+  fde1.cie = &cie;
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x600, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde1));
+  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
+      .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+  ASSERT_TRUE(mock_section.Step(0x600, nullptr, &process, &finished));
+  ASSERT_TRUE(mock_section.Step(0x700, nullptr, &process, &finished));
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 3e80733..d15823e 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -94,48 +94,48 @@
 
 TEST_F(RegsTest, rel_pc) {
   RegsArm64 arm64;
-  ASSERT_EQ(4U, arm64.GetPcAdjustment(0x10, elf_.get()));
-  ASSERT_EQ(4U, arm64.GetPcAdjustment(0x4, elf_.get()));
-  ASSERT_EQ(0U, arm64.GetPcAdjustment(0x3, elf_.get()));
-  ASSERT_EQ(0U, arm64.GetPcAdjustment(0x2, elf_.get()));
-  ASSERT_EQ(0U, arm64.GetPcAdjustment(0x1, elf_.get()));
-  ASSERT_EQ(0U, arm64.GetPcAdjustment(0x0, elf_.get()));
+  EXPECT_EQ(4U, arm64.GetPcAdjustment(0x10, elf_.get()));
+  EXPECT_EQ(4U, arm64.GetPcAdjustment(0x4, elf_.get()));
+  EXPECT_EQ(0U, arm64.GetPcAdjustment(0x3, elf_.get()));
+  EXPECT_EQ(0U, arm64.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(0U, arm64.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(0U, arm64.GetPcAdjustment(0x0, elf_.get()));
 
   RegsX86 x86;
-  ASSERT_EQ(1U, x86.GetPcAdjustment(0x100, elf_.get()));
-  ASSERT_EQ(1U, x86.GetPcAdjustment(0x2, elf_.get()));
-  ASSERT_EQ(1U, x86.GetPcAdjustment(0x1, elf_.get()));
-  ASSERT_EQ(0U, x86.GetPcAdjustment(0x0, elf_.get()));
+  EXPECT_EQ(1U, x86.GetPcAdjustment(0x100, elf_.get()));
+  EXPECT_EQ(1U, x86.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(1U, x86.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(0U, x86.GetPcAdjustment(0x0, elf_.get()));
 
   RegsX86_64 x86_64;
-  ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x100, elf_.get()));
-  ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x2, elf_.get()));
-  ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x1, elf_.get()));
-  ASSERT_EQ(0U, x86_64.GetPcAdjustment(0x0, elf_.get()));
+  EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x100, elf_.get()));
+  EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(0U, x86_64.GetPcAdjustment(0x0, elf_.get()));
 
   RegsMips mips;
-  ASSERT_EQ(8U, mips.GetPcAdjustment(0x10, elf_.get()));
-  ASSERT_EQ(8U, mips.GetPcAdjustment(0x8, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x7, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x6, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x5, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x4, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x3, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x2, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x1, elf_.get()));
-  ASSERT_EQ(0U, mips.GetPcAdjustment(0x0, elf_.get()));
+  EXPECT_EQ(8U, mips.GetPcAdjustment(0x10, elf_.get()));
+  EXPECT_EQ(8U, mips.GetPcAdjustment(0x8, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x7, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x6, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x5, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x4, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x3, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(0U, mips.GetPcAdjustment(0x0, elf_.get()));
 
   RegsMips64 mips64;
-  ASSERT_EQ(8U, mips64.GetPcAdjustment(0x10, elf_.get()));
-  ASSERT_EQ(8U, mips64.GetPcAdjustment(0x8, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x7, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x6, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x5, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x4, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x3, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x2, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x1, elf_.get()));
-  ASSERT_EQ(0U, mips64.GetPcAdjustment(0x0, elf_.get()));
+  EXPECT_EQ(8U, mips64.GetPcAdjustment(0x10, elf_.get()));
+  EXPECT_EQ(8U, mips64.GetPcAdjustment(0x8, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x7, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x6, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x5, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x4, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x3, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(0U, mips64.GetPcAdjustment(0x0, elf_.get()));
 }
 
 TEST_F(RegsTest, rel_pc_arm) {
@@ -143,34 +143,36 @@
 
   // Check fence posts.
   elf_->FakeSetLoadBias(0);
-  ASSERT_EQ(2U, arm.GetPcAdjustment(0x5, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x4, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x3, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x2, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x0, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x5, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x4, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x3, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(0U, arm.GetPcAdjustment(0x0, elf_.get()));
 
   elf_->FakeSetLoadBias(0x100);
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0xff, elf_.get()));
-  ASSERT_EQ(2U, arm.GetPcAdjustment(0x105, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x104, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x103, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x102, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x101, elf_.get()));
-  ASSERT_EQ(0U, arm.GetPcAdjustment(0x100, elf_.get()));
+  EXPECT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x2, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0xff, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x105, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x104, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x103, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x102, elf_.get()));
+  EXPECT_EQ(0U, arm.GetPcAdjustment(0x101, elf_.get()));
+  EXPECT_EQ(0U, arm.GetPcAdjustment(0x100, elf_.get()));
 
   // Check thumb instructions handling.
   elf_->FakeSetLoadBias(0);
   memory_->SetData32(0x2000, 0);
-  ASSERT_EQ(2U, arm.GetPcAdjustment(0x2005, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x2005, elf_.get()));
   memory_->SetData32(0x2000, 0xe000f000);
-  ASSERT_EQ(4U, arm.GetPcAdjustment(0x2005, elf_.get()));
+  EXPECT_EQ(4U, arm.GetPcAdjustment(0x2005, elf_.get()));
 
   elf_->FakeSetLoadBias(0x400);
   memory_->SetData32(0x2100, 0);
-  ASSERT_EQ(2U, arm.GetPcAdjustment(0x2505, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x2505, elf_.get()));
   memory_->SetData32(0x2100, 0xf111f111);
-  ASSERT_EQ(4U, arm.GetPcAdjustment(0x2505, elf_.get()));
+  EXPECT_EQ(4U, arm.GetPcAdjustment(0x2505, elf_.get()));
 }
 
 TEST_F(RegsTest, elf_invalid) {
@@ -181,32 +183,33 @@
   RegsMips regs_mips;
   RegsMips64 regs_mips64;
   MapInfo map_info(0x1000, 0x2000);
-  Elf* invalid_elf = new Elf(new MemoryFake);
+  Elf* invalid_elf = new Elf(nullptr);
   map_info.elf.reset(invalid_elf);
 
   regs_arm.set_pc(0x1500);
   EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
-  EXPECT_EQ(4U, regs_arm.GetPcAdjustment(0x500U, invalid_elf));
+  EXPECT_EQ(2U, regs_arm.GetPcAdjustment(0x500U, invalid_elf));
+  EXPECT_EQ(2U, regs_arm.GetPcAdjustment(0x511U, invalid_elf));
 
   regs_arm64.set_pc(0x1600);
   EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
-  EXPECT_EQ(0U, regs_arm64.GetPcAdjustment(0x600U, invalid_elf));
+  EXPECT_EQ(4U, regs_arm64.GetPcAdjustment(0x600U, invalid_elf));
 
   regs_x86.set_pc(0x1700);
   EXPECT_EQ(0x700U, invalid_elf->GetRelPc(regs_x86.pc(), &map_info));
-  EXPECT_EQ(0U, regs_x86.GetPcAdjustment(0x700U, invalid_elf));
+  EXPECT_EQ(1U, regs_x86.GetPcAdjustment(0x700U, invalid_elf));
 
   regs_x86_64.set_pc(0x1800);
   EXPECT_EQ(0x800U, invalid_elf->GetRelPc(regs_x86_64.pc(), &map_info));
-  EXPECT_EQ(0U, regs_x86_64.GetPcAdjustment(0x800U, invalid_elf));
+  EXPECT_EQ(1U, regs_x86_64.GetPcAdjustment(0x800U, invalid_elf));
 
   regs_mips.set_pc(0x1900);
   EXPECT_EQ(0x900U, invalid_elf->GetRelPc(regs_mips.pc(), &map_info));
-  EXPECT_EQ(0U, regs_mips.GetPcAdjustment(0x900U, invalid_elf));
+  EXPECT_EQ(8U, regs_mips.GetPcAdjustment(0x900U, invalid_elf));
 
   regs_mips64.set_pc(0x1a00);
   EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(regs_mips64.pc(), &map_info));
-  EXPECT_EQ(0U, regs_mips64.GetPcAdjustment(0xa00U, invalid_elf));
+  EXPECT_EQ(8U, regs_mips64.GetPcAdjustment(0xa00U, invalid_elf));
 }
 
 TEST_F(RegsTest, arm_verify_sp_pc) {
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 6c242a5..2b8f0c2 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -285,7 +285,7 @@
       "  #01 pc 00067f00  libarttestd.so (Java_Main_unwindInProcess+10032)\n"
       "  #02 pc 000021a8 (offset 0x2000)  137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
       "boolean)+136)\n"
-      "  #03 pc 0000fe81  anonymous:ee74c000 (boolean Main.bar(boolean)+65)\n"
+      "  #03 pc 0000fe80  anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
       "  #04 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #05 pc 00146ab5  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -300,7 +300,7 @@
       "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
       "  #09 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #10 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #11 pc 0000fe04  anonymous:ee74c000 (int Main.compare(Main, Main)+52)\n"
+      "  #11 pc 0000fe03  anonymous:ee74c000 (int Main.compare(Main, Main)+51)\n"
       "  #12 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #13 pc 00146ab5  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -315,8 +315,8 @@
       "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
       "  #17 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #18 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #19 pc 0000fd3c  anonymous:ee74c000 (int Main.compare(java.lang.Object, "
-      "java.lang.Object)+108)\n"
+      "  #19 pc 0000fd3b  anonymous:ee74c000 (int Main.compare(java.lang.Object, "
+      "java.lang.Object)+107)\n"
       "  #20 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #21 pc 00146ab5  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -331,9 +331,9 @@
       "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
       "  #25 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #26 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #27 pc 0000fbdc  anonymous:ee74c000 (int "
+      "  #27 pc 0000fbdb  anonymous:ee74c000 (int "
       "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
-      "java.util.Comparator)+332)\n"
+      "java.util.Comparator)+331)\n"
       "  #28 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
       "  #29 pc 00146acb  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
@@ -348,7 +348,7 @@
       "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
       "  #33 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #34 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #35 pc 0000f625  anonymous:ee74c000 (boolean Main.foo()+165)\n"
+      "  #35 pc 0000f624  anonymous:ee74c000 (boolean Main.foo()+164)\n"
       "  #36 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #37 pc 00146ab5  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -363,7 +363,7 @@
       "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
       "  #41 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #42 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #43 pc 0000eedc  anonymous:ee74c000 (void Main.runPrimary()+60)\n"
+      "  #43 pc 0000eedb  anonymous:ee74c000 (void Main.runPrimary()+59)\n"
       "  #44 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #45 pc 00146ab5  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -378,7 +378,7 @@
       "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
       "  #49 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #50 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
-      "  #51 pc 0000ac22  anonymous:ee74c000 (void Main.main(java.lang.String[])+98)\n"
+      "  #51 pc 0000ac21  anonymous:ee74c000 (void Main.main(java.lang.String[])+97)\n"
       "  #52 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
       "  #53 pc 00146acb  libartd.so "
       "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
@@ -420,7 +420,7 @@
   EXPECT_EQ(0xffeb52a0U, unwinder.frames()[1].sp);
   EXPECT_EQ(0xec6061a8U, unwinder.frames()[2].pc);
   EXPECT_EQ(0xffeb5ce0U, unwinder.frames()[2].sp);
-  EXPECT_EQ(0xee75be81U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xee75be80U, unwinder.frames()[3].pc);
   EXPECT_EQ(0xffeb5d30U, unwinder.frames()[3].sp);
   EXPECT_EQ(0xf728e4d2U, unwinder.frames()[4].pc);
   EXPECT_EQ(0xffeb5d60U, unwinder.frames()[4].sp);
@@ -436,7 +436,7 @@
   EXPECT_EQ(0xffeb5fb0U, unwinder.frames()[9].sp);
   EXPECT_EQ(0xf72945bdU, unwinder.frames()[10].pc);
   EXPECT_EQ(0xffeb6110U, unwinder.frames()[10].sp);
-  EXPECT_EQ(0xee75be04U, unwinder.frames()[11].pc);
+  EXPECT_EQ(0xee75be03U, unwinder.frames()[11].pc);
   EXPECT_EQ(0xffeb6160U, unwinder.frames()[11].sp);
   EXPECT_EQ(0xf728e4d2U, unwinder.frames()[12].pc);
   EXPECT_EQ(0xffeb6180U, unwinder.frames()[12].sp);
@@ -452,7 +452,7 @@
   EXPECT_EQ(0xffeb63e0U, unwinder.frames()[17].sp);
   EXPECT_EQ(0xf72945bdU, unwinder.frames()[18].pc);
   EXPECT_EQ(0xffeb6530U, unwinder.frames()[18].sp);
-  EXPECT_EQ(0xee75bd3cU, unwinder.frames()[19].pc);
+  EXPECT_EQ(0xee75bd3bU, unwinder.frames()[19].pc);
   EXPECT_EQ(0xffeb6580U, unwinder.frames()[19].sp);
   EXPECT_EQ(0xf728e4d2U, unwinder.frames()[20].pc);
   EXPECT_EQ(0xffeb65b0U, unwinder.frames()[20].sp);
@@ -468,7 +468,7 @@
   EXPECT_EQ(0xffeb6810U, unwinder.frames()[25].sp);
   EXPECT_EQ(0xf72945bdU, unwinder.frames()[26].pc);
   EXPECT_EQ(0xffeb6960U, unwinder.frames()[26].sp);
-  EXPECT_EQ(0xee75bbdcU, unwinder.frames()[27].pc);
+  EXPECT_EQ(0xee75bbdbU, unwinder.frames()[27].pc);
   EXPECT_EQ(0xffeb69b0U, unwinder.frames()[27].sp);
   EXPECT_EQ(0xf728e6a2U, unwinder.frames()[28].pc);
   EXPECT_EQ(0xffeb69f0U, unwinder.frames()[28].sp);
@@ -484,7 +484,7 @@
   EXPECT_EQ(0xffeb6c50U, unwinder.frames()[33].sp);
   EXPECT_EQ(0xf72945bdU, unwinder.frames()[34].pc);
   EXPECT_EQ(0xffeb6dd0U, unwinder.frames()[34].sp);
-  EXPECT_EQ(0xee75b625U, unwinder.frames()[35].pc);
+  EXPECT_EQ(0xee75b624U, unwinder.frames()[35].pc);
   EXPECT_EQ(0xffeb6e20U, unwinder.frames()[35].sp);
   EXPECT_EQ(0xf728e4d2U, unwinder.frames()[36].pc);
   EXPECT_EQ(0xffeb6e50U, unwinder.frames()[36].sp);
@@ -500,7 +500,7 @@
   EXPECT_EQ(0xffeb70a0U, unwinder.frames()[41].sp);
   EXPECT_EQ(0xf72945bdU, unwinder.frames()[42].pc);
   EXPECT_EQ(0xffeb71f0U, unwinder.frames()[42].sp);
-  EXPECT_EQ(0xee75aedcU, unwinder.frames()[43].pc);
+  EXPECT_EQ(0xee75aedbU, unwinder.frames()[43].pc);
   EXPECT_EQ(0xffeb7240U, unwinder.frames()[43].sp);
   EXPECT_EQ(0xf728e4d2U, unwinder.frames()[44].pc);
   EXPECT_EQ(0xffeb72a0U, unwinder.frames()[44].sp);
@@ -516,7 +516,7 @@
   EXPECT_EQ(0xffeb74f0U, unwinder.frames()[49].sp);
   EXPECT_EQ(0xf72945bdU, unwinder.frames()[50].pc);
   EXPECT_EQ(0xffeb7680U, unwinder.frames()[50].sp);
-  EXPECT_EQ(0xee756c22U, unwinder.frames()[51].pc);
+  EXPECT_EQ(0xee756c21U, unwinder.frames()[51].pc);
   EXPECT_EQ(0xffeb76d0U, unwinder.frames()[51].sp);
   EXPECT_EQ(0xf728e6a2U, unwinder.frames()[52].pc);
   EXPECT_EQ(0xffeb76f0U, unwinder.frames()[52].sp);
diff --git a/mkbootimg/Android.bp b/mkbootimg/Android.bp
new file mode 100644
index 0000000..b494346
--- /dev/null
+++ b/mkbootimg/Android.bp
@@ -0,0 +1,32 @@
+// Copyright 2012 The Android Open Source Project
+
+cc_library_headers {
+    name: "libmkbootimg_abi_headers",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+}
+
+cc_library_headers {
+    name: "bootimg_headers",
+    vendor_available: true,
+    export_include_dirs: ["include/bootimg"],
+    host_supported: true,
+    target: {
+        windows: {
+            enabled: true,
+        },
+    },
+}
+
+cc_library {
+    name: "libmkbootimg_abi_check",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "mkbootimg_dummy.cpp",
+    ],
+    header_libs: ["libmkbootimg_abi_headers"],
+    export_header_lib_headers: ["libmkbootimg_abi_headers"],
+}
diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk
index 8661d7d..92e1e27 100644
--- a/mkbootimg/Android.mk
+++ b/mkbootimg/Android.mk
@@ -9,3 +9,12 @@
 LOCAL_MODULE := mkbootimg
 
 include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := unpack_bootimg
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_IS_HOST_MODULE := true
+
+LOCAL_MODULE := unpack_bootimg
+
+include $(BUILD_PREBUILT)
diff --git a/mkbootimg/OWNERS b/mkbootimg/OWNERS
new file mode 100644
index 0000000..39448cf
--- /dev/null
+++ b/mkbootimg/OWNERS
@@ -0,0 +1 @@
+tbao@google.com
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
deleted file mode 100644
index 60834fe..0000000
--- a/mkbootimg/bootimg.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/* tools/mkbootimg/bootimg.h
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
-
-#include <stdint.h>
-
-#ifndef _BOOT_IMAGE_H_
-#define _BOOT_IMAGE_H_
-
-typedef struct boot_img_hdr boot_img_hdr;
-
-#define BOOT_MAGIC "ANDROID!"
-#define BOOT_MAGIC_SIZE 8
-#define BOOT_NAME_SIZE 16
-#define BOOT_ARGS_SIZE 512
-#define BOOT_EXTRA_ARGS_SIZE 1024
-
-struct boot_img_hdr
-{
-    uint8_t magic[BOOT_MAGIC_SIZE];
-
-    uint32_t kernel_size;  /* size in bytes */
-    uint32_t kernel_addr;  /* physical load addr */
-
-    uint32_t ramdisk_size; /* size in bytes */
-    uint32_t ramdisk_addr; /* physical load addr */
-
-    uint32_t second_size;  /* size in bytes */
-    uint32_t second_addr;  /* physical load addr */
-
-    uint32_t tags_addr;    /* physical addr for kernel tags */
-    uint32_t page_size;    /* flash page size we assume */
-    uint32_t unused;       /* reserved for future expansion: MUST be 0 */
-
-    /* operating system version and security patch level; for
-     * version "A.B.C" and patch level "Y-M-D":
-     * ver = A << 14 | B << 7 | C         (7 bits for each of A, B, C)
-     * lvl = ((Y - 2000) & 127) << 4 | M  (7 bits for Y, 4 bits for M)
-     * os_version = ver << 11 | lvl */
-    uint32_t os_version;
-
-    uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
-
-    uint8_t cmdline[BOOT_ARGS_SIZE];
-
-    uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
-
-    /* Supplemental command line data; kept here to maintain
-     * binary compatibility with older versions of mkbootimg */
-    uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
-} __attribute__((packed));
-
-/*
-** +-----------------+ 
-** | boot header     | 1 page
-** +-----------------+
-** | kernel          | n pages  
-** +-----------------+
-** | ramdisk         | m pages  
-** +-----------------+
-** | second stage    | o pages
-** +-----------------+
-**
-** n = (kernel_size + page_size - 1) / page_size
-** m = (ramdisk_size + page_size - 1) / page_size
-** o = (second_size + page_size - 1) / page_size
-**
-** 0. all entities are page_size aligned in flash
-** 1. kernel and ramdisk are required (size != 0)
-** 2. second is optional (second_size == 0 -> no second)
-** 3. load each element (kernel, ramdisk, second) at
-**    the specified physical address (kernel_addr, etc)
-** 4. prepare tags at tag_addr.  kernel_args[] is
-**    appended to the kernel commandline in the tags.
-** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
-** 6. if second_size != 0: jump to second_addr
-**    else: jump to kernel_addr
-*/
-
-#if 0
-typedef struct ptentry ptentry;
-
-struct ptentry {
-    char name[16];      /* asciiz partition name    */
-    unsigned start;     /* starting block number    */
-    unsigned length;    /* length in blocks         */
-    unsigned flags;     /* set to zero              */
-};
-
-/* MSM Partition Table ATAG
-**
-** length: 2 + 7 * n
-** atag:   0x4d534d70
-**         <ptentry> x n
-*/
-#endif
-
-#endif
diff --git a/mkbootimg/include/abi_check/mkbootimg_abi_check.h b/mkbootimg/include/abi_check/mkbootimg_abi_check.h
new file mode 100644
index 0000000..d478aba
--- /dev/null
+++ b/mkbootimg/include/abi_check/mkbootimg_abi_check.h
@@ -0,0 +1,28 @@
+/*
+ * 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 <bootimg/bootimg.h>
+
+// This header has been created for the following reaons:
+//    1) In order for a change in a user defined type to be classified as API /
+//       ABI breaking, it needs to be referenced by an 'exported interface'
+//       (in this case the function mkbootimg_dummy).
+//    2) Since 'mkbootimg_dummy' needs to be exported, we need to have it
+//       exposed through a public header.
+//    3) It is desirable not to pollute bootimg.h with interfaces which are not
+//       'used' in reality by on device binaries. Furthermore, bootimg.h might
+//       be exported by a library in the future, so we must avoid polluting it.
+void mkbootimg_dummy(boot_img_hdr*);
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
new file mode 100644
index 0000000..4311b46
--- /dev/null
+++ b/mkbootimg/include/bootimg/bootimg.h
@@ -0,0 +1,164 @@
+/* tools/mkbootimg/bootimg.h
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdint.h>
+
+#ifndef _BOOT_IMAGE_H_
+#define _BOOT_IMAGE_H_
+
+#define BOOT_MAGIC "ANDROID!"
+#define BOOT_MAGIC_SIZE 8
+#define BOOT_NAME_SIZE 16
+#define BOOT_ARGS_SIZE 512
+#define BOOT_EXTRA_ARGS_SIZE 1024
+
+#define BOOT_HEADER_VERSION_ZERO 0
+/*
+ *  Bootloader expects the structure of boot_img_hdr with header version
+ *  BOOT_HEADER_VERSION_ZERO to be as follows:
+ */
+struct boot_img_hdr_v0 {
+    uint8_t magic[BOOT_MAGIC_SIZE];
+
+    uint32_t kernel_size; /* size in bytes */
+    uint32_t kernel_addr; /* physical load addr */
+
+    uint32_t ramdisk_size; /* size in bytes */
+    uint32_t ramdisk_addr; /* physical load addr */
+
+    uint32_t second_size; /* size in bytes */
+    uint32_t second_addr; /* physical load addr */
+
+    uint32_t tags_addr; /* physical addr for kernel tags */
+    uint32_t page_size; /* flash page size we assume */
+    /*
+     * version for the boot image header.
+     */
+    uint32_t header_version;
+
+    /* operating system version and security patch level; for
+     * version "A.B.C" and patch level "Y-M-D":
+     * ver = A << 14 | B << 7 | C         (7 bits for each of A, B, C)
+     * lvl = ((Y - 2000) & 127) << 4 | M  (7 bits for Y, 4 bits for M)
+     * os_version = ver << 11 | lvl */
+    uint32_t os_version;
+
+    uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
+
+    uint8_t cmdline[BOOT_ARGS_SIZE];
+
+    uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
+
+    /* Supplemental command line data; kept here to maintain
+     * binary compatibility with older versions of mkbootimg */
+    uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
+} __attribute__((packed));
+
+/*
+ * It is expected that callers would explicitly specify which version of the
+ * boot image header they need to use.
+ */
+typedef struct boot_img_hdr_v0 boot_img_hdr;
+
+/* When a boot header is of version BOOT_HEADER_VERSION_ZERO, the structure of boot image is as
+ * follows:
+ *
+ * +-----------------+
+ * | boot header     | 1 page
+ * +-----------------+
+ * | kernel          | n pages
+ * +-----------------+
+ * | ramdisk         | m pages
+ * +-----------------+
+ * | second stage    | o pages
+ * +-----------------+
+ *
+ * n = (kernel_size + page_size - 1) / page_size
+ * m = (ramdisk_size + page_size - 1) / page_size
+ * o = (second_size + page_size - 1) / page_size
+ *
+ * 0. all entities are page_size aligned in flash
+ * 1. kernel and ramdisk are required (size != 0)
+ * 2. second is optional (second_size == 0 -> no second)
+ * 3. load each element (kernel, ramdisk, second) at
+ *    the specified physical address (kernel_addr, etc)
+ * 4. prepare tags at tag_addr.  kernel_args[] is
+ *    appended to the kernel commandline in the tags.
+ * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 6. if second_size != 0: jump to second_addr
+ *    else: jump to kernel_addr
+ */
+
+#define BOOT_HEADER_VERSION_ONE 1
+
+struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
+    uint32_t recovery_dtbo_size;   /* size in bytes for recovery DTBO image */
+    uint64_t recovery_dtbo_offset; /* physical load addr */
+    uint32_t header_size;
+} __attribute__((packed));
+
+/* When the boot image header has a version of BOOT_HEADER_VERSION_ONE, the structure of the boot
+ * image is as follows:
+ *
+ * +-----------------+
+ * | boot header     | 1 page
+ * +-----------------+
+ * | kernel          | n pages
+ * +-----------------+
+ * | ramdisk         | m pages
+ * +-----------------+
+ * | second stage    | o pages
+ * +-----------------+
+ * | recovery dtbo   | p pages
+ * +-----------------+
+ * n = (kernel_size + page_size - 1) / page_size
+ * m = (ramdisk_size + page_size - 1) / page_size
+ * o = (second_size + page_size - 1) / page_size
+ * p = (recovery_dtbo_size + page_size - 1) / page_size
+ *
+ * 0. all entities are page_size aligned in flash
+ * 1. kernel and ramdisk are required (size != 0)
+ * 2. recovery_dtbo is required for recovery.img in non-A/B devices(recovery_dtbo_size != 0)
+ * 3. second is optional (second_size == 0 -> no second)
+ * 4. load each element (kernel, ramdisk, second, recovery_dtbo) at
+ *    the specified physical address (kernel_addr, etc)
+ * 5. prepare tags at tag_addr.  kernel_args[] is
+ *    appended to the kernel commandline in the tags.
+ * 6. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 7. if second_size != 0: jump to second_addr
+ *    else: jump to kernel_addr
+ */
+
+#if 0
+typedef struct ptentry ptentry;
+
+struct ptentry {
+    char name[16];      /* asciiz partition name    */
+    unsigned start;     /* starting block number    */
+    unsigned length;    /* length in blocks         */
+    unsigned flags;     /* set to zero              */
+};
+
+/* MSM Partition Table ATAG
+**
+** length: 2 + 7 * n
+** atag:   0x4d534d70
+**         <ptentry> x n
+*/
+#endif
+
+#endif
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index 5a13da2..ac20d05 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -57,7 +57,7 @@
         args.base + args.second_offset,                 # physical load addr
         args.base + args.tags_offset,                   # physical addr for kernel tags
         args.pagesize,                                  # flash page size we assume
-        0,                                              # future expansion: MUST be 0
+        args.header_version,                            # version of bootimage header
         (args.os_version << 11) | args.os_patch_level)) # os version and patch level
     args.output.write(pack('16s', args.board.encode())) # asciiz product name
     args.output.write(pack('512s', args.cmdline[:512].encode()))
@@ -66,10 +66,20 @@
     update_sha(sha, args.kernel)
     update_sha(sha, args.ramdisk)
     update_sha(sha, args.second)
+
+    if args.header_version > 0:
+        update_sha(sha, args.recovery_dtbo)
+
     img_id = pack('32s', sha.digest())
 
     args.output.write(img_id)
     args.output.write(pack('1024s', args.cmdline[512:].encode()))
+
+    if args.header_version > 0:
+        args.output.write(pack('I', filesize(args.recovery_dtbo)))           # size in bytes
+        args.output.write(pack('Q', args.base + args.recovery_dtbo_offset))  # physical load addr
+        args.output.write(pack('I', args.output.tell() + 4))                 # size of boot header
+
     pad_file(args.output, args.pagesize)
     return img_id
 
@@ -132,6 +142,7 @@
                         required=True)
     parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
     parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
+    parser.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
     parser.add_argument('--cmdline', help='extra arguments to be passed on the '
                         'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
     parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
@@ -139,6 +150,8 @@
     parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
     parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
                         default=0x00f00000)
+    parser.add_argument('--recovery_dtbo_offset', help='recovery dtbo offset', type=parse_int,
+                        default=0x0f000000)
     parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
                         default=0)
     parser.add_argument('--os_patch_level', help='operating system patch level',
@@ -150,6 +163,7 @@
                         choices=[2**i for i in range(11,15)], default=2048)
     parser.add_argument('--id', help='print the image ID on standard output',
                         action='store_true')
+    parser.add_argument('--header_version', help='boot image header version', type=parse_int, default=0)
     parser.add_argument('-o', '--output', help='output file name', type=FileType('wb'),
                         required=True)
     return parser.parse_args()
@@ -160,6 +174,8 @@
     write_padded_file(args.output, args.ramdisk, args.pagesize)
     write_padded_file(args.output, args.second, args.pagesize)
 
+    if args.header_version > 0:
+        write_padded_file(args.output, args.recovery_dtbo, args.pagesize)
 
 def main():
     args = parse_cmdline()
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/mkbootimg/mkbootimg_dummy.cpp
new file mode 100644
index 0000000..410d379
--- /dev/null
+++ b/mkbootimg/mkbootimg_dummy.cpp
@@ -0,0 +1,24 @@
+/*
+ * 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 <abi_check/mkbootimg_abi_check.h>
+
+void mkbootimg_dummy(boot_img_hdr* hdr) {
+    // TODO: Hack to trigger abi checks, remove this.
+    if (hdr) {
+        hdr--;
+    }
+}
diff --git a/mkbootimg/unpack_bootimg b/mkbootimg/unpack_bootimg
new file mode 100755
index 0000000..8e42ec0
--- /dev/null
+++ b/mkbootimg/unpack_bootimg
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# Copyright 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.
+
+"""unpacks the bootimage.
+
+Extracts the kernel, ramdisk, second bootloader and recovery dtbo images.
+"""
+
+from __future__ import print_function
+from argparse import ArgumentParser, FileType
+from struct import unpack
+import os
+
+
+def create_out_dir(dir_path):
+    """creates a directory 'dir_path' if it does not exist"""
+    if not os.path.exists(dir_path):
+        os.makedirs(dir_path)
+
+
+def extract_image(offset, size, bootimage, extracted_image_name):
+    """extracts an image from the bootimage"""
+    bootimage.seek(offset)
+    with open(extracted_image_name, 'wb') as file_out:
+        file_out.write(bootimage.read(size))
+
+
+def get_number_of_pages(image_size, page_size):
+    """calculates the number of pages required for the image"""
+    return (image_size + page_size - 1) / page_size
+
+
+def unpack_bootimage(args):
+    """extracts kernel, ramdisk, second bootloader and recovery dtbo"""
+    boot_magic = unpack('8s', args.boot_img.read(8))
+    print('boot_magic: %s' % boot_magic)
+    kernel_ramdisk_second_info = unpack('10I', args.boot_img.read(10 * 4))
+    print('kernel_size: %s' % kernel_ramdisk_second_info[0])
+    print('kernel load address: %s' % kernel_ramdisk_second_info[1])
+    print('ramdisk size: %s' % kernel_ramdisk_second_info[2])
+    print('ramdisk load address: %s' % kernel_ramdisk_second_info[3])
+    print('second bootloader size: %s' % kernel_ramdisk_second_info[4])
+    print('second bootloader load address: %s' % kernel_ramdisk_second_info[5])
+    print('kernel tags load address: %s' % kernel_ramdisk_second_info[6])
+    print('page size: %s' % kernel_ramdisk_second_info[7])
+    print('boot image header version: %s' % kernel_ramdisk_second_info[8])
+    print('os version and patch level: %s' % kernel_ramdisk_second_info[9])
+
+    product_name = unpack('16s', args.boot_img.read(16))
+    print('product name: %s' % product_name)
+    cmdline = unpack('512s', args.boot_img.read(512))
+    print('command line args: %s' % cmdline)
+
+    args.boot_img.read(32)  # ignore SHA
+
+    extra_cmdline = unpack('1024s', args.boot_img.read(1024))
+    print('additional command line args: %s' % extra_cmdline)
+
+    kernel_size = kernel_ramdisk_second_info[0]
+    ramdisk_size = kernel_ramdisk_second_info[2]
+    second_size = kernel_ramdisk_second_info[4]
+    page_size = kernel_ramdisk_second_info[7]
+    version = kernel_ramdisk_second_info[8]
+    if version > 0:
+        recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
+        print('recovery dtbo size: %s' % recovery_dtbo_size)
+        recovery_dtbo_address = unpack('Q', args.boot_img.read(8))[0]
+        print('recovery dtbo load address: %s' % recovery_dtbo_address)
+        boot_header_size = unpack('I', args.boot_img.read(4))[0]
+        print('boot header size: %s' % boot_header_size)
+    else:
+        recovery_dtbo_size = 0
+
+    # The first page contains the boot header
+    num_header_pages = 1
+
+    num_kernel_pages = get_number_of_pages(kernel_size, page_size)
+    kernel_offset = page_size * num_header_pages  # header occupies a page
+    image_info_list = [(kernel_offset, kernel_size, 'kernel')]
+
+    num_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size)
+    ramdisk_offset = page_size * (num_header_pages + num_kernel_pages
+                                 ) # header + kernel
+    image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
+
+    num_second_pages = get_number_of_pages(second_size, page_size)
+    second_offset = page_size * (
+        num_header_pages + num_kernel_pages + num_ramdisk_pages
+    )  # header + kernel + ramdisk
+    image_info_list.append((second_offset, second_size, 'second'))
+
+    if recovery_dtbo_size > 0:
+        dtbo_offset = page_size * (num_header_pages + num_kernel_pages +
+                                   num_ramdisk_pages + num_second_pages)
+        image_info_list.append((dtbo_offset, recovery_dtbo_size,
+                                'recovery_dtbo'))
+
+    for image_info in image_info_list:
+        extract_image(image_info[0], image_info[1], args.boot_img,
+                      os.path.join(args.out, image_info[2]))
+
+
+def parse_cmdline():
+    """parse command line arguments"""
+    parser = ArgumentParser(
+        description='Unpacks boot.img/recovery.img, extracts the kernel,'
+        'ramdisk, second bootloader and recovery dtbo')
+    parser.add_argument(
+        '--boot_img',
+        help='path to boot image',
+        type=FileType('rb'),
+        required=True)
+    parser.add_argument('--out', help='path to out binaries', default='out')
+    return parser.parse_args()
+
+
+def main():
+    """parse arguments and unpack boot image"""
+    args = parse_cmdline()
+    create_out_dir(args.out)
+    unpack_bootimage(args)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 94465f4..ba96cc8 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -218,13 +218,20 @@
 namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%/hw
 namespace.vndk.asan.permitted.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%/hw
 
+# The "vndk" namespace links to "default" namespace for LLNDK libs and links to
+# "sphal" namespace for vendor libs.  The ordering matters.  The "default"
+# namespace has higher priority than the "sphal" namespace.
+namespace.vndk.links = default,sphal
+
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the default namespace. This is possible since their ABI is stable across
 # Android releases.
-namespace.vndk.links = default
 namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
+# Allow VNDK-SP extensions to use vendor libraries
+namespace.vndk.link.sphal.allow_all_shared_libs = true
+
 ###############################################################################
 # Namespace config for vendor processes. In O, no restriction is enforced for
 # them. However, in O-MR1, access to /system/${LIB} will not be allowed to