Merge "Implement support for linker rosegment option."

This commit is contained in:
Christopher Ferris 2018-10-04 22:38:26 +00:00 committed by Gerrit Code Review
commit d3a7ddcf8d
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);