Merge "Fix offsets when shared lib split across maps."

This commit is contained in:
Treehugger Robot 2018-12-19 17:11:53 +00:00 committed by Gerrit Code Review
commit 1baa19b1a6
14 changed files with 361 additions and 245 deletions

View File

@ -117,7 +117,7 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
back_frame->map.name = frame->map_name;
back_frame->map.start = frame->map_start;
back_frame->map.end = frame->map_end;
back_frame->map.offset = frame->map_offset;
back_frame->map.offset = frame->map_elf_start_offset;
back_frame->map.load_bias = frame->map_load_bias;
back_frame->map.flags = frame->map_flags;
}

View File

@ -32,33 +32,27 @@ namespace unwindstack {
bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
// One last attempt, see if the previous map is read-only with the
// same name and stretches across this map.
for (auto iter = maps_->begin(); iter != maps_->end(); ++iter) {
if (*iter == this) {
if (iter == maps_->begin()) {
return false;
}
--iter;
MapInfo* prev_map = *iter;
// Make sure this is a read-only map.
if (prev_map->flags != PROT_READ) {
return false;
}
uint64_t map_size = end - prev_map->end;
if (!memory->Init(name, prev_map->offset, map_size)) {
return false;
}
uint64_t max_size;
if (!Elf::GetInfo(memory, &max_size) || max_size < map_size) {
return false;
}
if (!memory->Init(name, prev_map->offset, max_size)) {
return false;
}
elf_offset = offset - prev_map->offset;
return true;
}
if (prev_map == nullptr || prev_map->flags != PROT_READ) {
return false;
}
return false;
uint64_t map_size = end - prev_map->end;
if (!memory->Init(name, prev_map->offset, map_size)) {
return false;
}
uint64_t max_size;
if (!Elf::GetInfo(memory, &max_size) || max_size < map_size) {
return false;
}
if (!memory->Init(name, prev_map->offset, max_size)) {
return false;
}
elf_offset = offset - prev_map->offset;
elf_start_offset = prev_map->offset;
return true;
}
Memory* MapInfo::GetFileMemory() {
@ -91,14 +85,13 @@ Memory* MapInfo::GetFileMemory() {
// Check if the start of this map is an embedded elf.
uint64_t max_size = 0;
uint64_t file_offset = offset;
if (Elf::GetInfo(memory.get(), &max_size)) {
if (max_size > map_size) {
if (memory->Init(name, file_offset, max_size)) {
if (memory->Init(name, offset, max_size)) {
return memory.release();
}
// Try to reinit using the default map_size.
if (memory->Init(name, file_offset, map_size)) {
if (memory->Init(name, offset, map_size)) {
return memory.release();
}
return nullptr;
@ -109,6 +102,13 @@ Memory* MapInfo::GetFileMemory() {
// No elf at offset, try to init as if the whole file is an elf.
if (memory->Init(name, 0) && Elf::IsValidElf(memory.get())) {
elf_offset = offset;
// Need to check how to set the elf start offset. If this map is not
// the r-x map of a r-- map, then use the real offset value. Otherwise,
// use 0.
if (prev_map == nullptr || prev_map->offset != 0 || prev_map->flags != PROT_READ ||
prev_map->name != name) {
elf_start_offset = offset;
}
return memory.release();
}
@ -156,35 +156,24 @@ Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
return memory.release();
}
if (name.empty() || maps_ == nullptr) {
return nullptr;
}
// Find the read-only map by looking at the previous map. The linker
// doesn't guarantee that this invariant will always be true. However,
// if that changes, there is likely something else that will change and
// break something.
MapInfo* ro_map_info = nullptr;
for (auto iter = maps_->begin(); iter != maps_->end(); ++iter) {
if (*iter == this) {
if (iter != maps_->begin()) {
--iter;
ro_map_info = *iter;
}
break;
}
}
if (ro_map_info == nullptr || ro_map_info->name != name || ro_map_info->offset >= offset) {
if (offset == 0 || name.empty() || prev_map == nullptr || prev_map->name != name ||
prev_map->offset >= offset) {
return nullptr;
}
// Make sure that relative pc values are corrected properly.
elf_offset = offset - ro_map_info->offset;
elf_offset = offset - prev_map->offset;
// Use this as the elf start offset, otherwise, you always get offsets into
// the r-x section, which is not quite the right information.
elf_start_offset = prev_map->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, prev_map->start, prev_map->end - prev_map->start, 0));
ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
return ranges;

View File

@ -67,13 +67,15 @@ 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(this, start, end, pgoff, flags, name));
maps_.push_back(
new MapInfo(maps_.empty() ? nullptr : maps_.back(), 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(this, start, end, offset, flags, name);
MapInfo* map_info =
new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, offset, flags, name);
map_info->load_bias = load_bias;
maps_.push_back(map_info);
}
@ -81,6 +83,13 @@ void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
void Maps::Sort() {
std::sort(maps_.begin(), maps_.end(),
[](const MapInfo* a, const MapInfo* b) { return a->start < b->start; });
// Set the prev_map values on the info objects.
MapInfo* prev_map = nullptr;
for (MapInfo* map_info : maps_) {
map_info->prev_map = prev_map;
prev_map = map_info;
}
}
Maps::~Maps() {
@ -98,7 +107,8 @@ 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(this, start, end, pgoff, flags, name));
maps_.push_back(
new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, pgoff, flags, name));
});
}

View File

@ -59,7 +59,8 @@ void Unwinder::FillInDexFrame() {
if (info != nullptr) {
frame->map_start = info->start;
frame->map_end = info->end;
frame->map_offset = info->offset;
frame->map_elf_start_offset = info->elf_start_offset;
frame->map_exact_offset = info->offset;
frame->map_load_bias = info->load_bias;
frame->map_flags = info->flags;
if (resolve_names_) {
@ -102,7 +103,8 @@ void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_
if (resolve_names_) {
frame->map_name = map_info->name;
}
frame->map_offset = map_info->offset;
frame->map_elf_start_offset = map_info->elf_start_offset;
frame->map_exact_offset = map_info->offset;
frame->map_start = map_info->start;
frame->map_end = map_info->end;
frame->map_flags = map_info->flags;
@ -290,10 +292,6 @@ std::string Unwinder::FormatFrame(const FrameData& frame, bool is32bit) {
data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
}
if (frame.map_offset != 0) {
data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_offset);
}
if (frame.map_start == frame.map_end) {
// No valid map associated with this frame.
data += " <unknown>";
@ -302,6 +300,11 @@ std::string Unwinder::FormatFrame(const FrameData& frame, bool is32bit) {
} else {
data += android::base::StringPrintf(" <anonymous:%" PRIx64 ">", frame.map_start);
}
if (frame.map_elf_start_offset != 0) {
data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_elf_start_offset);
}
if (!frame.function_name.empty()) {
data += " (" + frame.function_name;
if (frame.function_offset != 0) {

View File

@ -25,38 +25,31 @@
#include <string>
#include <unwindstack/Elf.h>
#include <unwindstack/Memory.h>
namespace unwindstack {
// Forward declarations.
class Maps;
class Memory;
struct MapInfo {
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,
MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const char* name)
: maps_(maps),
start(start),
: start(start),
end(end),
offset(offset),
flags(flags),
name(name),
prev_map(map_info),
load_bias(static_cast<uint64_t>(-1)) {}
MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const std::string& name)
: maps_(maps),
start(start),
: start(start),
end(end),
offset(offset),
flags(flags),
name(name),
prev_map(map_info),
load_bias(static_cast<uint64_t>(-1)) {}
~MapInfo() = default;
Maps* maps_ = nullptr;
uint64_t start = 0;
uint64_t end = 0;
uint64_t offset = 0;
@ -64,10 +57,14 @@ struct MapInfo {
std::string name;
std::shared_ptr<Elf> elf;
// This value is only non-zero if the offset is non-zero but there is
// no elf signature found at that offset. This indicates that the
// entire file is represented by the Memory object returned by CreateMemory,
// instead of a portion of the file.
// no elf signature found at that offset.
uint64_t elf_offset = 0;
// This value is the offset from the map in memory that is the start
// of the elf. This is not equal to offset when the linker splits
// shared libraries into a read-only and read-execute map.
uint64_t elf_start_offset = 0;
MapInfo* prev_map = nullptr;
std::atomic_uint64_t load_bias;

View File

@ -48,7 +48,13 @@ struct FrameData {
uint64_t function_offset = 0;
std::string map_name;
uint64_t map_offset = 0;
// The offset from the first map representing the frame. When there are
// two maps (read-only and read-execute) this will be the offset from
// the read-only map. When there is only one map, this will be the
// same as the actual offset of the map and match map_exact_offset.
uint64_t map_elf_start_offset = 0;
// The actual offset from the map where the pc lies.
uint64_t map_exact_offset = 0;
uint64_t map_start = 0;
uint64_t map_end = 0;
uint64_t map_load_bias = 0;

View File

@ -269,7 +269,7 @@ TEST_F(ElfTest, rel_pc) {
elf.FakeSetInterface(interface);
elf.FakeSetValid(true);
MapInfo map_info(nullptr, 0x1000, 0x2000);
MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));

View File

@ -118,6 +118,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0x100U, info.elf_offset);
EXPECT_EQ(0x100U, info.elf_start_offset);
// Read the entire file.
std::vector<uint8_t> buffer(1024);
@ -129,6 +130,44 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
}
ASSERT_FALSE(memory->ReadFully(1024, buffer.data(), 1));
// Now verify the elf start offset is set correctly based on the previous
// info.
MapInfo prev_info(nullptr, 0, 0x100, 0x10, 0, "");
info.prev_map = &prev_info;
// No preconditions met, change each one until it should set the elf start
// offset to zero.
info.elf_offset = 0;
info.elf_start_offset = 0;
memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0x100U, info.elf_offset);
EXPECT_EQ(0x100U, info.elf_start_offset);
prev_info.offset = 0;
info.elf_offset = 0;
info.elf_start_offset = 0;
memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0x100U, info.elf_offset);
EXPECT_EQ(0x100U, info.elf_start_offset);
prev_info.flags = PROT_READ;
info.elf_offset = 0;
info.elf_start_offset = 0;
memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0x100U, info.elf_offset);
EXPECT_EQ(0x100U, info.elf_start_offset);
prev_info.name = info.name;
info.elf_offset = 0;
info.elf_start_offset = 0;
memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0x100U, info.elf_offset);
EXPECT_EQ(0U, info.elf_start_offset);
}
// Verify that if the offset is non-zero and there is an elf at that
@ -139,6 +178,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
EXPECT_EQ(0U, info.elf_start_offset);
// Read the valid part of the file.
std::vector<uint8_t> buffer(0x100);
@ -162,6 +202,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_e
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
EXPECT_EQ(0U, info.elf_start_offset);
// Verify the memory is a valid elf.
uint8_t e_ident[SELFMAG + 1];
@ -178,6 +219,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_e
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
EXPECT_EQ(0U, info.elf_start_offset);
// Verify the memory is a valid elf.
uint8_t e_ident[SELFMAG + 1];
@ -250,6 +292,7 @@ TEST_F(MapInfoCreateMemoryTest, valid_rosegment_zero_offset) {
ASSERT_TRUE(mem.get() != nullptr);
EXPECT_EQ(0x4000UL, map_info->elf_offset);
EXPECT_EQ(0x4000UL, map_info->offset);
EXPECT_EQ(0U, map_info->elf_start_offset);
// Verify that reading values from this memory works properly.
std::vector<uint8_t> buffer(0x4000);
@ -295,6 +338,7 @@ TEST_F(MapInfoCreateMemoryTest, valid_rosegment_non_zero_offset) {
ASSERT_TRUE(mem.get() != nullptr);
EXPECT_EQ(0x1000UL, map_info->elf_offset);
EXPECT_EQ(0xb000UL, map_info->offset);
EXPECT_EQ(0xa000UL, map_info->elf_start_offset);
// Verify that reading values from this memory works properly.
std::vector<uint8_t> buffer(0x4000);
@ -333,6 +377,7 @@ TEST_F(MapInfoCreateMemoryTest, rosegment_from_file) {
std::vector<uint8_t> buffer(0x100);
EXPECT_EQ(0x2000U, map_info->offset);
EXPECT_EQ(0U, map_info->elf_offset);
EXPECT_EQ(0U, map_info->elf_start_offset);
ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 0x100));
EXPECT_EQ(0xffU, buffer[0]);
@ -346,6 +391,7 @@ TEST_F(MapInfoCreateMemoryTest, rosegment_from_file) {
memory.reset(map_info->CreateMemory(process_memory_));
EXPECT_EQ(0x2000U, map_info->offset);
EXPECT_EQ(0x1000U, map_info->elf_offset);
EXPECT_EQ(0x1000U, map_info->elf_start_offset);
Elf64_Ehdr ehdr_mem;
ASSERT_TRUE(memory->ReadFully(0, &ehdr_mem, sizeof(ehdr_mem)));
EXPECT_TRUE(memcmp(&ehdr, &ehdr_mem, sizeof(ehdr)) == 0);

View File

@ -62,7 +62,7 @@ TEST(MapsTest, map_add) {
}
TEST(MapsTest, verify_parse_line) {
MapInfo info(nullptr);
MapInfo info(nullptr, 0, 0, 0, 0, "");
VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
EXPECT_EQ(1U, info.start);
@ -135,7 +135,7 @@ TEST(MapsTest, verify_parse_line) {
}
TEST(MapsTest, verify_large_values) {
MapInfo info(nullptr);
MapInfo info(nullptr, 0, 0, 0, 0, "");
#if defined(__LP64__)
VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
EXPECT_EQ(0xfabcdef012345678UL, info.start);

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(nullptr, 0x1000, 0x2000);
MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
Elf* invalid_elf = new Elf(nullptr);
map_info.elf.reset(invalid_elf);

View File

@ -298,7 +298,7 @@ TEST_F(UnwindOfflineTest, jit_debug_x86) {
EXPECT_EQ(
" #00 pc 00068fb8 libarttestd.so (_ZN3artL13CauseSegfaultEv+72)\n"
" #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)\n"
" #02 pc 000021a8 (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
" #02 pc 000021a8 137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
"boolean)+136)\n"
" #03 pc 0000fe80 anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
" #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
@ -591,7 +591,7 @@ TEST_F(UnwindOfflineTest, jit_debug_arm) {
ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+866)\n"
" #01 pc 0000212d (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
" #01 pc 0000212d 137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
"boolean)+92)\n"
" #02 pc 00011cb1 anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
" #03 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
@ -1135,29 +1135,29 @@ TEST_F(UnwindOfflineTest, offset_arm) {
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(19U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 0032bfa0 (offset 0x42000) libunwindstack_test (SignalInnerFunction+40)\n"
" #01 pc 0032bfeb (offset 0x42000) libunwindstack_test (SignalMiddleFunction+2)\n"
" #02 pc 0032bff3 (offset 0x42000) libunwindstack_test (SignalOuterFunction+2)\n"
" #03 pc 0032fed3 (offset 0x42000) libunwindstack_test "
" #00 pc 0032bfa0 libunwindstack_test (SignalInnerFunction+40)\n"
" #01 pc 0032bfeb libunwindstack_test (SignalMiddleFunction+2)\n"
" #02 pc 0032bff3 libunwindstack_test (SignalOuterFunction+2)\n"
" #03 pc 0032fed3 libunwindstack_test "
"(_ZN11unwindstackL19SignalCallerHandlerEiP7siginfoPv+26)\n"
" #04 pc 00026528 (offset 0x25000) libc.so\n"
" #04 pc 00026528 libc.so\n"
" #05 pc 00000000 <unknown>\n"
" #06 pc 0032c2d9 (offset 0x42000) libunwindstack_test (InnerFunction+736)\n"
" #07 pc 0032cc4f (offset 0x42000) libunwindstack_test (MiddleFunction+42)\n"
" #08 pc 0032cc81 (offset 0x42000) libunwindstack_test (OuterFunction+42)\n"
" #09 pc 0032e547 (offset 0x42000) libunwindstack_test "
" #06 pc 0032c2d9 libunwindstack_test (InnerFunction+736)\n"
" #07 pc 0032cc4f libunwindstack_test (MiddleFunction+42)\n"
" #08 pc 0032cc81 libunwindstack_test (OuterFunction+42)\n"
" #09 pc 0032e547 libunwindstack_test "
"(_ZN11unwindstackL19RemoteThroughSignalEij+270)\n"
" #10 pc 0032ed99 (offset 0x42000) libunwindstack_test "
" #10 pc 0032ed99 libunwindstack_test "
"(_ZN11unwindstack55UnwindTest_remote_through_signal_with_invalid_func_Test8TestBodyEv+16)\n"
" #11 pc 00354453 (offset 0x42000) libunwindstack_test (_ZN7testing4Test3RunEv+154)\n"
" #12 pc 00354de7 (offset 0x42000) libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n"
" #13 pc 00355105 (offset 0x42000) libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n"
" #14 pc 0035a215 (offset 0x42000) libunwindstack_test "
" #11 pc 00354453 libunwindstack_test (_ZN7testing4Test3RunEv+154)\n"
" #12 pc 00354de7 libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n"
" #13 pc 00355105 libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n"
" #14 pc 0035a215 libunwindstack_test "
"(_ZN7testing8internal12UnitTestImpl11RunAllTestsEv+664)\n"
" #15 pc 00359f4f (offset 0x42000) libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n"
" #16 pc 0034d3db (offset 0x42000) libunwindstack_test (main+38)\n"
" #17 pc 00092c0d (offset 0x25000) libc.so (__libc_init+48)\n"
" #18 pc 0004202f (offset 0x42000) libunwindstack_test (_start_main+38)\n",
" #15 pc 00359f4f libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n"
" #16 pc 0034d3db libunwindstack_test (main+38)\n"
" #17 pc 00092c0d libc.so (__libc_init+48)\n"
" #18 pc 0004202f libunwindstack_test (_start_main+38)\n",
frame_info);
EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
@ -1248,14 +1248,14 @@ TEST_F(UnwindOfflineTest, shared_lib_in_apk_arm64) {
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 000000000014ccbc (offset 0x39000) linker64 (__dl_syscall+28)\n"
" #01 pc 000000000005426c (offset 0x39000) linker64 "
" #00 pc 000000000014ccbc linker64 (__dl_syscall+28)\n"
" #01 pc 000000000005426c linker64 "
"(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
" #02 pc 00000000000008bc vdso.so\n"
" #03 pc 00000000000846f4 (offset 0x40000) libc.so (abort+172)\n"
" #04 pc 0000000000084ad4 (offset 0x40000) libc.so (__assert2+36)\n"
" #05 pc 000000000003d5b4 (offset 0x40000) ANGLEPrebuilt.apk (ANGLEGetUtilityAPI+56)\n"
" #06 pc 000000000007fe68 (offset 0x40000) libc.so (__libc_init)\n",
" #03 pc 00000000000846f4 libc.so (abort+172)\n"
" #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
" #05 pc 000000000003d5b4 ANGLEPrebuilt.apk (offset 0x4000) (ANGLEGetUtilityAPI+56)\n"
" #06 pc 000000000007fe68 libc.so (__libc_init)\n",
frame_info);
EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
@ -1287,14 +1287,14 @@ TEST_F(UnwindOfflineTest, shared_lib_in_apk_memory_only_arm64) {
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 000000000014ccbc (offset 0x39000) linker64 (__dl_syscall+28)\n"
" #01 pc 000000000005426c (offset 0x39000) linker64 "
" #00 pc 000000000014ccbc linker64 (__dl_syscall+28)\n"
" #01 pc 000000000005426c linker64 "
"(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
" #02 pc 00000000000008bc vdso.so\n"
" #03 pc 00000000000846f4 (offset 0x40000) libc.so (abort+172)\n"
" #04 pc 0000000000084ad4 (offset 0x40000) libc.so (__assert2+36)\n"
" #05 pc 000000000003d5b4 (offset 0x2211000) ANGLEPrebuilt.apk\n"
" #06 pc 000000000007fe68 (offset 0x40000) libc.so (__libc_init)\n",
" #03 pc 00000000000846f4 libc.so (abort+172)\n"
" #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
" #05 pc 000000000003d5b4 ANGLEPrebuilt.apk (offset 0x21d5000)\n"
" #06 pc 000000000007fe68 libc.so (__libc_init)\n",
frame_info);
EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);

View File

@ -42,84 +42,64 @@
namespace unwindstack {
class MapsFake : public Maps {
public:
MapsFake() = default;
virtual ~MapsFake() = default;
bool Parse() { return true; }
void FakeClear() { maps_.clear(); }
void FakeAddMapInfo(MapInfo* map_info) { maps_.push_back(map_info); }
};
class UnwinderTest : public ::testing::Test {
protected:
static void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const char* name, Elf* elf = nullptr) {
std::string str_name(name);
maps_->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
if (elf != nullptr) {
MapInfo* map_info = *--maps_->end();
map_info->elf.reset(elf);
}
}
static void SetUpTestCase() {
maps_.FakeClear();
MapInfo* info =
new MapInfo(&maps_, 0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
maps_.reset(new Maps);
ElfFake* elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
AddMapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so", elf);
info = new MapInfo(&maps_, 0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
maps_.FakeAddMapInfo(info);
AddMapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
info = new MapInfo(&maps_, 0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
"/dev/fake_device");
maps_.FakeAddMapInfo(info);
AddMapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
"/dev/fake_device");
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);
AddMapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so", elf);
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);
AddMapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so", elf);
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);
AddMapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so", elf);
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);
AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
info = new MapInfo(&maps_, 0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
maps_.FakeAddMapInfo(info);
AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
info = new MapInfo(&maps_, 0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
"/fake/fake.vdex");
AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
MapInfo* info = *--maps_->end();
info->load_bias = 0;
maps_.FakeAddMapInfo(info);
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);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
elf->FakeSetLoadBias(0x5000);
maps_.FakeAddMapInfo(info);
AddMapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_load_bias.so",
elf);
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);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
AddMapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_offset.oat",
elf);
info = *--maps_->end();
info->elf_offset = 0x8000;
maps_.FakeAddMapInfo(info);
process_memory_.reset(new MemoryFake);
}
@ -130,12 +110,12 @@ class UnwinderTest : public ::testing::Test {
regs_.FakeSetReturnAddressValid(false);
}
static MapsFake maps_;
static std::unique_ptr<Maps> maps_;
static RegsFake regs_;
static std::shared_ptr<Memory> process_memory_;
};
MapsFake UnwinderTest::maps_;
std::unique_ptr<Maps> UnwinderTest::maps_;
RegsFake UnwinderTest::regs_(5);
std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
@ -150,7 +130,7 @@ TEST_F(UnwinderTest, multiple_frames) {
ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -164,7 +144,8 @@ TEST_F(UnwinderTest, multiple_frames) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -178,7 +159,8 @@ TEST_F(UnwinderTest, multiple_frames) {
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -192,7 +174,8 @@ TEST_F(UnwinderTest, multiple_frames) {
EXPECT_EQ("Frame2", frame->function_name);
EXPECT_EQ(2U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -210,7 +193,7 @@ TEST_F(UnwinderTest, multiple_frames_dont_resolve_names) {
ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.SetResolveNames(false);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -225,7 +208,8 @@ TEST_F(UnwinderTest, multiple_frames_dont_resolve_names) {
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -239,7 +223,8 @@ TEST_F(UnwinderTest, multiple_frames_dont_resolve_names) {
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -253,7 +238,8 @@ TEST_F(UnwinderTest, multiple_frames_dont_resolve_names) {
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -267,7 +253,7 @@ TEST_F(UnwinderTest, non_zero_load_bias) {
regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -281,7 +267,8 @@ TEST_F(UnwinderTest, non_zero_load_bias) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake_load_bias.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa5000U, frame->map_start);
EXPECT_EQ(0xa6000U, frame->map_end);
EXPECT_EQ(0x5000U, frame->map_load_bias);
@ -295,7 +282,7 @@ TEST_F(UnwinderTest, non_zero_elf_offset) {
regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -309,7 +296,8 @@ TEST_F(UnwinderTest, non_zero_elf_offset) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake_offset.oat", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa7000U, frame->map_start);
EXPECT_EQ(0xa8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -323,7 +311,7 @@ TEST_F(UnwinderTest, non_zero_map_offset) {
regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -337,7 +325,8 @@ TEST_F(UnwinderTest, non_zero_map_offset) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.apk", frame->map_name);
EXPECT_EQ(0x1d000U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0x1d000U, frame->map_exact_offset);
EXPECT_EQ(0x43000U, frame->map_start);
EXPECT_EQ(0x44000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -358,7 +347,7 @@ TEST_F(UnwinderTest, no_frames_after_finished) {
ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -372,7 +361,8 @@ TEST_F(UnwinderTest, no_frames_after_finished) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -389,7 +379,7 @@ TEST_F(UnwinderTest, max_frames) {
regs_.set_pc(0x1000);
regs_.set_sp(0x10000);
Unwinder unwinder(20, &maps_, &regs_, process_memory_);
Unwinder unwinder(20, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
@ -404,7 +394,8 @@ TEST_F(UnwinderTest, max_frames) {
EXPECT_EQ("Frame" + std::to_string(i), frame->function_name) << "Failed at frame " << i;
EXPECT_EQ(i, frame->function_offset) << "Failed at frame " << i;
EXPECT_EQ("/system/fake/libc.so", frame->map_name) << "Failed at frame " << i;
EXPECT_EQ(0U, frame->map_offset) << "Failed at frame " << i;
EXPECT_EQ(0U, frame->map_elf_start_offset) << "Failed at frame " << i;
EXPECT_EQ(0U, frame->map_exact_offset) << "Failed at frame " << i;
EXPECT_EQ(0x1000U, frame->map_start) << "Failed at frame " << i;
EXPECT_EQ(0x8000U, frame->map_end) << "Failed at frame " << i;
EXPECT_EQ(0U, frame->map_load_bias) << "Failed at frame " << i;
@ -429,7 +420,7 @@ TEST_F(UnwinderTest, verify_frames_skipped) {
ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10070, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"};
unwinder.Unwind(&skip_libs);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -444,7 +435,8 @@ TEST_F(UnwinderTest, verify_frames_skipped) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -458,7 +450,8 @@ TEST_F(UnwinderTest, verify_frames_skipped) {
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x20000U, frame->map_start);
EXPECT_EQ(0x22000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -472,7 +465,7 @@ TEST_F(UnwinderTest, verify_frames_skipped) {
EXPECT_EQ("Frame2", frame->function_name);
EXPECT_EQ(2U, frame->function_offset);
EXPECT_EQ("/fake/libanother.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x23000U, frame->map_start);
EXPECT_EQ(0x24000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -489,7 +482,7 @@ TEST_F(UnwinderTest, sp_not_in_map) {
ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -503,7 +496,7 @@ TEST_F(UnwinderTest, sp_not_in_map) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -517,7 +510,7 @@ TEST_F(UnwinderTest, sp_not_in_map) {
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x20000U, frame->map_start);
EXPECT_EQ(0x22000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -536,7 +529,7 @@ TEST_F(UnwinderTest, pc_in_device_stops_unwind) {
ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -555,7 +548,7 @@ TEST_F(UnwinderTest, sp_in_device_stops_unwind) {
ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -569,7 +562,7 @@ TEST_F(UnwinderTest, pc_without_map) {
regs_.set_pc(0x41000);
regs_.set_sp(0x13000);
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
@ -583,7 +576,8 @@ TEST_F(UnwinderTest, pc_without_map) {
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -604,7 +598,7 @@ TEST_F(UnwinderTest, speculative_frame) {
ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -618,7 +612,8 @@ TEST_F(UnwinderTest, speculative_frame) {
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -632,7 +627,8 @@ TEST_F(UnwinderTest, speculative_frame) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -646,7 +642,8 @@ TEST_F(UnwinderTest, speculative_frame) {
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/fake/libanother.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x23000U, frame->map_start);
EXPECT_EQ(0x24000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -666,7 +663,7 @@ TEST_F(UnwinderTest, speculative_frame_removed) {
regs_.FakeSetReturnAddress(0x12);
regs_.FakeSetReturnAddressValid(true);
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
@ -680,7 +677,8 @@ TEST_F(UnwinderTest, speculative_frame_removed) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x20000U, frame->map_start);
EXPECT_EQ(0x22000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -694,7 +692,8 @@ TEST_F(UnwinderTest, speculative_frame_removed) {
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -713,7 +712,7 @@ TEST_F(UnwinderTest, speculative_frame_not_removed_pc_bad) {
regs_.FakeSetReturnAddress(0x1202);
regs_.FakeSetReturnAddressValid(true);
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -727,7 +726,8 @@ TEST_F(UnwinderTest, speculative_frame_not_removed_pc_bad) {
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -741,7 +741,8 @@ TEST_F(UnwinderTest, speculative_frame_not_removed_pc_bad) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -762,14 +763,14 @@ TEST_F(UnwinderTest, map_ignore_suffixes) {
ElfInterfaceFake::FakePushStepData(StepData(0x53502, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
std::vector<std::string> suffixes{"oat"};
unwinder.Unwind(nullptr, &suffixes);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
ASSERT_EQ(2U, unwinder.NumFrames());
// Make sure the elf was not initialized.
MapInfo* map_info = maps_.Find(0x53000);
MapInfo* map_info = maps_->Find(0x53000);
ASSERT_TRUE(map_info != nullptr);
EXPECT_TRUE(map_info->elf == nullptr);
@ -781,7 +782,8 @@ TEST_F(UnwinderTest, map_ignore_suffixes) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -795,7 +797,8 @@ TEST_F(UnwinderTest, map_ignore_suffixes) {
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/fake/fake.apk", frame->map_name);
EXPECT_EQ(0x1d000U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0x1d000U, frame->map_exact_offset);
EXPECT_EQ(0x43000U, frame->map_start);
EXPECT_EQ(0x44000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -819,7 +822,7 @@ TEST_F(UnwinderTest, sp_pc_do_not_change) {
ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
@ -833,7 +836,8 @@ TEST_F(UnwinderTest, sp_pc_do_not_change) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -847,7 +851,8 @@ TEST_F(UnwinderTest, sp_pc_do_not_change) {
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/fake/compressed.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x33000U, frame->map_start);
EXPECT_EQ(0x34000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -861,7 +866,8 @@ TEST_F(UnwinderTest, sp_pc_do_not_change) {
EXPECT_EQ("Frame2", frame->function_name);
EXPECT_EQ(2U, frame->function_offset);
EXPECT_EQ("/fake/compressed.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x33000U, frame->map_start);
EXPECT_EQ(0x34000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -874,7 +880,7 @@ TEST_F(UnwinderTest, dex_pc_in_map) {
regs_.set_sp(0x10000);
regs_.FakeSetDexPc(0xa3400);
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -888,7 +894,8 @@ TEST_F(UnwinderTest, dex_pc_in_map) {
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.vdex", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa3000U, frame->map_start);
EXPECT_EQ(0xa4000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -902,7 +909,8 @@ TEST_F(UnwinderTest, dex_pc_in_map) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -915,7 +923,7 @@ TEST_F(UnwinderTest, dex_pc_not_in_map) {
regs_.set_sp(0x10000);
regs_.FakeSetDexPc(0x50000);
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -929,7 +937,8 @@ TEST_F(UnwinderTest, dex_pc_not_in_map) {
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -943,7 +952,8 @@ TEST_F(UnwinderTest, dex_pc_not_in_map) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -959,7 +969,7 @@ TEST_F(UnwinderTest, dex_pc_multiple_frames) {
ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, &regs_, process_memory_);
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@ -973,7 +983,8 @@ TEST_F(UnwinderTest, dex_pc_multiple_frames) {
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.vdex", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa3000U, frame->map_start);
EXPECT_EQ(0xa4000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -987,7 +998,8 @@ TEST_F(UnwinderTest, dex_pc_multiple_frames) {
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -1001,7 +1013,8 @@ TEST_F(UnwinderTest, dex_pc_multiple_frames) {
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/fake/compressed.so", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x33000U, frame->map_start);
EXPECT_EQ(0x34000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -1014,7 +1027,7 @@ TEST_F(UnwinderTest, dex_pc_max_frames) {
regs_.set_sp(0x10000);
regs_.FakeSetDexPc(0xa3400);
Unwinder unwinder(1, &maps_, &regs_, process_memory_);
Unwinder unwinder(1, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
@ -1028,7 +1041,8 @@ TEST_F(UnwinderTest, dex_pc_max_frames) {
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.vdex", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa3000U, frame->map_start);
EXPECT_EQ(0xa4000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@ -1045,17 +1059,17 @@ TEST_F(UnwinderTest, format_frame_static) {
frame.function_name = "function";
frame.function_offset = 100;
frame.map_name = "/fake/libfake.so";
frame.map_offset = 0x2000;
frame.map_elf_start_offset = 0x2000;
frame.map_start = 0x3000;
frame.map_end = 0x6000;
frame.map_flags = PROT_READ;
EXPECT_EQ(" #01 pc 0000000000001000 (offset 0x2000) /fake/libfake.so (function+100)",
EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (offset 0x2000) (function+100)",
Unwinder::FormatFrame(frame, false));
EXPECT_EQ(" #01 pc 00001000 (offset 0x2000) /fake/libfake.so (function+100)",
EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (offset 0x2000) (function+100)",
Unwinder::FormatFrame(frame, true));
frame.map_offset = 0;
frame.map_elf_start_offset = 0;
EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function+100)",
Unwinder::FormatFrame(frame, false));
EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)",
@ -1130,7 +1144,7 @@ TEST_F(UnwinderTest, format_frame) {
for (auto regs : reg_list) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
Unwinder unwinder(64, &maps_, regs, process_memory_);
Unwinder unwinder(64, maps_.get(), regs, process_memory_);
unwinder.Unwind();
ASSERT_EQ(1U, unwinder.NumFrames());

View File

@ -1,2 +1,4 @@
2b2a000-2b6c000 r--p 0 00:00 0 libunwindstack_test
2b6c000-2e92000 r-xp 42000 00:00 0 libunwindstack_test
f4110000-f4135000 r--p 0 00:00 0 libc.so
f4135000-f41a9000 r-xp 25000 00:00 0 libc.so

View File

@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <unistd.h>
@ -46,6 +47,7 @@ struct map_info_t {
uint64_t start;
uint64_t end;
uint64_t offset;
uint64_t flags;
std::string name;
};
@ -163,14 +165,19 @@ bool CreateElfFromMemory(std::shared_ptr<unwindstack::Memory>& memory, map_info_
return true;
}
bool CopyElfFromFile(map_info_t* info) {
bool CopyElfFromFile(map_info_t* info, bool* file_copied) {
std::string cur_name = basename(info->name.c_str());
if (*file_copied) {
info->name = cur_name;
return true;
}
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(info->name.c_str(), "r"), &fclose);
if (fp == nullptr) {
perror((std::string("Cannot open ") + info->name).c_str());
return false;
}
std::string cur_name = basename(info->name.c_str());
std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
if (output == nullptr) {
perror((std::string("Cannot create file " + cur_name)).c_str());
@ -193,6 +200,39 @@ bool CopyElfFromFile(map_info_t* info) {
return true;
}
map_info_t* FillInAndGetMapInfo(std::unordered_map<uint64_t, map_info_t>& maps_by_start,
unwindstack::MapInfo* map_info) {
auto info = &maps_by_start[map_info->start];
info->start = map_info->start;
info->end = map_info->end;
info->offset = map_info->offset;
info->name = map_info->name;
info->flags = map_info->flags;
return info;
}
void SaveMapInformation(std::shared_ptr<unwindstack::Memory>& process_memory, map_info_t* info,
bool* file_copied) {
if (CopyElfFromFile(info, file_copied)) {
return;
}
*file_copied = false;
// Try to create the elf from memory, this will handle cases where
// the data only exists in memory such as vdso data on x86.
if (CreateElfFromMemory(process_memory, info)) {
return;
}
printf("Cannot save memory or file for map ");
if (!info->name.empty()) {
printf("%s\n", info->name.c_str());
} else {
printf("anonymous:%" PRIx64 "\n", info->start);
}
}
int SaveData(pid_t pid) {
unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
if (regs == nullptr) {
@ -237,22 +277,21 @@ int SaveData(pid_t pid) {
}
if (maps_by_start.count(frame.map_start) == 0) {
auto info = &maps_by_start[frame.map_start];
info->start = frame.map_start;
info->end = frame.map_end;
info->offset = frame.map_offset;
info->name = frame.map_name;
if (!CopyElfFromFile(info)) {
// Try to create the elf from memory, this will handle cases where
// the data only exists in memory such as vdso data on x86.
if (!CreateElfFromMemory(process_memory, info)) {
printf("Ignoring map ");
if (!info->name.empty()) {
printf("%s\n", info->name.c_str());
} else {
printf("anonymous:%" PRIx64 "\n", info->start);
}
}
map_info = maps.Find(frame.map_start);
auto info = FillInAndGetMapInfo(maps_by_start, map_info);
bool file_copied = false;
SaveMapInformation(process_memory, info, &file_copied);
// If you are using a a linker that creates two maps (one read-only, one
// read-executable), it's necessary to capture the previous map
// information if needed.
unwindstack::MapInfo* prev_map = map_info->prev_map;
if (prev_map != nullptr && map_info->offset != 0 && prev_map->offset == 0 &&
prev_map->flags == PROT_READ && map_info->name == prev_map->name &&
maps_by_start.count(prev_map->start) == 0) {
info = FillInAndGetMapInfo(maps_by_start, prev_map);
SaveMapInformation(process_memory, info, &file_copied);
}
}
}
@ -277,8 +316,18 @@ int SaveData(pid_t pid) {
}
for (auto& element : sorted_maps) {
char perms[5] = {"---p"};
map_info_t& map = element.second;
fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " r-xp %" PRIx64 " 00:00 0", map.start, map.end,
if (map.flags & PROT_READ) {
perms[0] = 'r';
}
if (map.flags & PROT_WRITE) {
perms[1] = 'w';
}
if (map.flags & PROT_EXEC) {
perms[2] = 'x';
}
fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " 00:00 0", map.start, map.end, perms,
map.offset);
if (!map.name.empty()) {
fprintf(fp.get(), " %s", map.name.c_str());