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
This commit is contained in:
Christopher Ferris 2018-10-01 21:01:09 -07:00
parent 15a5c9c44f
commit 9d5712c123
20 changed files with 397 additions and 114 deletions

View file

@ -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: [

View file

@ -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",

View file

@ -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) {

View file

@ -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));
});
}

View file

@ -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)) {

View file

@ -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_;
};

View file

@ -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;

View file

@ -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);

View file

@ -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());

View file

@ -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));

View file

@ -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

View file

@ -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)

View file

@ -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_));
}

View file

@ -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);

View file

@ -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++) {

View file

@ -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));
}

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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);