Merge "Implement support for linker rosegment option."
This commit is contained in:
commit
d3a7ddcf8d
|
@ -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: [
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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_));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
|
||||
MemoryRange range(process_memory, 9001, src.size(), 0);
|
||||
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, 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
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue