summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2018-10-01 21:01:09 -0700
committerChristopher Ferris <cferris@google.com>2018-10-03 20:48:45 -0700
commit85be654773c87810d474d2dbb898c04a84766c3b (patch)
tree394f2aebadc25fe539efef5b6ee26515e706172a
parent29950c405fb7f5e6703d7055a3050fd3cd377946 (diff)
downloadunwinding-85be654773c87810d474d2dbb898c04a84766c3b.tar.gz
Implement support for linker rosegment option.
The rosegment linker option results in two maps containing the elf data existing. One is an execute map where the code lives, and the other is the read-only segment which contains the elf header information. If the file backing a shared library in memory is not readable, then the new code will attempt to find the read-only map that has the same name as the current execute segment, and that is at offest zero in the file. Add new unit tests for this functionality. Add the missing MapInfoCreateMemoryTest.cpp to the list of tests. Bug: 109657296 Test: Pass new unit tests. Test: All unit libbacktrace/libunwindstack tests pass with rosegment enabled. Change-Id: If8f69e4a067d77b3f2a7c31e2e5cd989a0702a8c
-rw-r--r--libbacktrace/Android.bp4
-rw-r--r--libunwindstack/Android.bp2
-rw-r--r--libunwindstack/MapInfo.cpp49
-rw-r--r--libunwindstack/Maps.cpp6
-rw-r--r--libunwindstack/Memory.cpp12
-rw-r--r--libunwindstack/include/unwindstack/MapInfo.h23
-rw-r--r--libunwindstack/include/unwindstack/Memory.h17
-rw-r--r--libunwindstack/tests/DexFileTest.cpp16
-rw-r--r--libunwindstack/tests/ElfCacheTest.cpp28
-rw-r--r--libunwindstack/tests/ElfTest.cpp2
-rw-r--r--libunwindstack/tests/MapInfoCreateMemoryTest.cpp119
-rw-r--r--libunwindstack/tests/MapInfoGetElfTest.cpp31
-rw-r--r--libunwindstack/tests/MapInfoGetLoadBiasTest.cpp4
-rw-r--r--libunwindstack/tests/MapsTest.cpp4
-rw-r--r--libunwindstack/tests/MemoryFake.cpp11
-rw-r--r--libunwindstack/tests/MemoryFake.h2
-rw-r--r--libunwindstack/tests/MemoryRangeTest.cpp64
-rw-r--r--libunwindstack/tests/MemoryRangesTest.cpp90
-rw-r--r--libunwindstack/tests/RegsTest.cpp2
-rw-r--r--libunwindstack/tests/UnwinderTest.cpp25
20 files changed, 397 insertions, 114 deletions
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 43bcd98..7f9a18a 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -116,10 +116,6 @@ cc_test_library {
target: {
linux_glibc: {
- // The host uses rosegment, which isn't supported yet.
- ldflags: [
- "-Wl,--no-rosegment",
- ],
// This forces the creation of eh_frame with unwind information
// for host.
cflags: [
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index be2145d..970e05c 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -178,6 +178,7 @@ cc_test {
"tests/JitDebugTest.cpp",
"tests/LocalUnwinderTest.cpp",
"tests/LogFake.cpp",
+ "tests/MapInfoCreateMemoryTest.cpp",
"tests/MapInfoGetElfTest.cpp",
"tests/MapInfoGetLoadBiasTest.cpp",
"tests/MapsTest.cpp",
@@ -188,6 +189,7 @@ cc_test {
"tests/MemoryOfflineBufferTest.cpp",
"tests/MemoryOfflineTest.cpp",
"tests/MemoryRangeTest.cpp",
+ "tests/MemoryRangesTest.cpp",
"tests/MemoryRemoteTest.cpp",
"tests/MemoryTest.cpp",
"tests/RegsInfoTest.cpp",
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 39378a3..64005ae 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -102,7 +102,54 @@ Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
if (!(flags & PROT_READ)) {
return nullptr;
}
- return new MemoryRange(process_memory, start, end - start, 0);
+
+ // Need to verify that this elf is valid. It's possible that
+ // only part of the elf file to be mapped into memory is in the executable
+ // map. In this case, there will be another read-only map that includes the
+ // first part of the elf file. This is done if the linker rosegment
+ // option is used.
+ std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0));
+ bool valid;
+ uint64_t max_size;
+ Elf::GetInfo(memory.get(), &valid, &max_size);
+ if (valid) {
+ // Valid elf, we are done.
+ return memory.release();
+ }
+
+ if (name.empty() || maps_ == nullptr) {
+ return nullptr;
+ }
+
+ // Find the read-only map that has the same name and has an offset closest
+ // to the current offset but less than the offset of the current map.
+ // For shared libraries, there should be a r-x map that has a non-zero
+ // offset and then a r-- map that has a zero offset.
+ // For shared libraries loaded from an apk, there should be a r-x map that
+ // has a non-zero offset and then a r-- map that has a non-zero offset less
+ // than the offset from the r-x map.
+ uint64_t closest_offset = 0;
+ MapInfo* ro_map_info = nullptr;
+ for (auto map_info : *maps_) {
+ if (map_info->flags == PROT_READ && map_info->name == name && map_info->offset < offset &&
+ map_info->offset >= closest_offset) {
+ ro_map_info = map_info;
+ closest_offset = ro_map_info->offset;
+ }
+ }
+
+ if (ro_map_info != nullptr) {
+ // Make sure that relative pc values are corrected properly.
+ elf_offset = offset - closest_offset;
+
+ MemoryRanges* ranges = new MemoryRanges;
+ ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
+ ro_map_info->end - ro_map_info->start, 0));
+ ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
+
+ return ranges;
+ }
+ return nullptr;
}
Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index e676a5a..8729871 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -66,13 +66,13 @@ bool Maps::Parse() {
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
+ maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
});
}
void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const std::string& name, uint64_t load_bias) {
- MapInfo* map_info = new MapInfo(start, end, offset, flags, name);
+ MapInfo* map_info = new MapInfo(this, start, end, offset, flags, name);
map_info->load_bias = load_bias;
maps_.push_back(map_info);
}
@@ -97,7 +97,7 @@ bool BufferMaps::Parse() {
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
+ maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
});
}
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index beb2aad..cfa8c6d 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -316,6 +316,18 @@ size_t MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
return memory_->Read(read_addr, dst, read_length);
}
+void MemoryRanges::Insert(MemoryRange* memory) {
+ maps_.emplace(memory->offset() + memory->length(), memory);
+}
+
+size_t MemoryRanges::Read(uint64_t addr, void* dst, size_t size) {
+ auto entry = maps_.upper_bound(addr);
+ if (entry != maps_.end()) {
+ return entry->second->Read(addr, dst, size);
+ }
+ return 0;
+}
+
bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
auto memory_file = std::make_shared<MemoryFileAtOffset>();
if (!memory_file->Init(file, offset)) {
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index ac0df41..9755c48 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -29,20 +29,25 @@
namespace unwindstack {
// Forward declarations.
+class Maps;
class Memory;
struct MapInfo {
- MapInfo() = default;
- MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {}
- MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name)
- : start(start),
+ MapInfo(Maps* maps) : maps_(maps) {}
+ MapInfo(Maps* maps, uint64_t start, uint64_t end) : maps_(maps), start(start), end(end) {}
+ MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const char* name)
+ : maps_(maps),
+ start(start),
end(end),
offset(offset),
flags(flags),
name(name),
load_bias(static_cast<uint64_t>(-1)) {}
- MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name)
- : start(start),
+ MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const std::string& name)
+ : maps_(maps),
+ start(start),
end(end),
offset(offset),
flags(flags),
@@ -50,6 +55,8 @@ struct MapInfo {
load_bias(static_cast<uint64_t>(-1)) {}
~MapInfo() = default;
+ Maps* maps_ = nullptr;
+
uint64_t start = 0;
uint64_t end = 0;
uint64_t offset = 0;
@@ -69,14 +76,14 @@ struct MapInfo {
uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
+ Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
+
private:
MapInfo(const MapInfo&) = delete;
void operator=(const MapInfo&) = delete;
Memory* GetFileMemory();
- Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
-
// Protect the creation of the elf object.
std::mutex mutex_;
};
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index dee5e98..9c425cb 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -22,6 +22,7 @@
#include <unistd.h>
#include <atomic>
+#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -119,6 +120,9 @@ class MemoryRange : public Memory {
size_t Read(uint64_t addr, void* dst, size_t size) override;
+ uint64_t offset() { return offset_; }
+ uint64_t length() { return length_; }
+
private:
std::shared_ptr<Memory> memory_;
uint64_t begin_;
@@ -126,6 +130,19 @@ class MemoryRange : public Memory {
uint64_t offset_;
};
+class MemoryRanges : public Memory {
+ public:
+ MemoryRanges() = default;
+ virtual ~MemoryRanges() = default;
+
+ void Insert(MemoryRange* memory);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ std::map<uint64_t, std::unique_ptr<MemoryRange>> maps_;
+};
+
class MemoryOffline : public Memory {
public:
MemoryOffline() = default;
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 4dd8cb0..40f9f8e 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -120,7 +120,7 @@ TEST(DexFileTest, create_using_file) {
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(0, 0x10000, 0, 0x5, tf.path);
+ MapInfo info(nullptr, 0, 0x10000, 0, 0x5, tf.path);
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x500, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -134,7 +134,7 @@ TEST(DexFileTest, create_using_file_non_zero_start) {
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(0x100, 0x10000, 0, 0x5, tf.path);
+ MapInfo info(nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x600, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -148,7 +148,7 @@ TEST(DexFileTest, create_using_file_non_zero_offset) {
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(0x100, 0x10000, 0x200, 0x5, tf.path);
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x400, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -156,7 +156,7 @@ TEST(DexFileTest, create_using_file_non_zero_offset) {
TEST(DexFileTest, create_using_memory_empty_file) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -164,7 +164,7 @@ TEST(DexFileTest, create_using_memory_empty_file) {
TEST(DexFileTest, create_using_memory_file_does_not_exist) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -178,7 +178,7 @@ TEST(DexFileTest, create_using_memory_file_is_malformed) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
+ MapInfo info(nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
@@ -200,7 +200,7 @@ TEST(DexFileTest, get_method_not_opened) {
TEST(DexFileTest, get_method) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
@@ -227,7 +227,7 @@ TEST(DexFileTest, get_method) {
TEST(DexFileTest, get_method_empty) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 89331ea..1afd4ef 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -79,8 +79,8 @@ void ElfCacheTest::VerifySameMap(bool cache_enabled) {
uint64_t start = 0x1000;
uint64_t end = 0x20000;
- MapInfo info1(start, end, 0, 0x5, tf.path);
- MapInfo info2(start, end, 0, 0x5, tf.path);
+ MapInfo info1(nullptr, start, end, 0, 0x5, tf.path);
+ MapInfo info2(nullptr, start, end, 0, 0x5, tf.path);
Elf* elf1 = info1.GetElf(memory_, true);
ASSERT_TRUE(elf1->valid());
@@ -120,17 +120,17 @@ void ElfCacheTest::VerifyWithinSameMap(bool cache_enabled) {
uint64_t start = 0x1000;
uint64_t end = 0x20000;
// Will have an elf at offset 0 in file.
- MapInfo info0_1(start, end, 0, 0x5, tf.path);
- MapInfo info0_2(start, end, 0, 0x5, tf.path);
+ MapInfo info0_1(nullptr, start, end, 0, 0x5, tf.path);
+ MapInfo info0_2(nullptr, start, end, 0, 0x5, tf.path);
// Will have an elf at offset 0x100 in file.
- MapInfo info100_1(start, end, 0x100, 0x5, tf.path);
- MapInfo info100_2(start, end, 0x100, 0x5, tf.path);
+ MapInfo info100_1(nullptr, start, end, 0x100, 0x5, tf.path);
+ MapInfo info100_2(nullptr, start, end, 0x100, 0x5, tf.path);
// Will have an elf at offset 0x200 in file.
- MapInfo info200_1(start, end, 0x200, 0x5, tf.path);
- MapInfo info200_2(start, end, 0x200, 0x5, tf.path);
+ MapInfo info200_1(nullptr, start, end, 0x200, 0x5, tf.path);
+ MapInfo info200_2(nullptr, start, end, 0x200, 0x5, tf.path);
// Will have an elf at offset 0 in file.
- MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
- MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
Elf* elf0_1 = info0_1.GetElf(memory_, true);
ASSERT_TRUE(elf0_1->valid());
@@ -217,10 +217,10 @@ void ElfCacheTest::VerifyWithinSameMapNeverReadAtZero(bool cache_enabled) {
uint64_t start = 0x1000;
uint64_t end = 0x20000;
// Multiple info sections at different offsets will have non-zero elf offsets.
- MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
- MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
- MapInfo info400_1(start, end, 0x400, 0x5, tf.path);
- MapInfo info400_2(start, end, 0x400, 0x5, tf.path);
+ MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info400_1(nullptr, start, end, 0x400, 0x5, tf.path);
+ MapInfo info400_2(nullptr, start, end, 0x400, 0x5, tf.path);
Elf* elf300_1 = info300_1.GetElf(memory_, true);
ASSERT_TRUE(elf300_1->valid());
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 55fe16f..9a117b2 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -297,7 +297,7 @@ TEST_F(ElfTest, rel_pc) {
elf.FakeSetInterface(interface);
elf.FakeSetValid(true);
- MapInfo map_info(0x1000, 0x2000);
+ MapInfo map_info(nullptr, 0x1000, 0x2000);
ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 866b5b4..2a73c7e 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -32,8 +32,10 @@
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
+#include "ElfTestUtils.h"
#include "MemoryFake.h"
namespace unwindstack {
@@ -94,7 +96,7 @@ TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
TEST_F(MapInfoCreateMemoryTest, end_le_start) {
- MapInfo info(0x100, 0x100, 0, 0, elf_.path);
+ MapInfo info(nullptr, 0x100, 0x100, 0, 0, elf_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
@@ -112,7 +114,7 @@ TEST_F(MapInfoCreateMemoryTest, end_le_start) {
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
- MapInfo info(0x100, 0x200, 0x100, 0, elf_.path);
+ MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -133,7 +135,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
- MapInfo info(0x100, 0x200, 0x100, 0, elf_at_100_.path);
+ MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_at_100_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -156,7 +158,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
// embedded elf is bigger than the initial map, the new object is larger
// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
- MapInfo info(0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
+ MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -172,7 +174,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_e
}
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
- MapInfo info(0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
+ MapInfo info(nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -192,27 +194,24 @@ TEST_F(MapInfoCreateMemoryTest, check_device_maps) {
// Set up some memory so that a valid local memory object would
// be returned if the file mapping fails, but the device check is incorrect.
std::vector<uint8_t> buffer(1024);
- MapInfo info;
- info.start = reinterpret_cast<uint64_t>(buffer.data());
- info.end = info.start + buffer.size();
- info.offset = 0;
+ uint64_t start = reinterpret_cast<uint64_t>(buffer.data());
+ MapInfo info(nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
- info.flags = 0x8000;
- info.name = "/dev/something";
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
}
TEST_F(MapInfoCreateMemoryTest, process_memory) {
- MapInfo info;
- info.start = 0x2000;
- info.end = 0x3000;
- info.offset = 0;
+ MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ std::vector<uint8_t> buffer(1024);
+ memcpy(buffer.data(), &ehdr, sizeof(ehdr));
// Verify that the the process_memory object is used, so seed it
// with memory.
- std::vector<uint8_t> buffer(1024);
- for (size_t i = 0; i < buffer.size(); i++) {
+ for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
buffer[i] = i % 256;
}
memory_->SetMemory(info.start, buffer.data(), buffer.size());
@@ -222,7 +221,8 @@ TEST_F(MapInfoCreateMemoryTest, process_memory) {
memset(buffer.data(), 0, buffer.size());
ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size()));
- for (size_t i = 0; i < buffer.size(); i++) {
+ ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i;
}
@@ -230,4 +230,87 @@ TEST_F(MapInfoCreateMemoryTest, process_memory) {
ASSERT_FALSE(memory->ReadFully(buffer.size(), buffer.data(), 1));
}
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_zero_offset) {
+ Maps maps;
+ maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+ maps.Add(0x1000, 0x2600, 0, PROT_READ, "/only/in/memory.so", 0);
+ maps.Add(0x3000, 0x5000, 0x4000, PROT_READ | PROT_EXEC, "/only/in/memory.so", 0);
+
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x1600 - sizeof(ehdr), 0xab);
+
+ // Set the memory in the r-x map.
+ memory_->SetMemoryBlock(0x3000, 0x2000, 0x5d);
+
+ MapInfo* map_info = maps.Find(0x3000);
+ ASSERT_TRUE(map_info != nullptr);
+
+ std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+ ASSERT_TRUE(mem.get() != nullptr);
+ EXPECT_EQ(0x4000UL, map_info->elf_offset);
+ EXPECT_EQ(0x4000UL, map_info->offset);
+
+ // Verify that reading values from this memory works properly.
+ std::vector<uint8_t> buffer(0x4000);
+ size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+ ASSERT_EQ(0x1600UL, bytes);
+ ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < bytes; i++) {
+ ASSERT_EQ(0xab, buffer[i]) << "Failed at byte " << i;
+ }
+
+ bytes = mem->Read(0x4000, buffer.data(), buffer.size());
+ ASSERT_EQ(0x2000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x5d, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_non_zero_offset) {
+ Maps maps;
+ maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+ maps.Add(0x1000, 0x2000, 0, PROT_READ, "/only/in/memory.apk", 0);
+ maps.Add(0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+ maps.Add(0x3000, 0x4000, 0xa000, PROT_READ, "/only/in/memory.apk", 0);
+ maps.Add(0x4000, 0x5000, 0xb000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+
+ // Setup an elf at offset 0x1000 in memory.
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x2000 - sizeof(ehdr), 0x12);
+ memory_->SetMemoryBlock(0x2000, 0x1000, 0x23);
+
+ // Setup an elf at offset 0x3000 in memory..
+ memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+ memory_->SetMemoryBlock(0x3000 + sizeof(ehdr), 0x4000 - sizeof(ehdr), 0x34);
+ memory_->SetMemoryBlock(0x4000, 0x1000, 0x43);
+
+ MapInfo* map_info = maps.Find(0x4000);
+ ASSERT_TRUE(map_info != nullptr);
+
+ std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+ ASSERT_TRUE(mem.get() != nullptr);
+ EXPECT_EQ(0x1000UL, map_info->elf_offset);
+ EXPECT_EQ(0xb000UL, map_info->offset);
+
+ // Verify that reading values from this memory works properly.
+ std::vector<uint8_t> buffer(0x4000);
+ size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+ ASSERT_EQ(0x1000UL, bytes);
+ ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < bytes; i++) {
+ ASSERT_EQ(0x34, buffer[i]) << "Failed at byte " << i;
+ }
+
+ bytes = mem->Read(0x1000, buffer.data(), buffer.size());
+ ASSERT_EQ(0x1000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x43, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index 861b82f..918c028 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -69,7 +69,7 @@ class MapInfoGetElfTest : public ::testing::Test {
};
TEST_F(MapInfoGetElfTest, invalid) {
- MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
// The map is empty, but this should still create an invalid elf object.
Elf* elf = info.GetElf(process_memory_, false);
@@ -78,7 +78,7 @@ TEST_F(MapInfoGetElfTest, invalid) {
}
TEST_F(MapInfoGetElfTest, valid32) {
- MapInfo info(0x3000, 0x4000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -92,7 +92,7 @@ TEST_F(MapInfoGetElfTest, valid32) {
}
TEST_F(MapInfoGetElfTest, valid64) {
- MapInfo info(0x8000, 0x9000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
Elf64_Ehdr ehdr;
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
@@ -106,7 +106,7 @@ TEST_F(MapInfoGetElfTest, valid64) {
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
- MapInfo info(0x4000, 0x8000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x4000, 0x8000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
[&](uint64_t offset, const void* ptr, size_t size) {
@@ -122,7 +122,7 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
- MapInfo info(0x6000, 0x8000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x6000, 0x8000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
[&](uint64_t offset, const void* ptr, size_t size) {
@@ -138,7 +138,7 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
- MapInfo info(0x2000, 0x3000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
[&](uint64_t offset, const void* ptr, size_t size) {
@@ -154,7 +154,7 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
- MapInfo info(0x5000, 0x8000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
[&](uint64_t offset, const void* ptr, size_t size) {
@@ -170,7 +170,7 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
}
TEST_F(MapInfoGetElfTest, end_le_start) {
- MapInfo info(0x1000, 0x1000, 0, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -197,7 +197,7 @@ TEST_F(MapInfoGetElfTest, end_le_start) {
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
- MapInfo info(0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x1000);
memset(buffer.data(), 0, buffer.size());
@@ -226,7 +226,7 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
- MapInfo info(0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -256,7 +256,7 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
// embedded elf is bigger than the initial map, the new object is larger
// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
- MapInfo info(0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -284,7 +284,7 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32)
}
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
- MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -312,7 +312,7 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64)
}
TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
- MapInfo info(0x9000, 0xa000, 0x1000, 0, "");
+ MapInfo info(nullptr, 0x9000, 0xa000, 0x1000, 0, "");
// Create valid elf data in process memory only.
Elf64_Ehdr ehdr;
@@ -333,7 +333,8 @@ TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
}
TEST_F(MapInfoGetElfTest, check_device_maps) {
- MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP, "/dev/something");
+ MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
+ "/dev/something");
// Create valid elf data in process memory for this to verify that only
// the name is causing invalid elf data.
@@ -378,7 +379,7 @@ TEST_F(MapInfoGetElfTest, multiple_thread_get_elf) {
wait = true;
// Create all of the threads and have them do the GetElf at the same time
// to make it likely that a race will occur.
- MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, "");
+ MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
for (size_t i = 0; i < kNumConcurrentThreads; i++) {
std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
while (wait)
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index 7e64a8a..f5ac6cb 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -50,7 +50,7 @@ class MapInfoGetLoadBiasTest : public ::testing::Test {
process_memory_.reset(memory_);
elf_ = new ElfFake(new MemoryFake);
elf_container_.reset(elf_);
- map_info_.reset(new MapInfo(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
+ map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
}
void MultipleThreadTest(uint64_t expected_load_bias);
@@ -63,7 +63,7 @@ class MapInfoGetLoadBiasTest : public ::testing::Test {
};
TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
- MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
}
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 9622ba5..6bdd0b2 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -63,7 +63,7 @@ TEST(MapsTest, map_add) {
}
TEST(MapsTest, verify_parse_line) {
- MapInfo info;
+ MapInfo info(nullptr);
VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
EXPECT_EQ(1U, info.start);
@@ -136,7 +136,7 @@ TEST(MapsTest, verify_parse_line) {
}
TEST(MapsTest, verify_large_values) {
- MapInfo info;
+ MapInfo info(nullptr);
#if defined(__LP64__)
VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
EXPECT_EQ(0xfabcdef012345678UL, info.start);
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
index 60936cd..5695dfc 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -23,6 +23,17 @@
namespace unwindstack {
+void MemoryFake::SetMemoryBlock(uint64_t addr, size_t length, uint8_t value) {
+ for (size_t i = 0; i < length; i++, addr++) {
+ auto entry = data_.find(addr);
+ if (entry != data_.end()) {
+ entry->second = value;
+ } else {
+ data_.insert({addr, value});
+ }
+ }
+}
+
void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
for (size_t i = 0; i < length; i++, addr++) {
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index 764a6c3..20610a5 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -36,6 +36,8 @@ class MemoryFake : public Memory {
void SetMemory(uint64_t addr, const void* memory, size_t length);
+ void SetMemoryBlock(uint64_t addr, size_t length, uint8_t value);
+
void SetData8(uint64_t addr, uint8_t value) {
SetMemory(addr, &value, sizeof(value));
}
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index cb1a0c9..2bac95b 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -15,7 +15,6 @@
*/
#include <stdint.h>
-#include <string.h>
#include <memory>
#include <vector>
@@ -28,30 +27,34 @@
namespace unwindstack {
-TEST(MemoryRangeTest, read) {
- std::vector<uint8_t> src(1024);
- memset(src.data(), 0x4c, 1024);
- MemoryFake* memory_fake = new MemoryFake;
- std::shared_ptr<Memory> process_memory(memory_fake);
- memory_fake->SetMemory(9001, src);
+class MemoryRangeTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ process_memory_.reset();
+ memory_fake_ = new MemoryFake;
+ process_memory_.reset(memory_fake_);
+ }
+
+ std::shared_ptr<Memory> process_memory_;
+ MemoryFake* memory_fake_ = nullptr;
+};
+
+TEST_F(MemoryRangeTest, read_fully) {
+ memory_fake_->SetMemoryBlock(9000, 2048, 0x4c);
- MemoryRange range(process_memory, 9001, src.size(), 0);
+ MemoryRange range(process_memory_, 9001, 1024, 0);
std::vector<uint8_t> dst(1024);
- ASSERT_TRUE(range.ReadFully(0, dst.data(), src.size()));
- for (size_t i = 0; i < 1024; i++) {
+ ASSERT_TRUE(range.ReadFully(0, dst.data(), dst.size()));
+ for (size_t i = 0; i < dst.size(); i++) {
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
}
}
-TEST(MemoryRangeTest, read_near_limit) {
- std::vector<uint8_t> src(4096);
- memset(src.data(), 0x4c, 4096);
- MemoryFake* memory_fake = new MemoryFake;
- std::shared_ptr<Memory> process_memory(memory_fake);
- memory_fake->SetMemory(1000, src);
+TEST_F(MemoryRangeTest, read_fully_near_limit) {
+ memory_fake_->SetMemoryBlock(0, 8192, 0x4c);
- MemoryRange range(process_memory, 1000, 1024, 0);
+ MemoryRange range(process_memory_, 1000, 1024, 0);
std::vector<uint8_t> dst(1024);
ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
@@ -68,7 +71,7 @@ TEST(MemoryRangeTest, read_near_limit) {
ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
}
-TEST(MemoryRangeTest, read_overflow) {
+TEST_F(MemoryRangeTest, read_fully_overflow) {
std::vector<uint8_t> buffer(100);
std::shared_ptr<Memory> process_memory(new MemoryFakeAlwaysReadZero);
@@ -76,19 +79,28 @@ TEST(MemoryRangeTest, read_overflow) {
ASSERT_FALSE(overflow->ReadFully(UINT64_MAX - 10, buffer.data(), 100));
}
-TEST(MemoryRangeTest, Read) {
- std::vector<uint8_t> src(4096);
- memset(src.data(), 0x4c, 4096);
- MemoryFake* memory_fake = new MemoryFake;
- std::shared_ptr<Memory> process_memory(memory_fake);
- memory_fake->SetMemory(1000, src);
+TEST_F(MemoryRangeTest, read) {
+ memory_fake_->SetMemoryBlock(0, 4096, 0x4c);
+
+ MemoryRange range(process_memory_, 1000, 1024, 0);
- MemoryRange range(process_memory, 1000, 1024, 0);
std::vector<uint8_t> dst(1024);
- ASSERT_EQ(4U, range.Read(1020, dst.data(), 1024));
+ ASSERT_EQ(4U, range.Read(1020, dst.data(), dst.size()));
for (size_t i = 0; i < 4; i++) {
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
}
}
+TEST_F(MemoryRangeTest, read_non_zero_offset) {
+ memory_fake_->SetMemoryBlock(1000, 1024, 0x12);
+
+ MemoryRange range(process_memory_, 1000, 1024, 400);
+
+ std::vector<uint8_t> dst(1024);
+ ASSERT_EQ(1024U, range.Read(400, dst.data(), dst.size()));
+ for (size_t i = 0; i < dst.size(); i++) {
+ ASSERT_EQ(0x12U, dst[i]) << "Failed at byte " << i;
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangesTest.cpp b/libunwindstack/tests/MemoryRangesTest.cpp
new file mode 100644
index 0000000..d24fcd2
--- /dev/null
+++ b/libunwindstack/tests/MemoryRangesTest.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MemoryRangesTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ MemoryFake* memory = new MemoryFake;
+ process_memory_.reset(memory);
+ memory->SetMemoryBlock(1000, 5000, 0x15);
+ memory->SetMemoryBlock(6000, 12000, 0x26);
+ memory->SetMemoryBlock(14000, 20000, 0x37);
+ memory->SetMemoryBlock(20000, 22000, 0x48);
+
+ ranges_.reset(new MemoryRanges);
+ ranges_->Insert(new MemoryRange(process_memory_, 15000, 100, 4000));
+ ranges_->Insert(new MemoryRange(process_memory_, 10000, 2000, 2000));
+ ranges_->Insert(new MemoryRange(process_memory_, 3000, 1000, 0));
+ ranges_->Insert(new MemoryRange(process_memory_, 19000, 1000, 6000));
+ ranges_->Insert(new MemoryRange(process_memory_, 20000, 1000, 7000));
+ }
+
+ std::shared_ptr<Memory> process_memory_;
+ std::unique_ptr<MemoryRanges> ranges_;
+};
+
+TEST_F(MemoryRangesTest, read) {
+ std::vector<uint8_t> dst(2000);
+ size_t bytes = ranges_->Read(0, dst.data(), dst.size());
+ ASSERT_EQ(1000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x15U, dst[i]) << "Failed at byte " << i;
+ }
+
+ bytes = ranges_->Read(2000, dst.data(), dst.size());
+ ASSERT_EQ(2000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x26U, dst[i]) << "Failed at byte " << i;
+ }
+
+ bytes = ranges_->Read(4000, dst.data(), dst.size());
+ ASSERT_EQ(100UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryRangesTest, read_fail) {
+ std::vector<uint8_t> dst(4096);
+ ASSERT_EQ(0UL, ranges_->Read(1000, dst.data(), dst.size()));
+ ASSERT_EQ(0UL, ranges_->Read(5000, dst.data(), dst.size()));
+ ASSERT_EQ(0UL, ranges_->Read(8000, dst.data(), dst.size()));
+}
+
+TEST_F(MemoryRangesTest, read_across_ranges) {
+ // The MemoryRanges object does not support reading across a range,
+ // so this will only read in the first range.
+ std::vector<uint8_t> dst(4096);
+ size_t bytes = ranges_->Read(6000, dst.data(), dst.size());
+ ASSERT_EQ(1000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 90c3fe6..00264c2 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -182,7 +182,7 @@ TEST_F(RegsTest, elf_invalid) {
RegsX86_64 regs_x86_64;
RegsMips regs_mips;
RegsMips64 regs_mips64;
- MapInfo map_info(0x1000, 0x2000);
+ MapInfo map_info(nullptr, 0x1000, 0x2000);
Elf* invalid_elf = new Elf(nullptr);
map_info.elf.reset(invalid_elf);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 2428f68..4369030 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -58,51 +58,54 @@ class UnwinderTest : public ::testing::Test {
protected:
static void SetUpTestCase() {
maps_.FakeClear();
- MapInfo* info = new MapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
+ MapInfo* info =
+ new MapInfo(&maps_, 0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
ElfFake* elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
+ info = new MapInfo(&maps_, 0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+ info = new MapInfo(&maps_, 0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
"/dev/fake_device");
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so");
+ info = new MapInfo(&maps_, 0x20000, 0x22000, 0, PROT_READ | PROT_WRITE,
+ "/system/fake/libunwind.so");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
+ info = new MapInfo(&maps_, 0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
+ info = new MapInfo(&maps_, 0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
+ info = new MapInfo(&maps_, 0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
+ info = new MapInfo(&maps_, 0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+ info = new MapInfo(&maps_, 0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/fake/fake.vdex");
info->load_bias = 0;
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ info = new MapInfo(&maps_, 0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
"/fake/fake_load_bias.so");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
@@ -110,7 +113,7 @@ class UnwinderTest : public ::testing::Test {
elf->FakeSetLoadBias(0x5000);
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ info = new MapInfo(&maps_, 0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
"/fake/fake_offset.oat");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);