diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp index 0cf3378b1..2e226da6c 100644 --- a/debuggerd/Android.bp +++ b/debuggerd/Android.bp @@ -183,6 +183,12 @@ cc_library_static { ], }, }, + + product_variables: { + debuggable: { + cflags: ["-DROOT_POSSIBLE"], + }, + }, } cc_test { diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp index 94fcfb28c..c60697099 100644 --- a/debuggerd/libdebuggerd/backtrace.cpp +++ b/debuggerd/libdebuggerd/backtrace.cpp @@ -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, diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h index 238c00c5d..f189c451b 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h @@ -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); diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp index cc337ed30..d1726cdee 100644 --- a/debuggerd/libdebuggerd/tombstone.cpp +++ b/debuggerd/libdebuggerd/tombstone.cpp @@ -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>& 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()); diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp index 7aebea8fe..9b2779a9e 100644 --- a/debuggerd/libdebuggerd/utility.cpp +++ b/debuggerd/libdebuggerd/utility.cpp @@ -38,6 +38,7 @@ #include #include #include +#include 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()); + } +} diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp index 28373b27f..03658b445 100644 --- a/libunwindstack/MapInfo.cpp +++ b/libunwindstack/MapInfo.cpp @@ -161,6 +161,7 @@ Memory* MapInfo::CreateMemory(const std::shared_ptr& process_memory) { // option is used. std::unique_ptr 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& 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& process_memory, ArchEnum exp std::lock_guard 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(); diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp index f3d2b5e1c..26626b5cd 100644 --- a/libunwindstack/Unwinder.cpp +++ b/libunwindstack/Unwinder.cpp @@ -141,6 +141,7 @@ void Unwinder::Unwind(const std::vector* 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* 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. diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h index e938986d8..025fd98da 100644 --- a/libunwindstack/include/unwindstack/MapInfo.h +++ b/libunwindstack/include/unwindstack/MapInfo.h @@ -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& process_memory, ArchEnum expected_arch); diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h index 75be209a3..52b3578d8 100644 --- a/libunwindstack/include/unwindstack/Unwinder.h +++ b/libunwindstack/include/unwindstack/Unwinder.h @@ -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_; }; diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp index 2ddadef37..6be8bdccb 100644 --- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp +++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp @@ -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(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(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(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(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(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 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 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(map_info->CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); + EXPECT_FALSE(map_info->memory_backed_elf); std::vector 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); diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp index 48e038e9a..f6350216f 100644 --- a/libunwindstack/tests/UnwinderTest.cpp +++ b/libunwindstack/tests/UnwinderTest.cpp @@ -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(), ®s_, 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(), ®s_, 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(), ®s_, 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(), ®s_, 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(), ®s_, 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(), ®s_, 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 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(), ®s_, 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(), ®s_, 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(), ®s_, 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(), ®s_, 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(), ®s_, 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(), ®s_, 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(), ®s_, 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 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 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(), ®s_, 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(), ®s_, 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(), ®s_, 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(), ®s_, 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(), ®s_, 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(), ®s_, 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(), ®s_, 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(), ®s_, 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);