Add indicator that an elf is memory backed.

Modify the unwinder library to indicate that at least one of the stack
frames contains an elf file that is unreadable.

Modify debuggerd to display a note about the unreadable frame and a possible
way to fix it.

Bug: 129769339

Test: New unit tests pass.
Test: Ran an app that crashes and has an unreadable file and verified the
Test: message is displayed. Then setenforce 0 and verify the message is
Test: not displayed.
Change-Id: Ibc4fe1d117e9b5840290454e90914ddc698d3cc2
Merged-In: Ibc4fe1d117e9b5840290454e90914ddc698d3cc2
(cherry picked from commit 4ae266ccbd)
This commit is contained in:
Christopher Ferris 2019-04-03 09:27:12 -07:00
parent 50b7b4c113
commit b7b0cecce4
11 changed files with 199 additions and 12 deletions

View file

@ -183,6 +183,12 @@ cc_library_static {
],
},
},
product_variables: {
debuggable: {
cflags: ["-DROOT_POSSIBLE"],
},
},
}
cc_test {

View file

@ -74,10 +74,7 @@ void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
return;
}
unwinder->SetDisplayBuildID(true);
for (size_t i = 0; i < unwinder->NumFrames(); i++) {
_LOG(&log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(i).c_str());
}
log_backtrace(&log, unwinder, " ");
}
void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,

View file

@ -73,9 +73,12 @@ typedef uint32_t word_t;
void _LOG(log_t* log, logtype ltype, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
namespace unwindstack {
class Unwinder;
class Memory;
}
void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix);
void dump_memory(log_t* log, unwindstack::Memory* backtrace, uint64_t addr, const std::string&);
void read_with_default(const char* path, char* buf, size_t len, const char* default_value);

View file

@ -371,13 +371,6 @@ static void dump_all_maps(log_t* log, unwindstack::Unwinder* unwinder, uint64_t
}
}
void dump_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
unwinder->SetDisplayBuildID(true);
for (size_t i = 0; i < unwinder->NumFrames(); i++) {
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
}
}
static void print_register_row(log_t* log,
const std::vector<std::pair<std::string, uint64_t>>& registers) {
std::string output;
@ -470,7 +463,7 @@ static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const Threa
_LOG(log, logtype::THREAD, "Failed to unwind");
} else {
_LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
dump_backtrace(log, unwinder, " ");
log_backtrace(log, unwinder, " ");
_LOG(log, logtype::STACK, "\nstack:\n");
dump_stack(log, unwinder->frames(), unwinder->GetMaps(), unwinder->GetProcessMemory().get());

View file

@ -38,6 +38,7 @@
#include <debuggerd/handler.h>
#include <log/log.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Unwinder.h>
using android::base::unique_fd;
@ -422,3 +423,22 @@ const char* get_sigcode(const siginfo_t* si) {
// Then give up...
return "?";
}
void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
if (unwinder->elf_from_memory_not_file()) {
_LOG(log, logtype::BACKTRACE,
"%sNOTE: Function names and BuildId information is missing for some frames due\n", prefix);
_LOG(log, logtype::BACKTRACE,
"%sNOTE: to unreadable libraries. For unwinds of apps, only shared libraries\n", prefix);
_LOG(log, logtype::BACKTRACE, "%sNOTE: found under the lib/ directory are readable.\n", prefix);
#if defined(ROOT_POSSIBLE)
_LOG(log, logtype::BACKTRACE,
"%sNOTE: On this device, run setenforce 0 to make the libraries readable.\n", prefix);
#endif
}
unwinder->SetDisplayBuildID(true);
for (size_t i = 0; i < unwinder->NumFrames(); i++) {
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
}
}

View file

@ -161,6 +161,7 @@ Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
// option is used.
std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0));
if (Elf::IsValidElf(memory.get())) {
memory_backed_elf = true;
return memory.release();
}
@ -184,6 +185,7 @@ Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
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));
memory_backed_elf = true;
return ranges;
}
@ -237,6 +239,7 @@ Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum exp
std::lock_guard<std::mutex> guard(prev_map->mutex_);
if (prev_map->elf.get() == nullptr) {
prev_map->elf = elf;
prev_map->memory_backed_elf = memory_backed_elf;
}
}
return elf.get();

View file

@ -141,6 +141,7 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
frames_.clear();
last_error_.code = ERROR_NONE;
last_error_.address = 0;
elf_from_memory_not_file_ = false;
ArchEnum arch = regs_->Arch();
@ -164,6 +165,12 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
break;
}
elf = map_info->GetElf(process_memory_, arch);
// If this elf is memory backed, and there is a valid file, then set
// an indicator that we couldn't open the file.
if (!elf_from_memory_not_file_ && map_info->memory_backed_elf && !map_info->name.empty() &&
map_info->name[0] != '[') {
elf_from_memory_not_file_ = true;
}
step_pc = regs_->pc();
rel_pc = elf->GetRelPc(step_pc, map_info);
// Everyone except elf data in gdb jit debug maps uses the relative pc.

View file

@ -75,6 +75,9 @@ struct MapInfo {
// make it easier to move to a fine grained lock in the future.
std::atomic_uintptr_t build_id;
// Set to true if the elf file data is coming from memory.
bool memory_backed_elf = false;
// This function guarantees it will never return nullptr.
Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);

View file

@ -111,6 +111,8 @@ class Unwinder {
void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
#endif
bool elf_from_memory_not_file() { return elf_from_memory_not_file_; }
ErrorCode LastErrorCode() { return last_error_.code; }
uint64_t LastErrorAddress() { return last_error_.address; }
@ -132,6 +134,9 @@ class Unwinder {
bool resolve_names_ = true;
bool embedded_soname_ = true;
bool display_build_id_ = false;
// True if at least one elf file is coming from memory and not the related
// file. This is only true if there is an actual file backing up the elf.
bool elf_from_memory_not_file_ = false;
ErrorData last_error_;
};

View file

@ -108,6 +108,7 @@ TEST_F(MapInfoCreateMemoryTest, end_le_start) {
info.end = 0x101;
memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
EXPECT_FALSE(info.memory_backed_elf);
}
// Verify that if the offset is non-zero but there is no elf at the offset,
@ -117,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);
EXPECT_FALSE(info.memory_backed_elf);
ASSERT_EQ(0x100U, info.elf_offset);
EXPECT_EQ(0x100U, info.elf_start_offset);
@ -140,32 +142,40 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
// offset to zero.
info.elf_offset = 0;
info.elf_start_offset = 0;
info.memory_backed_elf = false;
memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
EXPECT_FALSE(info.memory_backed_elf);
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;
info.memory_backed_elf = false;
memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
EXPECT_FALSE(info.memory_backed_elf);
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;
info.memory_backed_elf = false;
memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
EXPECT_FALSE(info.memory_backed_elf);
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;
info.memory_backed_elf = false;
memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
EXPECT_FALSE(info.memory_backed_elf);
ASSERT_EQ(0x100U, info.elf_offset);
EXPECT_EQ(0U, info.elf_start_offset);
}
@ -177,6 +187,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);
EXPECT_FALSE(info.memory_backed_elf);
ASSERT_EQ(0U, info.elf_offset);
EXPECT_EQ(0x1000U, info.elf_start_offset);
@ -201,6 +212,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);
EXPECT_FALSE(info.memory_backed_elf);
ASSERT_EQ(0U, info.elf_offset);
EXPECT_EQ(0x1000U, info.elf_start_offset);
@ -218,6 +230,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);
EXPECT_FALSE(info.memory_backed_elf);
ASSERT_EQ(0U, info.elf_offset);
EXPECT_EQ(0x2000U, info.elf_start_offset);
@ -259,6 +272,7 @@ TEST_F(MapInfoCreateMemoryTest, process_memory) {
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
EXPECT_TRUE(info.memory_backed_elf);
memset(buffer.data(), 0, buffer.size());
ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size()));
@ -290,6 +304,7 @@ TEST_F(MapInfoCreateMemoryTest, valid_rosegment_zero_offset) {
std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
ASSERT_TRUE(mem.get() != nullptr);
EXPECT_TRUE(map_info->memory_backed_elf);
EXPECT_EQ(0x4000UL, map_info->elf_offset);
EXPECT_EQ(0x4000UL, map_info->offset);
EXPECT_EQ(0U, map_info->elf_start_offset);
@ -336,6 +351,7 @@ TEST_F(MapInfoCreateMemoryTest, valid_rosegment_non_zero_offset) {
std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
ASSERT_TRUE(mem.get() != nullptr);
EXPECT_TRUE(map_info->memory_backed_elf);
EXPECT_EQ(0x1000UL, map_info->elf_offset);
EXPECT_EQ(0xb000UL, map_info->offset);
EXPECT_EQ(0xa000UL, map_info->elf_start_offset);
@ -374,6 +390,7 @@ TEST_F(MapInfoCreateMemoryTest, rosegment_from_file) {
// extend over the executable segment.
std::unique_ptr<Memory> memory(map_info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
EXPECT_FALSE(map_info->memory_backed_elf);
std::vector<uint8_t> buffer(0x100);
EXPECT_EQ(0x2000U, map_info->offset);
EXPECT_EQ(0U, map_info->elf_offset);
@ -388,7 +405,9 @@ TEST_F(MapInfoCreateMemoryTest, rosegment_from_file) {
ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET));
ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr)));
map_info->memory_backed_elf = false;
memory.reset(map_info->CreateMemory(process_memory_));
EXPECT_FALSE(map_info->memory_backed_elf);
EXPECT_EQ(0x2000U, map_info->offset);
EXPECT_EQ(0x1000U, map_info->elf_offset);
EXPECT_EQ(0x1000U, map_info->elf_start_offset);

View file

@ -108,6 +108,24 @@ class UnwinderTest : public ::testing::Test {
const auto& info2 = *--maps_->end();
info2->elf_offset = 0x8000;
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
AddMapInfo(0xc0000, 0xc1000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/unreadable.so", elf);
const auto& info3 = *--maps_->end();
info3->memory_backed_elf = true;
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
AddMapInfo(0xc1000, 0xc2000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "[vdso]", elf);
const auto& info4 = *--maps_->end();
info4->memory_backed_elf = true;
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
AddMapInfo(0xc2000, 0xc3000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "", elf);
const auto& info5 = *--maps_->end();
info5->memory_backed_elf = true;
process_memory_.reset(new MemoryFake);
}
@ -140,6 +158,7 @@ TEST_F(UnwinderTest, multiple_frames) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@ -204,6 +223,7 @@ TEST_F(UnwinderTest, multiple_frames_dont_resolve_names) {
unwinder.SetResolveNames(false);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@ -263,6 +283,7 @@ TEST_F(UnwinderTest, non_zero_load_bias) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@ -292,6 +313,7 @@ TEST_F(UnwinderTest, non_zero_elf_offset) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@ -321,6 +343,7 @@ TEST_F(UnwinderTest, non_zero_map_offset) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@ -351,6 +374,7 @@ TEST_F(UnwinderTest, disable_embedded_soname) {
unwinder.SetEmbeddedSoname(false);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@ -387,6 +411,7 @@ TEST_F(UnwinderTest, no_frames_after_finished) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@ -419,6 +444,7 @@ TEST_F(UnwinderTest, max_frames) {
Unwinder unwinder(20, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(20U, unwinder.NumFrames());
@ -461,6 +487,7 @@ TEST_F(UnwinderTest, verify_frames_skipped) {
std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"};
unwinder.Unwind(&skip_libs);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@ -522,6 +549,7 @@ TEST_F(UnwinderTest, sp_not_in_map) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@ -569,6 +597,7 @@ TEST_F(UnwinderTest, pc_in_device_stops_unwind) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
}
@ -588,6 +617,7 @@ TEST_F(UnwinderTest, sp_in_device_stops_unwind) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
}
@ -602,6 +632,7 @@ TEST_F(UnwinderTest, pc_without_map) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@ -638,6 +669,7 @@ TEST_F(UnwinderTest, speculative_frame) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@ -703,6 +735,7 @@ TEST_F(UnwinderTest, speculative_frame_removed) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@ -752,6 +785,7 @@ TEST_F(UnwinderTest, speculative_frame_not_removed_pc_bad) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@ -799,6 +833,7 @@ TEST_F(UnwinderTest, speculative_frame_check_with_no_frames) {
std::vector<std::string> skip_names{"libanother.so"};
unwinder.Unwind(&skip_names);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(0U, unwinder.NumFrames());
}
@ -821,6 +856,7 @@ TEST_F(UnwinderTest, map_ignore_suffixes) {
std::vector<std::string> suffixes{"oat"};
unwinder.Unwind(nullptr, &suffixes);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
// Make sure the elf was not initialized.
@ -879,6 +915,7 @@ TEST_F(UnwinderTest, sp_pc_do_not_change) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@ -937,6 +974,7 @@ TEST_F(UnwinderTest, dex_pc_in_map) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@ -980,6 +1018,7 @@ TEST_F(UnwinderTest, dex_pc_not_in_map) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@ -1026,6 +1065,7 @@ TEST_F(UnwinderTest, dex_pc_multiple_frames) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@ -1084,6 +1124,7 @@ TEST_F(UnwinderTest, dex_pc_max_frames) {
Unwinder unwinder(1, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@ -1103,6 +1144,96 @@ TEST_F(UnwinderTest, dex_pc_max_frames) {
EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
}
TEST_F(UnwinderTest, elf_from_memory_not_file) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
regs_.set_pc(0xc0050);
regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_TRUE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
auto* frame = &unwinder.frames()[0];
EXPECT_EQ(0U, frame->num);
EXPECT_EQ(0x50U, frame->rel_pc);
EXPECT_EQ(0xc0050U, frame->pc);
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/unreadable.so", frame->map_name);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xc0000U, frame->map_start);
EXPECT_EQ(0xc1000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
}
TEST_F(UnwinderTest, elf_from_memory_but_no_valid_file_with_bracket) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
regs_.set_pc(0xc1050);
regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
auto* frame = &unwinder.frames()[0];
EXPECT_EQ(0U, frame->num);
EXPECT_EQ(0x50U, frame->rel_pc);
EXPECT_EQ(0xc1050U, frame->pc);
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("[vdso]", frame->map_name);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xc1000U, frame->map_start);
EXPECT_EQ(0xc2000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
}
TEST_F(UnwinderTest, elf_from_memory_but_empty_filename) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
regs_.set_pc(0xc2050);
regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
auto* frame = &unwinder.frames()[0];
EXPECT_EQ(0U, frame->num);
EXPECT_EQ(0x50U, frame->rel_pc);
EXPECT_EQ(0xc2050U, frame->pc);
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
EXPECT_EQ(0U, frame->map_elf_start_offset);
EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xc2000U, frame->map_start);
EXPECT_EQ(0xc3000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
}
// Verify format frame code.
TEST_F(UnwinderTest, format_frame) {
RegsFake regs_arm(10);