Merge "Add basic support for remounting ext4 userdata into checkpoint"
diff --git a/adb/Android.bp b/adb/Android.bp
index d605907..bd1f124 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -134,6 +134,7 @@
     "transport_fd.cpp",
     "transport_local.cpp",
     "transport_usb.cpp",
+    "types.cpp",
 ]
 
 libadb_posix_srcs = [
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 1abae87..a9ad805 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -117,14 +117,18 @@
     }
 };
 
+template <class Payload>
 struct IoBlock {
     bool pending = false;
     struct iocb control = {};
-    std::shared_ptr<Block> payload;
+    Payload payload;
 
     TransferId id() const { return TransferId::from_value(control.aio_data); }
 };
 
+using IoReadBlock = IoBlock<Block>;
+using IoWriteBlock = IoBlock<std::shared_ptr<Block>>;
+
 struct ScopedAioContext {
     ScopedAioContext() = default;
     ~ScopedAioContext() { reset(); }
@@ -208,16 +212,17 @@
 
     virtual bool Write(std::unique_ptr<apacket> packet) override final {
         LOG(DEBUG) << "USB write: " << dump_header(&packet->msg);
-        Block header(sizeof(packet->msg));
-        memcpy(header.data(), &packet->msg, sizeof(packet->msg));
+        auto header = std::make_shared<Block>(sizeof(packet->msg));
+        memcpy(header->data(), &packet->msg, sizeof(packet->msg));
 
         std::lock_guard<std::mutex> lock(write_mutex_);
-        write_requests_.push_back(CreateWriteBlock(std::move(header), next_write_id_++));
+        write_requests_.push_back(
+                CreateWriteBlock(std::move(header), 0, sizeof(packet->msg), next_write_id_++));
         if (!packet->payload.empty()) {
             // The kernel attempts to allocate a contiguous block of memory for each write,
             // which can fail if the write is large and the kernel heap is fragmented.
             // Split large writes into smaller chunks to avoid this.
-            std::shared_ptr<Block> payload = std::make_shared<Block>(std::move(packet->payload));
+            auto payload = std::make_shared<Block>(std::move(packet->payload));
             size_t offset = 0;
             size_t len = payload->size();
 
@@ -464,16 +469,20 @@
         worker_thread_.join();
     }
 
-    void PrepareReadBlock(IoBlock* block, uint64_t id) {
+    void PrepareReadBlock(IoReadBlock* block, uint64_t id) {
         block->pending = false;
-        block->payload = std::make_shared<Block>(kUsbReadSize);
+        if (block->payload.capacity() >= kUsbReadSize) {
+            block->payload.resize(kUsbReadSize);
+        } else {
+            block->payload = Block(kUsbReadSize);
+        }
         block->control.aio_data = static_cast<uint64_t>(TransferId::read(id));
-        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data());
-        block->control.aio_nbytes = block->payload->size();
+        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
+        block->control.aio_nbytes = block->payload.size();
     }
 
-    IoBlock CreateReadBlock(uint64_t id) {
-        IoBlock block;
+    IoReadBlock CreateReadBlock(uint64_t id) {
+        IoReadBlock block;
         PrepareReadBlock(&block, id);
         block.control.aio_rw_flags = 0;
         block.control.aio_lio_opcode = IOCB_CMD_PREAD;
@@ -518,9 +527,9 @@
 
     void HandleRead(TransferId id, int64_t size) {
         uint64_t read_idx = id.id % kUsbReadQueueDepth;
-        IoBlock* block = &read_requests_[read_idx];
+        IoReadBlock* block = &read_requests_[read_idx];
         block->pending = false;
-        block->payload->resize(size);
+        block->payload.resize(size);
 
         // Notification for completed reads can be received out of order.
         if (block->id().id != needed_read_id_) {
@@ -531,7 +540,7 @@
 
         for (uint64_t id = needed_read_id_;; ++id) {
             size_t read_idx = id % kUsbReadQueueDepth;
-            IoBlock* current_block = &read_requests_[read_idx];
+            IoReadBlock* current_block = &read_requests_[read_idx];
             if (current_block->pending) {
                 break;
             }
@@ -540,19 +549,19 @@
         }
     }
 
-    void ProcessRead(IoBlock* block) {
-        if (!block->payload->empty()) {
+    void ProcessRead(IoReadBlock* block) {
+        if (!block->payload.empty()) {
             if (!incoming_header_.has_value()) {
-                CHECK_EQ(sizeof(amessage), block->payload->size());
-                amessage msg;
-                memcpy(&msg, block->payload->data(), sizeof(amessage));
+                CHECK_EQ(sizeof(amessage), block->payload.size());
+                amessage& msg = incoming_header_.emplace();
+                memcpy(&msg, block->payload.data(), sizeof(msg));
                 LOG(DEBUG) << "USB read:" << dump_header(&msg);
                 incoming_header_ = msg;
             } else {
                 size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
-                Block payload = std::move(*block->payload);
+                Block payload = std::move(block->payload);
                 CHECK_LE(payload.size(), bytes_left);
-                incoming_payload_.append(std::make_unique<Block>(std::move(payload)));
+                incoming_payload_.append(std::move(payload));
             }
 
             if (incoming_header_->data_length == incoming_payload_.size()) {
@@ -560,11 +569,15 @@
                 packet->msg = *incoming_header_;
 
                 // TODO: Make apacket contain an IOVector so we don't have to coalesce.
-                packet->payload = incoming_payload_.coalesce();
+                packet->payload = std::move(incoming_payload_).coalesce();
                 read_callback_(this, std::move(packet));
 
                 incoming_header_.reset();
-                incoming_payload_.clear();
+                // reuse the capacity of the incoming payload while we can.
+                auto free_block = incoming_payload_.clear();
+                if (block->payload.capacity() == 0) {
+                    block->payload = std::move(free_block);
+                }
             }
         }
 
@@ -572,7 +585,7 @@
         SubmitRead(block);
     }
 
-    bool SubmitRead(IoBlock* block) {
+    bool SubmitRead(IoReadBlock* block) {
         block->pending = true;
         struct iocb* iocb = &block->control;
         if (io_submit(aio_context_.get(), 1, &iocb) != 1) {
@@ -594,7 +607,7 @@
         std::lock_guard<std::mutex> lock(write_mutex_);
         auto it =
                 std::find_if(write_requests_.begin(), write_requests_.end(), [id](const auto& req) {
-                    return static_cast<uint64_t>(req->id()) == static_cast<uint64_t>(id);
+                    return static_cast<uint64_t>(req.id()) == static_cast<uint64_t>(id);
                 });
         CHECK(it != write_requests_.end());
 
@@ -605,27 +618,26 @@
         SubmitWrites();
     }
 
-    std::unique_ptr<IoBlock> CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset,
-                                              size_t len, uint64_t id) {
-        auto block = std::make_unique<IoBlock>();
-        block->payload = std::move(payload);
-        block->control.aio_data = static_cast<uint64_t>(TransferId::write(id));
-        block->control.aio_rw_flags = 0;
-        block->control.aio_lio_opcode = IOCB_CMD_PWRITE;
-        block->control.aio_reqprio = 0;
-        block->control.aio_fildes = write_fd_.get();
-        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data() + offset);
-        block->control.aio_nbytes = len;
-        block->control.aio_offset = 0;
-        block->control.aio_flags = IOCB_FLAG_RESFD;
-        block->control.aio_resfd = worker_event_fd_.get();
+    IoWriteBlock CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset, size_t len,
+                                  uint64_t id) {
+        auto block = IoWriteBlock();
+        block.payload = std::move(payload);
+        block.control.aio_data = static_cast<uint64_t>(TransferId::write(id));
+        block.control.aio_rw_flags = 0;
+        block.control.aio_lio_opcode = IOCB_CMD_PWRITE;
+        block.control.aio_reqprio = 0;
+        block.control.aio_fildes = write_fd_.get();
+        block.control.aio_buf = reinterpret_cast<uintptr_t>(block.payload->data() + offset);
+        block.control.aio_nbytes = len;
+        block.control.aio_offset = 0;
+        block.control.aio_flags = IOCB_FLAG_RESFD;
+        block.control.aio_resfd = worker_event_fd_.get();
         return block;
     }
 
-    std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
-        std::shared_ptr<Block> block = std::make_shared<Block>(std::move(payload));
-        size_t len = block->size();
-        return CreateWriteBlock(std::move(block), 0, len, id);
+    IoWriteBlock CreateWriteBlock(Block&& payload, uint64_t id) {
+        size_t len = payload.size();
+        return CreateWriteBlock(std::make_shared<Block>(std::move(payload)), 0, len, id);
     }
 
     void SubmitWrites() REQUIRES(write_mutex_) {
@@ -642,9 +654,9 @@
 
         struct iocb* iocbs[kUsbWriteQueueDepth];
         for (int i = 0; i < writes_to_submit; ++i) {
-            CHECK(!write_requests_[writes_submitted_ + i]->pending);
-            write_requests_[writes_submitted_ + i]->pending = true;
-            iocbs[i] = &write_requests_[writes_submitted_ + i]->control;
+            CHECK(!write_requests_[writes_submitted_ + i].pending);
+            write_requests_[writes_submitted_ + i].pending = true;
+            iocbs[i] = &write_requests_[writes_submitted_ + i].control;
             LOG(VERBOSE) << "submitting write_request " << static_cast<void*>(iocbs[i]);
         }
 
@@ -689,7 +701,7 @@
     std::optional<amessage> incoming_header_;
     IOVector incoming_payload_;
 
-    std::array<IoBlock, kUsbReadQueueDepth> read_requests_;
+    std::array<IoReadBlock, kUsbReadQueueDepth> read_requests_;
     IOVector read_data_;
 
     // ID of the next request that we're going to send out.
@@ -699,7 +711,7 @@
     size_t needed_read_id_ = 0;
 
     std::mutex write_mutex_;
-    std::deque<std::unique_ptr<IoBlock>> write_requests_ GUARDED_BY(write_mutex_);
+    std::deque<IoWriteBlock> write_requests_ GUARDED_BY(write_mutex_);
     size_t next_write_id_ GUARDED_BY(write_mutex_) = 0;
     size_t writes_submitted_ GUARDED_BY(write_mutex_) = 0;
 
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index e78530c..7d5bf17 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -125,8 +125,7 @@
         if (rc > 0 && static_cast<size_t>(rc) == s->packet_queue.size()) {
             s->packet_queue.clear();
         } else if (rc > 0) {
-            // TODO: Implement a faster drop_front?
-            s->packet_queue.take_front(rc);
+            s->packet_queue.drop_front(rc);
             fdevent_add(s->fde, FDE_WRITE);
             return SocketFlushResult::TryAgain;
         } else if (rc == -1 && errno == EAGAIN) {
diff --git a/adb/transport_fd.cpp b/adb/transport_fd.cpp
index a93e68a..8d2ad66 100644
--- a/adb/transport_fd.cpp
+++ b/adb/transport_fd.cpp
@@ -93,8 +93,8 @@
 
                 if (pfds[0].revents & POLLIN) {
                     // TODO: Should we be getting blocks from a free list?
-                    auto block = std::make_unique<IOVector::block_type>(MAX_PAYLOAD);
-                    rc = adb_read(fd_.get(), &(*block)[0], block->size());
+                    auto block = IOVector::block_type(MAX_PAYLOAD);
+                    rc = adb_read(fd_.get(), &block[0], block.size());
                     if (rc == -1) {
                         *error = std::string("read failed: ") + strerror(errno);
                         return;
@@ -102,7 +102,7 @@
                         *error = "read failed: EOF";
                         return;
                     }
-                    block->resize(rc);
+                    block.resize(rc);
                     read_buffer_.append(std::move(block));
 
                     if (!read_header_ && read_buffer_.size() >= sizeof(amessage)) {
@@ -116,7 +116,7 @@
                         auto data_chain = read_buffer_.take_front(read_header_->data_length);
 
                         // TODO: Make apacket carry around a IOVector instead of coalescing.
-                        auto payload = data_chain.coalesce<apacket::payload_type>();
+                        auto payload = std::move(data_chain).coalesce();
                         auto packet = std::make_unique<apacket>();
                         packet->msg = *read_header_;
                         packet->payload = std::move(payload);
@@ -184,8 +184,7 @@
             return WriteResult::Error;
         }
 
-        // TODO: Implement a more efficient drop_front?
-        write_buffer_.take_front(rc);
+        write_buffer_.drop_front(rc);
         writable_ = write_buffer_.empty();
         if (write_buffer_.empty()) {
             return WriteResult::Completed;
@@ -199,10 +198,10 @@
         std::lock_guard<std::mutex> lock(write_mutex_);
         const char* header_begin = reinterpret_cast<const char*>(&packet->msg);
         const char* header_end = header_begin + sizeof(packet->msg);
-        auto header_block = std::make_unique<IOVector::block_type>(header_begin, header_end);
+        auto header_block = IOVector::block_type(header_begin, header_end);
         write_buffer_.append(std::move(header_block));
         if (!packet->payload.empty()) {
-            write_buffer_.append(std::make_unique<IOVector::block_type>(std::move(packet->payload)));
+            write_buffer_.append(std::move(packet->payload));
         }
 
         WriteResult result = DispatchWrites();
diff --git a/adb/types.cpp b/adb/types.cpp
new file mode 100644
index 0000000..26b77ab
--- /dev/null
+++ b/adb/types.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2019 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 "types.h"
+
+IOVector& IOVector::operator=(IOVector&& move) noexcept {
+    chain_ = std::move(move.chain_);
+    chain_length_ = move.chain_length_;
+    begin_offset_ = move.begin_offset_;
+    start_index_ = move.start_index_;
+
+    move.clear();
+    return *this;
+}
+
+IOVector::block_type IOVector::clear() {
+    chain_length_ = 0;
+    begin_offset_ = 0;
+    start_index_ = 0;
+    block_type res;
+    if (!chain_.empty()) {
+        res = std::move(chain_.back());
+    }
+    chain_.clear();
+    return res;
+}
+
+void IOVector::drop_front(IOVector::size_type len) {
+    if (len == 0) {
+        return;
+    }
+    if (len == size()) {
+        clear();
+        return;
+    }
+    CHECK_LT(len, size());
+
+    auto dropped = 0u;
+    while (dropped < len) {
+        const auto next = chain_[start_index_].size() - begin_offset_;
+        if (dropped + next < len) {
+            pop_front_block();
+            dropped += next;
+        } else {
+            const auto taken = len - dropped;
+            begin_offset_ += taken;
+            break;
+        }
+    }
+}
+
+IOVector IOVector::take_front(IOVector::size_type len) {
+    if (len == 0) {
+        return {};
+    }
+    if (len == size()) {
+        return std::move(*this);
+    }
+
+    CHECK_GE(size(), len);
+    IOVector res;
+    // first iterate over the blocks that completely go into the other vector
+    while (chain_[start_index_].size() - begin_offset_ <= len) {
+        chain_length_ -= chain_[start_index_].size();
+        len -= chain_[start_index_].size() - begin_offset_;
+        if (chain_[start_index_].size() > begin_offset_) {
+            res.append(std::move(chain_[start_index_]));
+            if (begin_offset_) {
+                res.begin_offset_ = std::exchange(begin_offset_, 0);
+            }
+        } else {
+            begin_offset_ = 0;
+        }
+        ++start_index_;
+    }
+
+    if (len > 0) {
+        // what's left is a single buffer that needs to be split between the |res| and |this|
+        // we know that it has to be split - there was a check for the case when it has to
+        // go away as a whole.
+        if (begin_offset_ != 0 || len < chain_[start_index_].size() / 2) {
+            // let's memcpy the data out
+            block_type block(chain_[start_index_].begin() + begin_offset_,
+                             chain_[start_index_].begin() + begin_offset_ + len);
+            res.append(std::move(block));
+            begin_offset_ += len;
+        } else {
+            CHECK_EQ(begin_offset_, 0u);
+            // move out the internal buffer out and copy only the tail of it back in
+            block_type block(chain_[start_index_].begin() + len, chain_[start_index_].end());
+            chain_length_ -= chain_[start_index_].size();
+            chain_[start_index_].resize(len);
+            res.append(std::move(chain_[start_index_]));
+            chain_length_ += block.size();
+            chain_[start_index_] = std::move(block);
+        }
+    }
+    return res;
+}
+
+void IOVector::trim_front() {
+    if ((begin_offset_ == 0 && start_index_ == 0) || chain_.empty()) {
+        return;
+    }
+    block_type& first_block = chain_[start_index_];
+    if (begin_offset_ == first_block.size()) {
+        ++start_index_;
+    } else {
+        memmove(first_block.data(), first_block.data() + begin_offset_,
+                first_block.size() - begin_offset_);
+        first_block.resize(first_block.size() - begin_offset_);
+    }
+    chain_length_ -= begin_offset_;
+    begin_offset_ = 0;
+    trim_chain_front();
+}
+
+void IOVector::trim_chain_front() {
+    if (start_index_) {
+        chain_.erase(chain_.begin(), chain_.begin() + start_index_);
+        start_index_ = 0;
+    }
+}
+
+void IOVector::pop_front_block() {
+    chain_length_ -= chain_[start_index_].size();
+    begin_offset_ = 0;
+    chain_[start_index_].clear();
+    ++start_index_;
+    if (start_index_ > std::max<size_t>(4, chain_.size() / 2)) {
+        trim_chain_front();
+    }
+}
+
+IOVector::block_type IOVector::coalesce() && {
+    // Destructive coalesce() may optimize for several cases when it doesn't need to allocate
+    // new buffer, or even return one of the existing blocks as is. The only guarantee is that
+    // after this call the IOVector is in some valid state. Nothing is guaranteed about the
+    // specifics.
+    if (size() == 0) {
+        return {};
+    }
+    if (begin_offset_ == chain_[start_index_].size() && chain_.size() == start_index_ + 2) {
+        chain_length_ -= chain_.back().size();
+        auto res = std::move(chain_.back());
+        chain_.pop_back();
+        return res;
+    }
+    if (chain_.size() == start_index_ + 1) {
+        chain_length_ -= chain_.back().size();
+        auto res = std::move(chain_.back());
+        chain_.pop_back();
+        if (begin_offset_ != 0) {
+            memmove(res.data(), res.data() + begin_offset_, res.size() - begin_offset_);
+            res.resize(res.size() - begin_offset_);
+            begin_offset_ = 0;
+        }
+        return res;
+    }
+    if (auto& firstBuffer = chain_[start_index_]; firstBuffer.capacity() >= size()) {
+        auto res = std::move(chain_[start_index_]);
+        auto size = res.size();
+        chain_length_ -= size;
+        if (begin_offset_ != 0) {
+            memmove(res.data(), res.data() + begin_offset_, res.size() - begin_offset_);
+            size -= begin_offset_;
+            begin_offset_ = 0;
+        }
+        for (auto i = start_index_ + 1; i < chain_.size(); ++i) {
+            memcpy(res.data() + size, chain_[i].data(), chain_[i].size());
+            size += chain_[i].size();
+        }
+        res.resize(size);
+        ++start_index_;
+        return res;
+    }
+    return const_cast<const IOVector*>(this)->coalesce<>();
+}
+
+std::vector<adb_iovec> IOVector::iovecs() const {
+    std::vector<adb_iovec> result;
+    result.reserve(chain_.size() - start_index_);
+    iterate_blocks([&result](const char* data, size_t len) {
+        adb_iovec iov;
+        iov.iov_base = const_cast<char*>(data);
+        iov.iov_len = len;
+        result.emplace_back(iov);
+    });
+
+    return result;
+}
diff --git a/adb/types.h b/adb/types.h
index 8bd66be..6b00224 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -19,9 +19,7 @@
 #include <string.h>
 
 #include <algorithm>
-#include <deque>
 #include <memory>
-#include <type_traits>
 #include <utility>
 #include <vector>
 
@@ -33,7 +31,7 @@
 struct Block {
     using iterator = char*;
 
-    Block() {}
+    Block() = default;
 
     explicit Block(size_t size) { allocate(size); }
 
@@ -43,24 +41,21 @@
     }
 
     Block(const Block& copy) = delete;
-    Block(Block&& move) noexcept {
-        std::swap(data_, move.data_);
-        std::swap(capacity_, move.capacity_);
-        std::swap(size_, move.size_);
-    }
+    Block(Block&& move) noexcept
+        : data_(std::exchange(move.data_, nullptr)),
+          capacity_(std::exchange(move.capacity_, 0)),
+          size_(std::exchange(move.size_, 0)) {}
 
     Block& operator=(const Block& copy) = delete;
     Block& operator=(Block&& move) noexcept {
         clear();
-
-        std::swap(data_, move.data_);
-        std::swap(capacity_, move.capacity_);
-        std::swap(size_, move.size_);
-
+        data_ = std::exchange(move.data_, nullptr);
+        capacity_ = std::exchange(move.capacity_, 0);
+        size_ = std::exchange(move.size_, 0);
         return *this;
     }
 
-    ~Block() { clear(); }
+    ~Block() = default;
 
     void resize(size_t new_size) {
         if (!data_) {
@@ -144,146 +139,63 @@
     using block_type = Block;
     using size_type = size_t;
 
-    IOVector() {}
+    IOVector() = default;
 
-    explicit IOVector(std::unique_ptr<block_type> block) {
-        append(std::move(block));
-    }
+    explicit IOVector(block_type&& block) { append(std::move(block)); }
 
     IOVector(const IOVector& copy) = delete;
     IOVector(IOVector&& move) noexcept : IOVector() { *this = std::move(move); }
 
     IOVector& operator=(const IOVector& copy) = delete;
-    IOVector& operator=(IOVector&& move) noexcept {
-        chain_ = std::move(move.chain_);
-        chain_length_ = move.chain_length_;
-        begin_offset_ = move.begin_offset_;
-        end_offset_ = move.end_offset_;
+    IOVector& operator=(IOVector&& move) noexcept;
 
-        move.chain_.clear();
-        move.chain_length_ = 0;
-        move.begin_offset_ = 0;
-        move.end_offset_ = 0;
-
-        return *this;
-    }
-
-    size_type size() const { return chain_length_ - begin_offset_ - end_offset_; }
+    size_type size() const { return chain_length_ - begin_offset_; }
     bool empty() const { return size() == 0; }
 
-    void clear() {
-        chain_length_ = 0;
-        begin_offset_ = 0;
-        end_offset_ = 0;
-        chain_.clear();
-    }
+    // Return the last block so the caller can still reuse its allocated capacity
+    // or it can be simply ignored.
+    block_type clear();
+
+    void drop_front(size_type len);
 
     // Split the first |len| bytes out of this chain into its own.
-    IOVector take_front(size_type len) {
-        IOVector head;
-
-        if (len == 0) {
-            return head;
-        }
-        CHECK_GE(size(), len);
-
-        std::shared_ptr<const block_type> first_block = chain_.front();
-        CHECK_GE(first_block->size(), begin_offset_);
-        head.append_shared(std::move(first_block));
-        head.begin_offset_ = begin_offset_;
-
-        while (head.size() < len) {
-            pop_front_block();
-            CHECK(!chain_.empty());
-
-            head.append_shared(chain_.front());
-        }
-
-        if (head.size() == len) {
-            // Head takes full ownership of the last block it took.
-            head.end_offset_ = 0;
-            begin_offset_ = 0;
-            pop_front_block();
-        } else {
-            // Head takes partial ownership of the last block it took.
-            size_t bytes_taken = head.size() - len;
-            head.end_offset_ = bytes_taken;
-            CHECK_GE(chain_.front()->size(), bytes_taken);
-            begin_offset_ = chain_.front()->size() - bytes_taken;
-        }
-
-        return head;
-    }
+    IOVector take_front(size_type len);
 
     // Add a nonempty block to the chain.
-    // The end of the chain must be a complete block (i.e. end_offset_ == 0).
-    void append(std::unique_ptr<const block_type> block) {
-        if (block->size() == 0) {
+    void append(block_type&& block) {
+        if (block.size() == 0) {
             return;
         }
-
-        CHECK_EQ(0ULL, end_offset_);
-        chain_length_ += block->size();
+        CHECK_NE(0ULL, block.size());
+        chain_length_ += block.size();
         chain_.emplace_back(std::move(block));
     }
 
-    void append(block_type&& block) { append(std::make_unique<block_type>(std::move(block))); }
-
-    void trim_front() {
-        if (begin_offset_ == 0) {
-            return;
-        }
-
-        const block_type* first_block = chain_.front().get();
-        auto copy = std::make_unique<block_type>(first_block->size() - begin_offset_);
-        memcpy(copy->data(), first_block->data() + begin_offset_, copy->size());
-        chain_.front() = std::move(copy);
-
-        chain_length_ -= begin_offset_;
-        begin_offset_ = 0;
-    }
+    void trim_front();
 
   private:
-    // append, except takes a shared_ptr.
-    // Private to prevent exterior mutation of blocks.
-    void append_shared(std::shared_ptr<const block_type> block) {
-        CHECK_NE(0ULL, block->size());
-        CHECK_EQ(0ULL, end_offset_);
-        chain_length_ += block->size();
-        chain_.emplace_back(std::move(block));
-    }
+    void trim_chain_front();
 
     // Drop the front block from the chain, and update chain_length_ appropriately.
-    void pop_front_block() {
-        chain_length_ -= chain_.front()->size();
-        begin_offset_ = 0;
-        chain_.pop_front();
-    }
+    void pop_front_block();
 
     // Iterate over the blocks with a callback with an operator()(const char*, size_t).
     template <typename Fn>
     void iterate_blocks(Fn&& callback) const {
-        if (chain_.size() == 0) {
+        if (size() == 0) {
             return;
         }
 
-        for (size_t i = 0; i < chain_.size(); ++i) {
-            const std::shared_ptr<const block_type>& block = chain_.at(i);
-            const char* begin = block->data();
-            size_t length = block->size();
+        for (size_t i = start_index_; i < chain_.size(); ++i) {
+            const auto& block = chain_[i];
+            const char* begin = block.data();
+            size_t length = block.size();
 
-            // Note that both of these conditions can be true if there's only one block.
-            if (i == 0) {
-                CHECK_GE(block->size(), begin_offset_);
+            if (i == start_index_) {
+                CHECK_GE(block.size(), begin_offset_);
                 begin += begin_offset_;
                 length -= begin_offset_;
             }
-
-            if (i == chain_.size() - 1) {
-                CHECK_GE(length, end_offset_);
-                length -= end_offset_;
-            }
-
             callback(begin, length);
         }
     }
@@ -291,7 +203,7 @@
   public:
     // Copy all of the blocks into a single block.
     template <typename CollectionType = block_type>
-    CollectionType coalesce() const {
+    CollectionType coalesce() const& {
         CollectionType result;
         if (size() == 0) {
             return result;
@@ -308,12 +220,13 @@
         return result;
     }
 
+    block_type coalesce() &&;
+
     template <typename FunctionType>
-    auto coalesced(FunctionType&& f) const ->
-        typename std::result_of<FunctionType(const char*, size_t)>::type {
-        if (chain_.size() == 1) {
+    auto coalesced(FunctionType&& f) const {
+        if (chain_.size() == start_index_ + 1) {
             // If we only have one block, we can use it directly.
-            return f(chain_.front()->data() + begin_offset_, size());
+            return f(chain_[start_index_].data() + begin_offset_, size());
         } else {
             // Otherwise, copy to a single block.
             auto data = coalesce();
@@ -322,23 +235,13 @@
     }
 
     // Get a list of iovecs that can be used to write out all of the blocks.
-    std::vector<adb_iovec> iovecs() const {
-        std::vector<adb_iovec> result;
-        iterate_blocks([&result](const char* data, size_t len) {
-            adb_iovec iov;
-            iov.iov_base = const_cast<char*>(data);
-            iov.iov_len = len;
-            result.emplace_back(iov);
-        });
-
-        return result;
-    }
+    std::vector<adb_iovec> iovecs() const;
 
   private:
     // Total length of all of the blocks in the chain.
     size_t chain_length_ = 0;
 
     size_t begin_offset_ = 0;
-    size_t end_offset_ = 0;
-    std::deque<std::shared_ptr<const block_type>> chain_;
+    size_t start_index_ = 0;
+    std::vector<block_type> chain_;
 };
diff --git a/adb/types_test.cpp b/adb/types_test.cpp
index 1fbd2ca..2c99f95 100644
--- a/adb/types_test.cpp
+++ b/adb/types_test.cpp
@@ -19,21 +19,21 @@
 #include <memory>
 #include "types.h"
 
-static std::unique_ptr<IOVector::block_type> create_block(const std::string& string) {
-    return std::make_unique<IOVector::block_type>(string.begin(), string.end());
+static IOVector::block_type create_block(const std::string& string) {
+    return IOVector::block_type(string.begin(), string.end());
 }
 
-static std::unique_ptr<IOVector::block_type> create_block(char value, size_t len) {
-    auto block = std::make_unique<IOVector::block_type>();
-    block->resize(len);
-    memset(&(*block)[0], value, len);
+static IOVector::block_type create_block(char value, size_t len) {
+    auto block = IOVector::block_type();
+    block.resize(len);
+    memset(&(block)[0], value, len);
     return block;
 }
 
 template <typename T>
-static std::unique_ptr<IOVector::block_type> copy_block(T&& block) {
-    auto copy = std::make_unique<IOVector::block_type>();
-    copy->assign(block->begin(), block->end());
+static IOVector::block_type copy_block(const T& block) {
+    auto copy = IOVector::block_type();
+    copy.assign(block.begin(), block.end());
     return copy;
 }
 
@@ -50,7 +50,7 @@
     bc.append(copy_block(block));
     ASSERT_EQ(100ULL, bc.size());
     auto coalesced = bc.coalesce();
-    ASSERT_EQ(*block, coalesced);
+    ASSERT_EQ(block, coalesced);
 }
 
 TEST(IOVector, single_block_split) {
@@ -60,8 +60,8 @@
     IOVector foo = bc.take_front(3);
     ASSERT_EQ(3ULL, foo.size());
     ASSERT_EQ(3ULL, bc.size());
-    ASSERT_EQ(*create_block("foo"), foo.coalesce());
-    ASSERT_EQ(*create_block("bar"), bc.coalesce());
+    ASSERT_EQ(create_block("foo"), foo.coalesce());
+    ASSERT_EQ(create_block("bar"), bc.coalesce());
 }
 
 TEST(IOVector, aligned_split) {
@@ -73,15 +73,15 @@
 
     IOVector foo = bc.take_front(3);
     ASSERT_EQ(3ULL, foo.size());
-    ASSERT_EQ(*create_block("foo"), foo.coalesce());
+    ASSERT_EQ(create_block("foo"), foo.coalesce());
 
     IOVector bar = bc.take_front(3);
     ASSERT_EQ(3ULL, bar.size());
-    ASSERT_EQ(*create_block("bar"), bar.coalesce());
+    ASSERT_EQ(create_block("bar"), bar.coalesce());
 
     IOVector baz = bc.take_front(3);
     ASSERT_EQ(3ULL, baz.size());
-    ASSERT_EQ(*create_block("baz"), baz.coalesce());
+    ASSERT_EQ(create_block("baz"), baz.coalesce());
 
     ASSERT_EQ(0ULL, bc.size());
 }
@@ -97,23 +97,23 @@
     // Aligned left, misaligned right, across multiple blocks.
     IOVector foob = bc.take_front(4);
     ASSERT_EQ(4ULL, foob.size());
-    ASSERT_EQ(*create_block("foob"), foob.coalesce());
+    ASSERT_EQ(create_block("foob"), foob.coalesce());
 
     // Misaligned left, misaligned right, in one block.
     IOVector a = bc.take_front(1);
     ASSERT_EQ(1ULL, a.size());
-    ASSERT_EQ(*create_block("a"), a.coalesce());
+    ASSERT_EQ(create_block("a"), a.coalesce());
 
     // Misaligned left, misaligned right, across two blocks.
     IOVector rba = bc.take_front(3);
     ASSERT_EQ(3ULL, rba.size());
-    ASSERT_EQ(*create_block("rba"), rba.coalesce());
+    ASSERT_EQ(create_block("rba"), rba.coalesce());
 
     // Misaligned left, misaligned right, across three blocks.
     IOVector zquxquu = bc.take_front(7);
     ASSERT_EQ(7ULL, zquxquu.size());
-    ASSERT_EQ(*create_block("zquxquu"), zquxquu.coalesce());
+    ASSERT_EQ(create_block("zquxquu"), zquxquu.coalesce());
 
     ASSERT_EQ(1ULL, bc.size());
-    ASSERT_EQ(*create_block("x"), bc.coalesce());
+    ASSERT_EQ(create_block("x"), bc.coalesce());
 }
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 414a186..32702ae 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -123,6 +123,7 @@
 cc_test {
     name: "libfs_avb_device_test",
     test_suites: ["device-tests"],
+    require_root: true,
     static_libs: [
         "libavb",
         "libdm",
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 7411e5a..8e3875f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -72,6 +72,8 @@
 
 static constexpr const std::string_view kCowGroupName = "cow";
 
+bool SourceCopyOperationIsClone(const chromeos_update_engine::InstallOperation& operation);
+
 enum class UpdateState : unsigned int {
     // No update or merge is in progress.
     None,
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 77315b4..61f5c0c 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -62,27 +62,52 @@
     return false;
 }
 
+bool SourceCopyOperationIsClone(const InstallOperation& operation) {
+    using ChromeOSExtent = chromeos_update_engine::Extent;
+    if (operation.src_extents().size() != operation.dst_extents().size()) {
+        return false;
+    }
+    return std::equal(operation.src_extents().begin(), operation.src_extents().end(),
+                      operation.dst_extents().begin(),
+                      [](const ChromeOSExtent& src, const ChromeOSExtent& dst) {
+                          return src.start_block() == dst.start_block() &&
+                                 src.num_blocks() == dst.num_blocks();
+                      });
+}
+
+void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,
+                 unsigned int sectors_per_block) {
+    const auto block_boundary = de.start_block() + de.num_blocks();
+    for (auto b = de.start_block(); b < block_boundary; ++b) {
+        for (unsigned int s = 0; s < sectors_per_block; ++s) {
+            const auto sector_id = b * sectors_per_block + s;
+            sc->WriteSector(sector_id);
+        }
+    }
+}
+
 uint64_t PartitionCowCreator::GetCowSize() {
     // WARNING: The origin partition should be READ-ONLY
     const uint64_t logical_block_size = current_metadata->logical_block_size();
     const unsigned int sectors_per_block = logical_block_size / kSectorSize;
     DmSnapCowSizeCalculator sc(kSectorSize, kSnapshotChunkSize);
 
+    // Allocate space for extra extents (if any). These extents are those that can be
+    // used for error corrections or to store verity hash trees.
+    for (const auto& de : extra_extents) {
+        WriteExtent(&sc, de, sectors_per_block);
+    }
+
     if (operations == nullptr) return sc.cow_size_bytes();
 
     for (const auto& iop : *operations) {
-        for (const auto& de : iop.dst_extents()) {
-            // Skip if no blocks are written
-            if (de.num_blocks() == 0) continue;
+        // Do not allocate space for operations that are going to be skipped
+        // during OTA application.
+        if (iop.type() == InstallOperation::SOURCE_COPY && SourceCopyOperationIsClone(iop))
+            continue;
 
-            // Flag all the blocks that were written
-            const auto block_boundary = de.start_block() + de.num_blocks();
-            for (auto b = de.start_block(); b < block_boundary; ++b) {
-                for (unsigned int s = 0; s < sectors_per_block; ++s) {
-                    const auto sector_id = b * sectors_per_block + s;
-                    sc.WriteSector(sector_id);
-                }
-            }
+        for (const auto& de : iop.dst_extents()) {
+            WriteExtent(&sc, de, sectors_per_block);
         }
     }
 
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index d3d186b..699f9a1 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -18,6 +18,7 @@
 
 #include <optional>
 #include <string>
+#include <vector>
 
 #include <liblp/builder.h>
 #include <update_engine/update_metadata.pb.h>
@@ -30,6 +31,7 @@
 // Helper class that creates COW for a partition.
 struct PartitionCowCreator {
     using Extent = android::fs_mgr::Extent;
+    using ChromeOSExtent = chromeos_update_engine::Extent;
     using Interval = android::fs_mgr::Interval;
     using MetadataBuilder = android::fs_mgr::MetadataBuilder;
     using Partition = android::fs_mgr::Partition;
@@ -50,6 +52,9 @@
     std::string current_suffix;
     // List of operations to be applied on the partition.
     const RepeatedPtrField<InstallOperation>* operations = nullptr;
+    // Extra extents that are going to be invalidated during the update
+    // process.
+    std::vector<ChromeOSExtent> extra_extents = {};
 
     struct Return {
         SnapshotStatus snapshot_status;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 0fb4af9..80d73c3 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -65,6 +65,7 @@
 using android::fs_mgr::SlotNumberForSlotSuffix;
 using android::hardware::boot::V1_1::MergeStatus;
 using chromeos_update_engine::DeltaArchiveManifest;
+using chromeos_update_engine::Extent;
 using chromeos_update_engine::InstallOperation;
 template <typename T>
 using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
@@ -128,6 +129,13 @@
     auto file = LockExclusive();
     if (!file) return false;
 
+    // Purge the ImageManager just in case there is a corrupt lp_metadata file
+    // lying around. (NB: no need to return false on an error, we can let the
+    // update try to progress.)
+    if (EnsureImageManager()) {
+        images_->RemoveAllImages();
+    }
+
     auto state = ReadUpdateState(file.get());
     if (state != UpdateState::None) {
         LOG(ERROR) << "An update is already in progress, cannot begin a new update";
@@ -1181,8 +1189,26 @@
     }
 
     bool ok = true;
+    bool has_mapped_cow_images = false;
     for (const auto& name : snapshots) {
-        ok &= (UnmapPartitionWithSnapshot(lock, name) && DeleteSnapshot(lock, name));
+        if (!UnmapPartitionWithSnapshot(lock, name) || !DeleteSnapshot(lock, name)) {
+            // Remember whether or not we were able to unmap the cow image.
+            auto cow_image_device = GetCowImageDeviceName(name);
+            has_mapped_cow_images |= images_->IsImageMapped(cow_image_device);
+
+            ok = false;
+        }
+    }
+
+    if (ok || !has_mapped_cow_images) {
+        // Delete any image artifacts as a precaution, in case an update is
+        // being cancelled due to some corrupted state in an lp_metadata file.
+        // Note that we do not do this if some cow images are still mapped,
+        // since we must not remove backing storage if it's in use.
+        if (!EnsureImageManager() || !images_->RemoveAllImages()) {
+            LOG(ERROR) << "Could not remove all snapshot artifacts";
+            return false;
+        }
     }
     return ok;
 }
@@ -1886,12 +1912,15 @@
     // these devices.
     AutoDeviceList created_devices;
 
-    PartitionCowCreator cow_creator{.target_metadata = target_metadata.get(),
-                                    .target_suffix = target_suffix,
-                                    .target_partition = nullptr,
-                                    .current_metadata = current_metadata.get(),
-                                    .current_suffix = current_suffix,
-                                    .operations = nullptr};
+    PartitionCowCreator cow_creator{
+            .target_metadata = target_metadata.get(),
+            .target_suffix = target_suffix,
+            .target_partition = nullptr,
+            .current_metadata = current_metadata.get(),
+            .current_suffix = current_suffix,
+            .operations = nullptr,
+            .extra_extents = {},
+    };
 
     if (!CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
                                        &all_snapshot_status)) {
@@ -1937,15 +1966,24 @@
     }
 
     std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
+    std::map<std::string, std::vector<Extent>> extra_extents_map;
     for (const auto& partition_update : manifest.partitions()) {
         auto suffixed_name = partition_update.partition_name() + target_suffix;
-        auto&& [it, inserted] = install_operation_map.emplace(std::move(suffixed_name),
-                                                              &partition_update.operations());
+        auto&& [it, inserted] =
+                install_operation_map.emplace(suffixed_name, &partition_update.operations());
         if (!inserted) {
             LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
                        << " in update manifest.";
             return false;
         }
+
+        auto& extra_extents = extra_extents_map[suffixed_name];
+        if (partition_update.has_hash_tree_extent()) {
+            extra_extents.push_back(partition_update.hash_tree_extent());
+        }
+        if (partition_update.has_fec_extent()) {
+            extra_extents.push_back(partition_update.fec_extent());
+        }
     }
 
     for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
@@ -1956,6 +1994,12 @@
             cow_creator->operations = operations_it->second;
         }
 
+        cow_creator->extra_extents.clear();
+        auto extra_extents_it = extra_extents_map.find(target_partition->name());
+        if (extra_extents_it != extra_extents_map.end()) {
+            cow_creator->extra_extents = std::move(extra_extents_it->second);
+        }
+
         // Compute the device sizes for the partition.
         auto cow_creator_ret = cow_creator->Run();
         if (!cow_creator_ret.has_value()) {
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 7d6e78f..1d2a1f1 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -716,6 +716,45 @@
         return AssertionSuccess();
     }
 
+    AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path = nullptr) {
+        std::string real_path;
+        if (!sm->MapUpdateSnapshot(
+                    CreateLogicalPartitionParams{
+                            .block_device = fake_super,
+                            .metadata_slot = 1,
+                            .partition_name = name,
+                            .timeout_ms = 10s,
+                            .partition_opener = opener_.get(),
+                    },
+                    &real_path)) {
+            return AssertionFailure() << "Unable to map snapshot " << name;
+        }
+        if (path) {
+            *path = real_path;
+        }
+        return AssertionSuccess() << "Mapped snapshot " << name << " to " << real_path;
+    }
+
+    AssertionResult WriteSnapshotAndHash(const std::string& name,
+                                         std::optional<size_t> size = std::nullopt) {
+        std::string path;
+        auto res = MapUpdateSnapshot(name, &path);
+        if (!res) {
+            return res;
+        }
+
+        std::string size_string = size ? (std::to_string(*size) + " bytes") : "";
+
+        if (!WriteRandomData(path, size, &hashes_[name])) {
+            return AssertionFailure() << "Unable to write " << size_string << " to " << path
+                                      << " for partition " << name;
+        }
+
+        return AssertionSuccess() << "Written " << size_string << " to " << path
+                                  << " for snapshot partition " << name
+                                  << ", hash: " << hashes_[name];
+    }
+
     std::unique_ptr<TestPartitionOpener> opener_;
     DeltaArchiveManifest manifest_;
     std::unique_ptr<MetadataBuilder> src_;
@@ -762,21 +801,7 @@
 
     // Write some data to target partitions.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        std::string path;
-        ASSERT_TRUE(sm->MapUpdateSnapshot(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata_slot = 1,
-                        .partition_name = name,
-                        .timeout_ms = 10s,
-                        .partition_opener = opener_.get(),
-                },
-                &path))
-                << name;
-        ASSERT_TRUE(WriteRandomData(path));
-        auto hash = GetHash(path);
-        ASSERT_TRUE(hash.has_value());
-        hashes_[name] = *hash;
+        ASSERT_TRUE(WriteSnapshotAndHash(name, partition_size));
     }
 
     // Assert that source partitions aren't affected.
@@ -890,17 +915,7 @@
 
     // Check that target partitions can be mapped.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        std::string path;
-        EXPECT_TRUE(sm->MapUpdateSnapshot(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata_slot = 1,
-                        .partition_name = name,
-                        .timeout_ms = 10s,
-                        .partition_opener = opener_.get(),
-                },
-                &path))
-                << name;
+        EXPECT_TRUE(MapUpdateSnapshot(name));
     }
 }
 
@@ -921,21 +936,7 @@
 
     // Write some data to target partitions.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        std::string path;
-        ASSERT_TRUE(sm->MapUpdateSnapshot(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata_slot = 1,
-                        .partition_name = name,
-                        .timeout_ms = 10s,
-                        .partition_opener = opener_.get(),
-                },
-                &path))
-                << name;
-        ASSERT_TRUE(WriteRandomData(path));
-        auto hash = GetHash(path);
-        ASSERT_TRUE(hash.has_value());
-        hashes_[name] = *hash;
+        ASSERT_TRUE(WriteSnapshotAndHash(name));
     }
 
     // Assert that source partitions aren't affected.
@@ -1089,21 +1090,7 @@
 
     // Write some data to target partitions.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        std::string path;
-        ASSERT_TRUE(sm->MapUpdateSnapshot(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata_slot = 1,
-                        .partition_name = name,
-                        .timeout_ms = 10s,
-                        .partition_opener = opener_.get(),
-                },
-                &path))
-                << name;
-        ASSERT_TRUE(WriteRandomData(path));
-        auto hash = GetHash(path);
-        ASSERT_TRUE(hash.has_value());
-        hashes_[name] = *hash;
+        ASSERT_TRUE(WriteSnapshotAndHash(name));
     }
 
     // Assert that source partitions aren't affected.
@@ -1313,6 +1300,56 @@
     EXPECT_FALSE(test_device->IsSlotUnbootable(0));
 }
 
+TEST_F(SnapshotUpdateTest, Hashtree) {
+    constexpr auto partition_size = 4_MiB;
+    constexpr auto data_size = 3_MiB;
+    constexpr auto hashtree_size = 512_KiB;
+    constexpr auto fec_size = partition_size - data_size - hashtree_size;
+
+    const auto block_size = manifest_.block_size();
+    SetSize(sys_, partition_size);
+
+    auto e = sys_->add_operations()->add_dst_extents();
+    e->set_start_block(0);
+    e->set_num_blocks(data_size / block_size);
+
+    // Set hastree extents.
+    sys_->mutable_hash_tree_data_extent()->set_start_block(0);
+    sys_->mutable_hash_tree_data_extent()->set_num_blocks(data_size / block_size);
+
+    sys_->mutable_hash_tree_extent()->set_start_block(data_size / block_size);
+    sys_->mutable_hash_tree_extent()->set_num_blocks(hashtree_size / block_size);
+
+    // Set FEC extents.
+    sys_->mutable_fec_data_extent()->set_start_block(0);
+    sys_->mutable_fec_data_extent()->set_num_blocks((data_size + hashtree_size) / block_size);
+
+    sys_->mutable_fec_extent()->set_start_block((data_size + hashtree_size) / block_size);
+    sys_->mutable_fec_extent()->set_num_blocks(fec_size / block_size);
+
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+    // Write some data to target partition.
+    ASSERT_TRUE(WriteSnapshotAndHash("sys_b", partition_size));
+
+    // Finish update.
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // After reboot, init does first stage mount.
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+    // Check that the target partition have the same content. Hashtree and FEC extents
+    // should be accounted for.
+    ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
+}
+
 class FlashAfterUpdateTest : public SnapshotUpdateTest,
                              public WithParamInterface<std::tuple<uint32_t, bool>> {
   public:
diff --git a/fs_mgr/libsnapshot/snapshotctl.rc b/fs_mgr/libsnapshot/snapshotctl.rc
index 29707f1..3ab0645 100644
--- a/fs_mgr/libsnapshot/snapshotctl.rc
+++ b/fs_mgr/libsnapshot/snapshotctl.rc
@@ -1,2 +1,2 @@
 on property:sys.boot_completed=1
-    exec - root root -- /system/bin/snapshotctl merge --logcat
+    exec_background - root root -- /system/bin/snapshotctl merge --logcat
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 312fa3e..2d62347 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -62,24 +62,6 @@
     return PartitionOpener::GetDeviceString(partition_name);
 }
 
-bool WriteRandomData(const std::string& path) {
-    unique_fd rand(open("/dev/urandom", O_RDONLY));
-    unique_fd fd(open(path.c_str(), O_WRONLY));
-
-    char buf[4096];
-    while (true) {
-        ssize_t n = TEMP_FAILURE_RETRY(read(rand.get(), buf, sizeof(buf)));
-        if (n <= 0) return false;
-        if (!WriteFully(fd.get(), buf, n)) {
-            if (errno == ENOSPC) {
-                return true;
-            }
-            PLOG(ERROR) << "Cannot write " << path;
-            return false;
-        }
-    }
-}
-
 std::string ToHexString(const uint8_t* buf, size_t len) {
     char lookup[] = "0123456789abcdef";
     std::string out(len * 2 + 1, '\0');
@@ -91,6 +73,47 @@
     return out;
 }
 
+bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size,
+                     std::string* hash) {
+    unique_fd rand(open("/dev/urandom", O_RDONLY));
+    unique_fd fd(open(path.c_str(), O_WRONLY));
+
+    SHA256_CTX ctx;
+    if (hash) {
+        SHA256_Init(&ctx);
+    }
+
+    char buf[4096];
+    size_t total_written = 0;
+    while (!expect_size || total_written < *expect_size) {
+        ssize_t n = TEMP_FAILURE_RETRY(read(rand.get(), buf, sizeof(buf)));
+        if (n <= 0) return false;
+        if (!WriteFully(fd.get(), buf, n)) {
+            if (errno == ENOSPC) {
+                break;
+            }
+            PLOG(ERROR) << "Cannot write " << path;
+            return false;
+        }
+        total_written += n;
+        if (hash) {
+            SHA256_Update(&ctx, buf, n);
+        }
+    }
+
+    if (expect_size && total_written != *expect_size) {
+        PLOG(ERROR) << "Written " << total_written << " bytes, expected " << *expect_size;
+        return false;
+    }
+
+    if (hash) {
+        uint8_t out[32];
+        SHA256_Final(out, &ctx);
+        *hash = ToHexString(out, sizeof(out));
+    }
+    return true;
+}
+
 std::optional<std::string> GetHash(const std::string& path) {
     std::string content;
     if (!android::base::ReadFileToString(path, &content, true)) {
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
index 9083843..2bf1b57 100644
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -137,8 +137,11 @@
 // Helper for error-spam-free cleanup.
 void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::string& name);
 
-// Write some random data to the given device. Will write until reaching end of the device.
-bool WriteRandomData(const std::string& device);
+// Write some random data to the given device.
+// If expect_size is not specified, will write until reaching end of the device.
+// Expect space of |path| is multiple of 4K.
+bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
+                     std::string* hash = nullptr);
 
 std::optional<std::string> GetHash(const std::string& path);
 
diff --git a/init/service.cpp b/init/service.cpp
index 574ff52..ad42df7 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -42,9 +42,11 @@
 
 #if defined(__ANDROID__)
 #include <ApexProperties.sysprop.h>
+#include <android/api-level.h>
 
 #include "mount_namespace.h"
 #include "property_service.h"
+#include "selinux.h"
 #else
 #include "host_init_stubs.h"
 #endif
@@ -182,7 +184,7 @@
     }
 }
 
-void Service::KillProcessGroup(int signal) {
+void Service::KillProcessGroup(int signal, bool report_oneshot) {
     // If we've already seen a successful result from killProcessGroup*(), then we have removed
     // the cgroup already and calling these functions a second time will simply result in an error.
     // This is true regardless of which signal was sent.
@@ -190,11 +192,20 @@
     if (!process_cgroup_empty_) {
         LOG(INFO) << "Sending signal " << signal << " to service '" << name_ << "' (pid " << pid_
                   << ") process group...";
+        int max_processes = 0;
         int r;
         if (signal == SIGTERM) {
-            r = killProcessGroupOnce(proc_attr_.uid, pid_, signal);
+            r = killProcessGroupOnce(proc_attr_.uid, pid_, signal, &max_processes);
         } else {
-            r = killProcessGroup(proc_attr_.uid, pid_, signal);
+            r = killProcessGroup(proc_attr_.uid, pid_, signal, &max_processes);
+        }
+
+        if (report_oneshot && max_processes > 0) {
+            LOG(WARNING)
+                    << "Killed " << max_processes
+                    << " additional processes from a oneshot process group for service '" << name_
+                    << "'. This is new behavior, previously child processes would not be killed in "
+                       "this case.";
         }
 
         if (r == 0) process_cgroup_empty_ = true;
@@ -244,7 +255,16 @@
 
 void Service::Reap(const siginfo_t& siginfo) {
     if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
-        KillProcessGroup(SIGKILL);
+        KillProcessGroup(SIGKILL, false);
+    } else {
+        // Legacy behavior from ~2007 until Android R: this else branch did not exist and we did not
+        // kill the process group in this case.
+        if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
+            // The new behavior in Android R is to kill these process groups in all cases.  The
+            // 'true' parameter instructions KillProcessGroup() to report a warning message where it
+            // detects a difference in behavior has occurred.
+            KillProcessGroup(SIGKILL, true);
+        }
     }
 
     // Remove any socket resources we may have created.
diff --git a/init/service.h b/init/service.h
index 272c9f9..f842b3c 100644
--- a/init/service.h
+++ b/init/service.h
@@ -132,7 +132,7 @@
   private:
     void NotifyStateChange(const std::string& new_state) const;
     void StopOrReset(int how);
-    void KillProcessGroup(int signal);
+    void KillProcessGroup(int signal, bool report_oneshot = false);
     void SetProcessAttributesAndCaps();
 
     static unsigned long next_start_order_;
diff --git a/libcutils/include_vndk/cutils/log.h b/libcutils/include_vndk/cutils/log.h
deleted file mode 100644
index 21dc11e..0000000
--- a/libcutils/include_vndk/cutils/log.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*Special log.h file for VNDK linking modules*/
-/*
- * Copyright (C) 2005-2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
-#ifndef _LIBS_CUTIL_LOG_H
-#define _LIBS_CUTIL_LOG_H
-
-/* We do not know if developer wanted log/log.h or subset android/log.h */
-#include <log/log.h>
-
-#if defined(__GNUC__)
-#if defined( __clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic warning "-W#warnings"
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpedantic"
-#elif (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR > 9))
-#pragma GCC diagnostic push
-#pragma GCC diagnostic warning "-W#warnings"
-#else
-#pragma GCC diagnostic push
-#pragma GCC diagnostic warning "-Wcpp"
-#endif
-#endif
-
-#warning "Deprecated: don't include cutils/log.h, use either android/log.h or log/log.h"
-
-#if defined(__GNUC__)
-#if defined( __clang__)
-#pragma clang diagnostic pop
-#endif
-#pragma GCC diagnostic pop
-#endif
-
-#endif /* _LIBS_CUTIL_LOG_H */
diff --git a/libcutils/include_vndk/cutils/log.h b/libcutils/include_vndk/cutils/log.h
new file mode 120000
index 0000000..b868d50
--- /dev/null
+++ b/libcutils/include_vndk/cutils/log.h
@@ -0,0 +1 @@
+../../include/cutils/log.h
\ No newline at end of file
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 6af49bb..8212eba 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -113,6 +113,10 @@
     if (ret == 0) {
         memcpy(ss, ai->ai_addr, ai->ai_addrlen);
         freeaddrinfo(ai);
+    } else {
+        // Getaddrinfo has its own error codes. Convert to negative errno.
+        // There, the only thing that can reasonably happen is that the passed-in string is invalid.
+        ret = (ret == EAI_SYSTEM) ? -errno : -EINVAL;
     }
 
     return ret;
@@ -263,19 +267,13 @@
     struct {
         struct nlmsghdr n;
         struct ifaddrmsg r;
-        // Allow for IPv6 address, headers, IPv4 broadcast addr and padding.
-        char attrbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
-                     NLMSG_ALIGN(sizeof(struct rtattr)) +
-                     NLMSG_ALIGN(INET6_ADDRLEN) +
-                     NLMSG_ALIGN(sizeof(struct rtattr)) +
-                     NLMSG_ALIGN(INET_ADDRLEN)];
+        // Allow for IPv4 or IPv6 address, headers, IPv4 broadcast address and padding.
+        char attrbuf[NLMSG_ALIGN(sizeof(struct rtattr)) + NLMSG_ALIGN(INET6_ADDRLEN) +
+                     NLMSG_ALIGN(sizeof(struct rtattr)) + NLMSG_ALIGN(INET_ADDRLEN)];
     } req;
     struct rtattr *rta;
     struct nlmsghdr *nh;
     struct nlmsgerr *err;
-    char buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
-             NLMSG_ALIGN(sizeof(struct nlmsgerr)) +
-             NLMSG_ALIGN(sizeof(struct nlmsghdr))];
 
     // Get interface ID.
     ifindex = if_nametoindex(name);
@@ -344,6 +342,7 @@
         return -saved_errno;
     }
 
+    char buf[NLMSG_ALIGN(sizeof(struct nlmsgerr)) + sizeof(req)];
     len = recv(s, buf, sizeof(buf), 0);
     saved_errno = errno;
     close(s);
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index f73ec2d..0b38b6b 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -47,11 +47,14 @@
 // Return 0 and removes the cgroup if there are no longer any processes in it.
 // Returns -1 in the case of an error occurring or if there are processes still running
 // even after retrying for up to 200ms.
-int killProcessGroup(uid_t uid, int initialPid, int signal);
+// If max_processes is not nullptr, it returns the maximum number of processes seen in the cgroup
+// during the killing process.  Note that this can be 0 if all processes from the process group have
+// already been terminated.
+int killProcessGroup(uid_t uid, int initialPid, int signal, int* max_processes = nullptr);
 
 // Returns the same as killProcessGroup(), however it does not retry, which means
 // that it only returns 0 in the case that the cgroup exists and it contains no processes.
-int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal, int* max_processes = nullptr);
 
 int createProcessGroup(uid_t uid, int initialPid, bool memControl = false);
 
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 7b6dde2..6272664 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -301,7 +301,8 @@
     return feof(fd.get()) ? processes : -1;
 }
 
-static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
+static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries,
+                            int* max_processes) {
     std::string cpuacct_path;
     std::string memory_path;
 
@@ -316,9 +317,16 @@
 
     std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
 
+    if (max_processes != nullptr) {
+        *max_processes = 0;
+    }
+
     int retry = retries;
     int processes;
     while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) {
+        if (max_processes != nullptr && processes > *max_processes) {
+            *max_processes = processes;
+        }
         LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
         if (retry > 0) {
             std::this_thread::sleep_for(5ms);
@@ -359,12 +367,12 @@
     }
 }
 
-int killProcessGroup(uid_t uid, int initialPid, int signal) {
-    return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/);
+int killProcessGroup(uid_t uid, int initialPid, int signal, int* max_processes) {
+    return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/, max_processes);
 }
 
-int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
-    return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/);
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal, int* max_processes) {
+    return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/, max_processes);
 }
 
 int createProcessGroup(uid_t uid, int initialPid, bool memControl) {
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index c141b2e..6627787 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -112,35 +112,33 @@
                      gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
 }
 
-bool Elf::GetGlobalVariable(const std::string& name, uint64_t* memory_address) {
+bool Elf::GetGlobalVariableOffset(const std::string& name, uint64_t* memory_offset) {
   if (!valid_) {
     return false;
   }
 
-  if (!interface_->GetGlobalVariable(name, memory_address) &&
+  uint64_t vaddr;
+  if (!interface_->GetGlobalVariable(name, &vaddr) &&
       (gnu_debugdata_interface_ == nullptr ||
-       !gnu_debugdata_interface_->GetGlobalVariable(name, memory_address))) {
+       !gnu_debugdata_interface_->GetGlobalVariable(name, &vaddr))) {
     return false;
   }
 
-  // Adjust by the load bias.
-  if (load_bias_ > 0 && *memory_address < static_cast<uint64_t>(load_bias_)) {
-    return false;
+  // Check the .data section.
+  uint64_t vaddr_start = interface_->data_vaddr_start();
+  if (vaddr >= vaddr_start && vaddr < interface_->data_vaddr_end()) {
+    *memory_offset = vaddr - vaddr_start + interface_->data_offset();
+    return true;
   }
 
-  *memory_address -= load_bias_;
-
-  // If this winds up in the dynamic section, then we might need to adjust
-  // the address.
-  uint64_t dynamic_end = interface_->dynamic_vaddr() + interface_->dynamic_size();
-  if (*memory_address >= interface_->dynamic_vaddr() && *memory_address < dynamic_end) {
-    if (interface_->dynamic_vaddr() > interface_->dynamic_offset()) {
-      *memory_address -= interface_->dynamic_vaddr() - interface_->dynamic_offset();
-    } else {
-      *memory_address += interface_->dynamic_offset() - interface_->dynamic_vaddr();
-    }
+  // Check the .dynamic section.
+  vaddr_start = interface_->dynamic_vaddr_start();
+  if (vaddr >= vaddr_start && vaddr < interface_->dynamic_vaddr_end()) {
+    *memory_offset = vaddr - vaddr_start + interface_->dynamic_offset();
+    return true;
   }
-  return true;
+
+  return false;
 }
 
 std::string Elf::GetBuildID() {
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 5f95fa8..7676289 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -236,8 +236,12 @@
 
     case PT_DYNAMIC:
       dynamic_offset_ = phdr.p_offset;
-      dynamic_vaddr_ = phdr.p_vaddr;
-      dynamic_size_ = phdr.p_memsz;
+      dynamic_vaddr_start_ = phdr.p_vaddr;
+      if (__builtin_add_overflow(dynamic_vaddr_start_, phdr.p_memsz, &dynamic_vaddr_end_)) {
+        dynamic_offset_ = 0;
+        dynamic_vaddr_start_ = 0;
+        dynamic_vaddr_end_ = 0;
+      }
       break;
 
     default:
@@ -360,6 +364,14 @@
             eh_frame_hdr_offset_ = shdr.sh_offset;
             eh_frame_hdr_section_bias_ = static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset;
             eh_frame_hdr_size_ = shdr.sh_size;
+          } else if (name == ".data") {
+            data_offset_ = shdr.sh_offset;
+            data_vaddr_start_ = shdr.sh_addr;
+            if (__builtin_add_overflow(data_vaddr_start_, shdr.sh_size, &data_vaddr_end_)) {
+              data_offset_ = 0;
+              data_vaddr_start_ = 0;
+              data_vaddr_end_ = 0;
+            }
           }
         }
       }
@@ -398,7 +410,7 @@
   // Find the soname location from the dynamic headers section.
   DynType dyn;
   uint64_t offset = dynamic_offset_;
-  uint64_t max_offset = offset + dynamic_size_;
+  uint64_t max_offset = offset + dynamic_vaddr_end_ - dynamic_vaddr_start_;
   for (uint64_t offset = dynamic_offset_; offset < max_offset; offset += sizeof(DynType)) {
     if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) {
       last_error_.code = ERROR_MEMORY_INVALID;
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index a20be00..ec977e1 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -39,28 +39,22 @@
   }
 }
 
-uint64_t Global::GetVariableOffset(MapInfo* info, const std::string& variable) {
-  if (!search_libs_.empty()) {
-    bool found = false;
-    const char* lib = basename(info->name.c_str());
-    for (const std::string& name : search_libs_) {
-      if (name == lib) {
-        found = true;
-        break;
-      }
-    }
-    if (!found) {
-      return 0;
-    }
+bool Global::Searchable(const std::string& name) {
+  if (search_libs_.empty()) {
+    return true;
   }
 
-  Elf* elf = info->GetElf(memory_, arch());
-  uint64_t ptr;
-  // Find first non-empty list (libraries might be loaded multiple times).
-  if (elf->GetGlobalVariable(variable, &ptr) && ptr != 0) {
-    return ptr + info->start;
+  if (name.empty()) {
+    return false;
   }
-  return 0;
+
+  const char* base_name = basename(name.c_str());
+  for (const std::string& lib : search_libs_) {
+    if (base_name == lib) {
+      return true;
+    }
+  }
+  return false;
 }
 
 void Global::FindAndReadVariable(Maps* maps, const char* var_str) {
@@ -78,24 +72,27 @@
   //   f2000-f3000 2000 rw- /system/lib/libc.so
   MapInfo* map_start = nullptr;
   for (const auto& info : *maps) {
-    if (map_start != nullptr) {
-      if (map_start->name == info->name) {
-        if (info->offset != 0 &&
-            (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
-          uint64_t ptr = GetVariableOffset(map_start, variable);
-          if (ptr != 0 && ReadVariableData(ptr)) {
-            break;
-          } else {
-            // Failed to find the global variable, do not bother trying again.
-            map_start = nullptr;
+    if (map_start != nullptr && map_start->name == info->name) {
+      if (info->offset != 0 &&
+          (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
+        Elf* elf = map_start->GetElf(memory_, arch());
+        uint64_t ptr;
+        if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
+          uint64_t offset_end = info->offset + info->end - info->start;
+          if (ptr >= info->offset && ptr < offset_end) {
+            ptr = info->start + ptr - info->offset;
+            if (ReadVariableData(ptr)) {
+              break;
+            }
           }
         }
-      } else {
         map_start = nullptr;
       }
+    } else {
+      map_start = nullptr;
     }
     if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
-        !info->name.empty()) {
+        Searchable(info->name)) {
       map_start = info.get();
     }
   }
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index fc3f2a6..472ed92 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -63,7 +63,7 @@
 
   bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
 
-  bool GetGlobalVariable(const std::string& name, uint64_t* memory_address);
+  bool GetGlobalVariableOffset(const std::string& name, uint64_t* memory_offset);
 
   uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
 
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index ae9bd9a..0c39b23 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -77,8 +77,11 @@
   void SetGnuDebugdataInterface(ElfInterface* interface) { gnu_debugdata_interface_ = interface; }
 
   uint64_t dynamic_offset() { return dynamic_offset_; }
-  uint64_t dynamic_vaddr() { return dynamic_vaddr_; }
-  uint64_t dynamic_size() { return dynamic_size_; }
+  uint64_t dynamic_vaddr_start() { return dynamic_vaddr_start_; }
+  uint64_t dynamic_vaddr_end() { return dynamic_vaddr_end_; }
+  uint64_t data_offset() { return data_offset_; }
+  uint64_t data_vaddr_start() { return data_vaddr_start_; }
+  uint64_t data_vaddr_end() { return data_vaddr_end_; }
   uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; }
   int64_t eh_frame_hdr_section_bias() { return eh_frame_hdr_section_bias_; }
   uint64_t eh_frame_hdr_size() { return eh_frame_hdr_size_; }
@@ -141,8 +144,12 @@
 
   // Stored elf data.
   uint64_t dynamic_offset_ = 0;
-  uint64_t dynamic_vaddr_ = 0;
-  uint64_t dynamic_size_ = 0;
+  uint64_t dynamic_vaddr_start_ = 0;
+  uint64_t dynamic_vaddr_end_ = 0;
+
+  uint64_t data_offset_ = 0;
+  uint64_t data_vaddr_start_ = 0;
+  uint64_t data_vaddr_end_ = 0;
 
   uint64_t eh_frame_hdr_offset_ = 0;
   int64_t eh_frame_hdr_section_bias_ = 0;
diff --git a/libunwindstack/include/unwindstack/Global.h b/libunwindstack/include/unwindstack/Global.h
index a7e6c15..b9bb141 100644
--- a/libunwindstack/include/unwindstack/Global.h
+++ b/libunwindstack/include/unwindstack/Global.h
@@ -45,7 +45,7 @@
   ArchEnum arch() { return arch_; }
 
  protected:
-  uint64_t GetVariableOffset(MapInfo* info, const std::string& variable);
+  bool Searchable(const std::string& name);
   void FindAndReadVariable(Maps* maps, const char* variable);
 
   virtual bool ReadVariableData(uint64_t offset) = 0;
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index 1ea9e5c..0dd3af6 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -36,14 +36,18 @@
 
 class DexFilesTest : public ::testing::Test {
  protected:
-  void CreateFakeElf(MapInfo* map_info) {
+  void CreateFakeElf(MapInfo* map_info, uint64_t global_offset, uint64_t data_offset,
+                     uint64_t data_vaddr, uint64_t data_size) {
     MemoryFake* memory = new MemoryFake;
     ElfFake* elf = new ElfFake(memory);
     elf->FakeSetValid(true);
     ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
     elf->FakeSetInterface(interface);
 
-    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
+    interface->FakeSetGlobalVariable("__dex_debug_descriptor", global_offset);
+    interface->FakeSetDataOffset(data_offset);
+    interface->FakeSetDataVaddrStart(data_vaddr);
+    interface->FakeSetDataVaddrEnd(data_vaddr + data_size);
     map_info->elf.reset(elf);
   }
 
@@ -54,11 +58,11 @@
     maps_.reset(
         new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf\n"
                        "4000-6000 r--s 00000000 00:00 0 /fake/elf\n"
-                       "6000-8000 -wxs 00000000 00:00 0 /fake/elf\n"
+                       "6000-8000 -wxs 00002000 00:00 0 /fake/elf\n"
                        "a000-c000 r--p 00000000 00:00 0 /fake/elf2\n"
-                       "c000-f000 rw-p 00001000 00:00 0 /fake/elf2\n"
+                       "c000-f000 rw-p 00002000 00:00 0 /fake/elf2\n"
                        "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
-                       "100000-110000 rw-p 0001000 00:00 0 /fake/elf3\n"
+                       "100000-110000 rw-p 00f1000 00:00 0 /fake/elf3\n"
                        "200000-210000 rw-p 0002000 00:00 0 /fake/elf3\n"
                        "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"));
     ASSERT_TRUE(maps_->Parse());
@@ -66,17 +70,17 @@
     // Global variable in a section that is not readable.
     MapInfo* map_info = maps_->Get(kMapGlobalNonReadable);
     ASSERT_TRUE(map_info != nullptr);
-    CreateFakeElf(map_info);
+    CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
 
     // Global variable not set by default.
     map_info = maps_->Get(kMapGlobalSetToZero);
     ASSERT_TRUE(map_info != nullptr);
-    CreateFakeElf(map_info);
+    CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
 
     // Global variable set in this map.
     map_info = maps_->Get(kMapGlobal);
     ASSERT_TRUE(map_info != nullptr);
-    CreateFakeElf(map_info);
+    CreateFakeElf(map_info, 0xf1800, 0xf1000, 0xf1000, 0x10000);
   }
 
   void SetUp() override {
@@ -156,7 +160,7 @@
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  WriteDescriptor32(0xf800, 0x200000);
+  WriteDescriptor32(0x100800, 0x200000);
   WriteEntry32(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
@@ -172,7 +176,7 @@
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  WriteDescriptor64(0xf800, 0x200000);
+  WriteDescriptor64(0x100800, 0x200000);
   WriteEntry64(0x200000, 0, 0, 0x301000);
   WriteDex(0x301000);
 
@@ -186,7 +190,7 @@
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  WriteDescriptor32(0xf800, 0x200000);
+  WriteDescriptor32(0x100800, 0x200000);
   WriteEntry32(0x200000, 0x200100, 0, 0x100000);
   WriteEntry32(0x200100, 0, 0x200000, 0x300000);
   WriteDex(0x300000);
@@ -203,7 +207,7 @@
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  WriteDescriptor64(0xf800, 0x200000);
+  WriteDescriptor64(0x100800, 0x200000);
   WriteEntry64(0x200000, 0x200100, 0, 0x100000);
   WriteEntry64(0x200100, 0, 0x200000, 0x300000);
   WriteDex(0x300000);
@@ -218,7 +222,7 @@
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  WriteDescriptor32(0xf800, 0x200000);
+  WriteDescriptor32(0x100800, 0x200000);
   WriteEntry32(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
@@ -238,7 +242,7 @@
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  WriteDescriptor32(0xf800, 0x200000);
+  WriteDescriptor32(0x100800, 0x200000);
   WriteEntry32(0x200000, 0x200100, 0, 0x100000);
   WriteEntry32(0x200100, 0, 0x200000, 0x300000);
   WriteDex(0x300000);
@@ -274,9 +278,9 @@
   MapInfo* info = maps_->Get(kMapDexFiles);
 
   // First global variable found, but value is zero.
-  WriteDescriptor32(0xa800, 0);
+  WriteDescriptor32(0xc800, 0);
 
-  WriteDescriptor32(0xf800, 0x200000);
+  WriteDescriptor32(0x100800, 0x200000);
   WriteEntry32(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
@@ -289,7 +293,7 @@
   dex_files_->SetArch(ARCH_ARM);
   method_name = "fail";
   method_offset = 0x123;
-  WriteDescriptor32(0xa800, 0x100000);
+  WriteDescriptor32(0xc800, 0x100000);
   dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
   EXPECT_EQ("fail", method_name);
   EXPECT_EQ(0x123U, method_offset);
@@ -303,9 +307,9 @@
   MapInfo* info = maps_->Get(kMapDexFiles);
 
   // First global variable found, but value is zero.
-  WriteDescriptor64(0xa800, 0);
+  WriteDescriptor64(0xc800, 0);
 
-  WriteDescriptor64(0xf800, 0x200000);
+  WriteDescriptor64(0x100800, 0x200000);
   WriteEntry64(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
@@ -318,7 +322,7 @@
   dex_files_->SetArch(ARCH_ARM64);
   method_name = "fail";
   method_offset = 0x123;
-  WriteDescriptor64(0xa800, 0x100000);
+  WriteDescriptor64(0xc800, 0x100000);
   dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
   EXPECT_EQ("fail", method_name);
   EXPECT_EQ(0x123U, method_offset);
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index 832e64a..c33908d 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -97,6 +97,14 @@
 
   void FakeSetErrorAddress(uint64_t address) { last_error_.address = address; }
 
+  void FakeSetDataOffset(uint64_t offset) { data_offset_ = offset; }
+  void FakeSetDataVaddrStart(uint64_t vaddr) { data_vaddr_start_ = vaddr; }
+  void FakeSetDataVaddrEnd(uint64_t vaddr) { data_vaddr_end_ = vaddr; }
+
+  void FakeSetDynamicOffset(uint64_t offset) { dynamic_offset_ = offset; }
+  void FakeSetDynamicVaddrStart(uint64_t vaddr) { dynamic_vaddr_start_ = vaddr; }
+  void FakeSetDynamicVaddrEnd(uint64_t vaddr) { dynamic_vaddr_end_ = vaddr; }
+
  private:
   std::unordered_map<std::string, uint64_t> globals_;
   std::string fake_build_id_;
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index e6728a0..d227b60 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -320,9 +320,13 @@
   MOCK_METHOD(bool, GetGlobalVariable, (const std::string&, uint64_t*), (override));
   MOCK_METHOD(bool, IsValidPc, (uint64_t), (override));
 
+  void MockSetDataOffset(uint64_t offset) { data_offset_ = offset; }
+  void MockSetDataVaddrStart(uint64_t vaddr) { data_vaddr_start_ = vaddr; }
+  void MockSetDataVaddrEnd(uint64_t vaddr) { data_vaddr_end_ = vaddr; }
+
   void MockSetDynamicOffset(uint64_t offset) { dynamic_offset_ = offset; }
-  void MockSetDynamicVaddr(uint64_t vaddr) { dynamic_vaddr_ = vaddr; }
-  void MockSetDynamicSize(uint64_t size) { dynamic_size_ = size; }
+  void MockSetDynamicVaddrStart(uint64_t vaddr) { dynamic_vaddr_start_ = vaddr; }
+  void MockSetDynamicVaddrEnd(uint64_t vaddr) { dynamic_vaddr_end_ = vaddr; }
 };
 
 TEST_F(ElfTest, step_in_interface) {
@@ -348,7 +352,7 @@
 
   std::string global("something");
   uint64_t offset;
-  ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+  ASSERT_FALSE(elf.GetGlobalVariableOffset(global, &offset));
 }
 
 TEST_F(ElfTest, get_global_valid_not_in_interface) {
@@ -358,119 +362,69 @@
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
 
-  uint64_t offset;
   std::string global("something");
-  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false));
+  EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+      .WillOnce(::testing::Return(false));
 
-  ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+  uint64_t offset;
+  ASSERT_FALSE(elf.GetGlobalVariableOffset(global, &offset));
 }
 
-TEST_F(ElfTest, get_global_valid_below_load_bias) {
+TEST_F(ElfTest, get_global_vaddr_in_no_sections) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
-  elf.FakeSetLoadBias(0x1000);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
 
-  uint64_t offset;
   std::string global("something");
-  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+  EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
       .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
 
-  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);
+  ASSERT_FALSE(elf.GetGlobalVariableOffset(global, &offset));
 }
 
-TEST_F(ElfTest, get_global_valid_dynamic_zero) {
+TEST_F(ElfTest, get_global_vaddr_in_data_section) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
+  interface->MockSetDataVaddrStart(0x500);
+  interface->MockSetDataVaddrEnd(0x600);
+  interface->MockSetDataOffset(0xa000);
 
-  ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_);
-  elf.FakeSetGnuDebugdataInterface(gnu_interface);
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x580), ::testing::Return(true)));
 
   uint64_t offset;
-  std::string global("something");
-  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false));
-
-  EXPECT_CALL(*gnu_interface, GetGlobalVariable(global, &offset))
-      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x500), ::testing::Return(true)));
-
-  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
-  EXPECT_EQ(0x500U, offset);
+  ASSERT_TRUE(elf.GetGlobalVariableOffset(global, &offset));
+  EXPECT_EQ(0xa080U, offset);
 }
 
-TEST_F(ElfTest, get_global_valid_in_gnu_debugdata_dynamic_zero) {
+TEST_F(ElfTest, get_global_vaddr_in_dynamic_section) {
   ElfFake elf(memory_);
   elf.FakeSetValid(true);
 
   ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
   elf.FakeSetInterface(interface);
+  interface->MockSetDataVaddrStart(0x500);
+  interface->MockSetDataVaddrEnd(0x600);
+  interface->MockSetDataOffset(0xa000);
+
+  interface->MockSetDynamicVaddrStart(0x800);
+  interface->MockSetDynamicVaddrEnd(0x900);
+  interface->MockSetDynamicOffset(0xc000);
+
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x880), ::testing::Return(true)));
 
   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(0x300U, offset);
-}
-
-TEST_F(ElfTest, get_global_valid_dynamic_adjust_negative) {
-  ElfFake elf(memory_);
-  elf.FakeSetValid(true);
-
-  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
-  interface->MockSetDynamicOffset(0x400);
-  interface->MockSetDynamicVaddr(0x800);
-  interface->MockSetDynamicSize(0x100);
-  elf.FakeSetInterface(interface);
-
-  uint64_t offset;
-  std::string global("something");
-  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
-      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true)));
-
-  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
-  EXPECT_EQ(0x450U, offset);
-}
-
-TEST_F(ElfTest, get_global_valid_dynamic_adjust_positive) {
-  ElfFake elf(memory_);
-  elf.FakeSetValid(true);
-
-  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
-  interface->MockSetDynamicOffset(0x1000);
-  interface->MockSetDynamicVaddr(0x800);
-  interface->MockSetDynamicSize(0x100);
-  elf.FakeSetInterface(interface);
-
-  uint64_t offset;
-  std::string global("something");
-  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
-      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true)));
-
-  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
-  EXPECT_EQ(0x1050U, offset);
+  ASSERT_TRUE(elf.GetGlobalVariableOffset(global, &offset));
+  EXPECT_EQ(0xc080U, offset);
 }
 
 TEST_F(ElfTest, is_valid_pc_elf_invalid) {
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
index b1ca111..9b32a3a 100644
--- a/libunwindstack/tests/JitDebugTest.cpp
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -35,13 +35,17 @@
 
 class JitDebugTest : public ::testing::Test {
  protected:
-  void CreateFakeElf(MapInfo* map_info) {
+  void CreateFakeElf(MapInfo* map_info, uint64_t global_offset, uint64_t data_offset,
+                     uint64_t data_vaddr, uint64_t data_size) {
     MemoryFake* memory = new MemoryFake;
     ElfFake* elf = new ElfFake(memory);
     elf->FakeSetValid(true);
     ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
     elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
+    interface->FakeSetGlobalVariable("__jit_debug_descriptor", global_offset);
+    interface->FakeSetDataOffset(data_offset);
+    interface->FakeSetDataVaddrStart(data_vaddr);
+    interface->FakeSetDataVaddrEnd(data_vaddr + data_size);
     map_info->elf.reset(elf);
   }
 
@@ -52,27 +56,27 @@
     maps_.reset(
         new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf1\n"
                        "4000-6000 r--s 00000000 00:00 0 /fake/elf1\n"
-                       "6000-8000 -wxs 00000000 00:00 0 /fake/elf1\n"
+                       "6000-8000 -wxs 00002000 00:00 0 /fake/elf1\n"
                        "a000-c000 --xp 00000000 00:00 0 /fake/elf2\n"
-                       "c000-f000 rw-p 00001000 00:00 0 /fake/elf2\n"
+                       "c000-f000 rw-p 00002000 00:00 0 /fake/elf2\n"
                        "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
-                       "11000-12000 rw-p 00001000 00:00 0 /fake/elf3\n"
+                       "11000-12000 rw-p 00002000 00:00 0 /fake/elf3\n"
                        "12000-14000 r--p 00000000 00:00 0 /fake/elf4\n"
-                       "100000-110000 rw-p 0001000 00:00 0 /fake/elf4\n"
-                       "200000-210000 rw-p 0002000 00:00 0 /fake/elf4\n"));
+                       "100000-110000 rw-p 00ee000 00:00 0 /fake/elf4\n"
+                       "200000-210000 rw-p 01ee000 00:00 0 /fake/elf4\n"));
     ASSERT_TRUE(maps_->Parse());
 
     MapInfo* map_info = maps_->Get(3);
     ASSERT_TRUE(map_info != nullptr);
-    CreateFakeElf(map_info);
+    CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
 
     map_info = maps_->Get(5);
     ASSERT_TRUE(map_info != nullptr);
-    CreateFakeElf(map_info);
+    CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
 
     map_info = maps_->Get(7);
     ASSERT_TRUE(map_info != nullptr);
-    CreateFakeElf(map_info);
+    CreateFakeElf(map_info, 0xee800, 0xee000, 0xee000, 0x10000);
   }
 
   void SetUp() override {
@@ -258,7 +262,7 @@
 TEST_F(JitDebugTest, get_elf_no_valid_code_entry) {
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
 
-  WriteDescriptor32(0xf800, 0x200000);
+  WriteDescriptor32(0x11800, 0x200000);
 
   Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf == nullptr);
@@ -267,7 +271,7 @@
 TEST_F(JitDebugTest, get_elf_invalid_descriptor_first_entry) {
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
 
-  WriteDescriptor32(0xf800, 0);
+  WriteDescriptor32(0x11800, 0);
 
   Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf == nullptr);
@@ -276,9 +280,9 @@
 TEST_F(JitDebugTest, get_elf_invalid_descriptor_version) {
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
 
-  WriteDescriptor32(0xf800, 0x20000);
+  WriteDescriptor32(0x11800, 0x20000);
   // Set the version to an invalid value.
-  memory_->SetData32(0xf800, 2);
+  memory_->SetData32(0x11800, 2);
 
   Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf == nullptr);
@@ -287,7 +291,7 @@
 TEST_F(JitDebugTest, get_elf_32) {
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
 
-  WriteDescriptor32(0xf800, 0x200000);
+  WriteDescriptor32(0x11800, 0x200000);
   WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
 
   Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
@@ -304,16 +308,16 @@
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x5000, ELFCLASS32, EM_ARM, 0x2000, 0x300);
 
-  WriteDescriptor32(0xf800, 0x200000);
+  WriteDescriptor32(0x11800, 0x200000);
   WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
-  WriteDescriptor32(0x12800, 0x201000);
+  WriteDescriptor32(0x100800, 0x201000);
   WriteEntry32Pad(0x201000, 0, 0, 0x5000, 0x1000);
 
   ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
   ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x2000) == nullptr);
 
   // Now clear the descriptor entry for the first one.
-  WriteDescriptor32(0xf800, 0);
+  WriteDescriptor32(0x11800, 0);
   jit_debug_.reset(new JitDebug(process_memory_));
   jit_debug_->SetArch(ARCH_ARM);
 
@@ -326,7 +330,7 @@
 
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
 
-  WriteDescriptor32(0xf800, 0x200000);
+  WriteDescriptor32(0x11800, 0x200000);
   WriteEntry32Pack(0x200000, 0, 0, 0x4000, 0x1000);
 
   jit_debug_->SetArch(ARCH_X86);
@@ -345,7 +349,7 @@
 
   CreateElf<Elf64_Ehdr, Elf64_Shdr>(0x4000, ELFCLASS64, EM_AARCH64, 0x1500, 0x200);
 
-  WriteDescriptor64(0xf800, 0x200000);
+  WriteDescriptor64(0x11800, 0x200000);
   WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000);
 
   Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
@@ -362,7 +366,7 @@
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x5000, ELFCLASS32, EM_ARM, 0x2300, 0x400);
 
-  WriteDescriptor32(0xf800, 0x200000);
+  WriteDescriptor32(0x11800, 0x200000);
   WriteEntry32Pad(0x200000, 0, 0x200100, 0x4000, 0x1000);
   WriteEntry32Pad(0x200100, 0x200100, 0, 0x5000, 0x1000);
 
@@ -385,7 +389,7 @@
 TEST_F(JitDebugTest, get_elf_search_libs) {
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
 
-  WriteDescriptor32(0xf800, 0x200000);
+  WriteDescriptor32(0x11800, 0x200000);
   WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
 
   // Only search a given named list of libs.
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
index 5657373..1ff12db 100644
--- a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
@@ -1,4 +1,4 @@
 d0250000-d2600000 r-xp 0 00:00 0 <anonymous:d0250000>
 e466e000-e4ae8000 r-xp 0 00:00 0 libart.so
-e4ae8000-e4ae9000 rw-p 1000 00:00 0 libart.so
+e4af1000-e4af2000 rw-p 482000 00:00 0 libart.so
 e7d91000-e7e31000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
index 4043122..3b87f2f 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
@@ -4,8 +4,8 @@
 e0447000-e0448000 r-xp 2000 00:00 0   137-cfi.odex
 e2796000-e4796000 r-xp 0 00:00 0   anonymous:e2796000
 e648e000-e690f000 r-xp 0 00:00 0  libart.so
-e690f000-e6910000 rw-p 1000 00:00 0  libart.so
+e6918000-e6919000 rw-p 489000 00:00 0  libart.so
 ed306000-ed801000 r-xp 0 00:00 0   libartd.so
-ed801000-ed802000 rw-p 1000 00:00 0   libartd.so
+ed80a000-ed80b000 rw-p 503000 00:00 0   libartd.so
 eda88000-edb23000 r-xp 0 00:00 0   libc.so
 ede4e000-ede50000 r-xp 0 00:00 0   anonymous:ede4e000
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
index f255a44..c22b5de 100644
--- a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
@@ -4,5 +4,5 @@
 ec606000-ec607000 r-xp 2000 00:00 0   137-cfi.odex
 ee74c000-f074c000 r-xp 0 00:00 0   anonymous:ee74c000
 f6be1000-f732b000 r-xp 0 00:00 0   libartd.so
-f732b000-f732c000 rw-p 1000 00:00 0   libartd.so
+f7334000-f7335000 rw-p 752000 00:00 0   libartd.so
 f734b000-f74fc000 r-xp 0 00:00 0   libc.so
diff --git a/libutils/StrongPointer.cpp b/libutils/StrongPointer.cpp
index ba52502..ef46723 100644
--- a/libutils/StrongPointer.cpp
+++ b/libutils/StrongPointer.cpp
@@ -21,4 +21,7 @@
 namespace android {
 
 void sp_report_race() { LOG_ALWAYS_FATAL("sp<> assignment detected data race"); }
+
+void sp_report_stack_pointer() { LOG_ALWAYS_FATAL("sp<> constructed with stack pointer argument"); }
+
 }
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 9cd7c75..07dd3f1 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -122,26 +122,54 @@
         return o != *this;
     }
 
-private:    
+private:
     template<typename Y> friend class sp;
     template<typename Y> friend class wp;
     void set_pointer(T* ptr);
+    static inline void check_not_on_stack(const void* ptr);
     T* m_ptr;
 };
 
-// For code size reasons, we do not want this inlined or templated.
+// For code size reasons, we do not want these inlined or templated.
 void sp_report_race();
+void sp_report_stack_pointer();
 
 #undef COMPARE
 
 // ---------------------------------------------------------------------------
 // No user serviceable parts below here.
 
+// Check whether address is definitely on the calling stack.  We actually check whether it is on
+// the same 4K page as the frame pointer.
+//
+// Assumptions:
+// - Pages are never smaller than 4K (MIN_PAGE_SIZE)
+// - Malloced memory never shares a page with a stack.
+//
+// It does not appear safe to broaden this check to include adjacent pages; apparently this code
+// is used in environments where there may not be a guard page below (at higher addresses than)
+// the bottom of the stack.
+//
+// TODO: Consider adding make_sp<T>() to allocate an object and wrap the resulting pointer safely
+// without checking overhead.
+template <typename T>
+void sp<T>::check_not_on_stack(const void* ptr) {
+    static constexpr int MIN_PAGE_SIZE = 0x1000;  // 4K. Safer than including sys/user.h.
+    static constexpr uintptr_t MIN_PAGE_MASK = ~static_cast<uintptr_t>(MIN_PAGE_SIZE - 1);
+    uintptr_t my_frame_address =
+            reinterpret_cast<uintptr_t>(__builtin_frame_address(0 /* this frame */));
+    if (((reinterpret_cast<uintptr_t>(ptr) ^ my_frame_address) & MIN_PAGE_MASK) == 0) {
+        sp_report_stack_pointer();
+    }
+}
+
 template<typename T>
 sp<T>::sp(T* other)
         : m_ptr(other) {
-    if (other)
+    if (other) {
+        check_not_on_stack(other);
         other->incStrong(this);
+    }
 }
 
 template<typename T>
@@ -159,8 +187,10 @@
 template<typename T> template<typename U>
 sp<T>::sp(U* other)
         : m_ptr(other) {
-    if (other)
+    if (other) {
+        check_not_on_stack(other);
         (static_cast<T*>(other))->incStrong(this);
+    }
 }
 
 template<typename T> template<typename U>
@@ -207,7 +237,10 @@
 template<typename T>
 sp<T>& sp<T>::operator =(T* other) {
     T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
-    if (other) other->incStrong(this);
+    if (other) {
+        check_not_on_stack(other);
+        other->incStrong(this);
+    }
     if (oldPtr) oldPtr->decStrong(this);
     if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
     m_ptr = other;
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 2d14bf3..02a61a5 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -865,7 +865,7 @@
                         }
                     }
                     // close output and error channels, replace with console
-                    dup2(fd, STDOUT_FILENO);
+                    dup2(fd, output_fd_.get());
                     dup2(fd, STDERR_FILENO);
                     close(fd);
                 }
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 7a13e12..782fb92 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -701,8 +701,6 @@
   # Mount default storage into root namespace
   mount none /mnt/user/0 /storage bind rec
   mount none none /storage slave rec
-  # Bootstrap the emulated volume for the pass_through directory for user 0
-  mount none /data/media /mnt/pass_through/0/emulated bind rec
 on zygote-start && property:persist.sys.fuse=false
   # Mount default storage into root namespace
   mount none /mnt/runtime/default /storage bind rec