From b7b0cecce43022a31d99622be0591e9f900fa80c Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 3 Apr 2019 09:27:12 -0700 Subject: [PATCH] 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 4ae266ccbddbd0a6529248ecd1b324feab261c0d) --- debuggerd/Android.bp | 6 + debuggerd/libdebuggerd/backtrace.cpp | 5 +- .../include/libdebuggerd/utility.h | 3 + debuggerd/libdebuggerd/tombstone.cpp | 9 +- debuggerd/libdebuggerd/utility.cpp | 20 +++ libunwindstack/MapInfo.cpp | 3 + libunwindstack/Unwinder.cpp | 7 + libunwindstack/include/unwindstack/MapInfo.h | 3 + libunwindstack/include/unwindstack/Unwinder.h | 5 + .../tests/MapInfoCreateMemoryTest.cpp | 19 +++ libunwindstack/tests/UnwinderTest.cpp | 131 ++++++++++++++++++ 11 files changed, 199 insertions(+), 12 deletions(-) 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);