Merge "Dup the correct fd for qemu pipe."
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/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;