diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 21dd306e7..5e9438870 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -167,6 +167,7 @@ cc_test { data: [ "tests/files/elf32.xz", "tests/files/elf64.xz", + "tests/files/offline/gnu_debugdata_arm32/*", "tests/files/offline/straddle_arm32/*", "tests/files/offline/straddle_arm64/*", ], diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp index f486e2314..5ec4a3d2e 100644 --- a/libunwindstack/Elf.cpp +++ b/libunwindstack/Elf.cpp @@ -79,6 +79,7 @@ void Elf::InitGnuDebugdata() { uint64_t load_bias; if (gnu->Init(&load_bias)) { gnu->InitHeaders(); + interface_->SetGnuDebugdataInterface(gnu); } else { // Free all of the memory associated with the gnu_debugdata section. gnu_debugdata_memory_.reset(nullptr); @@ -115,17 +116,9 @@ bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, R return true; } - // Adjust the load bias to get the real relative pc. - if (adjusted_rel_pc < load_bias_) { - return false; - } - adjusted_rel_pc -= load_bias_; - // Lock during the step which can update information in the object. std::lock_guard guard(lock_); - return interface_->Step(adjusted_rel_pc, regs, process_memory, finished) || - (gnu_debugdata_interface_ && - gnu_debugdata_interface_->Step(adjusted_rel_pc, regs, process_memory, finished)); + return interface_->Step(adjusted_rel_pc, load_bias_, regs, process_memory, finished); } bool Elf::IsValidElf(Memory* memory) { diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp index 334cf76f6..df1642e18 100644 --- a/libunwindstack/ElfInterface.cpp +++ b/libunwindstack/ElfInterface.cpp @@ -386,16 +386,29 @@ bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias return false; } -bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) { +bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory, + bool* finished) { + // Adjust the load bias to get the real relative pc. + if (pc < load_bias) { + return false; + } + uint64_t adjusted_pc = pc - load_bias; + // Try the eh_frame first. DwarfSection* eh_frame = eh_frame_.get(); - if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) { + if (eh_frame != nullptr && eh_frame->Step(adjusted_pc, regs, process_memory, finished)) { return true; } // Try the debug_frame next. DwarfSection* debug_frame = debug_frame_.get(); - if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory, finished)) { + if (debug_frame != nullptr && debug_frame->Step(adjusted_pc, regs, process_memory, finished)) { + return true; + } + + // Finally try the gnu_debugdata interface, but always use a zero load bias. + if (gnu_debugdata_interface_ != nullptr && + gnu_debugdata_interface_->Step(pc, 0, regs, process_memory, finished)) { return true; } return false; diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp index 9841e2474..5d99bd791 100644 --- a/libunwindstack/ElfInterfaceArm.cpp +++ b/libunwindstack/ElfInterfaceArm.cpp @@ -92,16 +92,24 @@ bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type, uint64_t load_b return true; } -bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) { +bool ElfInterfaceArm::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory, + bool* finished) { // Dwarf unwind information is precise about whether a pc is covered or not, // but arm unwind information only has ranges of pc. In order to avoid // incorrectly doing a bad unwind using arm unwind information for a // different function, always try and unwind with the dwarf information first. - return ElfInterface32::Step(pc, regs, process_memory, finished) || - StepExidx(pc, regs, process_memory, finished); + return ElfInterface32::Step(pc, load_bias, regs, process_memory, finished) || + StepExidx(pc, load_bias, regs, process_memory, finished); } -bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) { +bool ElfInterfaceArm::StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory, + bool* finished) { + // Adjust the load bias to get the real relative pc. + if (pc < load_bias) { + return false; + } + pc -= load_bias; + RegsArm* regs_arm = reinterpret_cast(regs); uint64_t entry_offset; if (!FindEntry(pc, &entry_offset)) { diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h index eeb2e1770..9c067ba3a 100644 --- a/libunwindstack/ElfInterfaceArm.h +++ b/libunwindstack/ElfInterfaceArm.h @@ -70,9 +70,11 @@ class ElfInterfaceArm : public ElfInterface32 { bool HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) override; - bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override; + bool Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory, + bool* finished) override; - bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished); + bool StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory, + bool* finished); uint64_t start_offset() { return start_offset_; } diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp index 51bce8eea..89fe038dc 100644 --- a/libunwindstack/MapInfo.cpp +++ b/libunwindstack/MapInfo.cpp @@ -122,13 +122,21 @@ Elf* MapInfo::GetElf(const std::shared_ptr& process_memory, bool init_gn } uint64_t MapInfo::GetLoadBias(const std::shared_ptr& process_memory) { + uint64_t cur_load_bias = load_bias.load(); + if (cur_load_bias != static_cast(-1)) { + return cur_load_bias; + } + { // Make sure no other thread is trying to add the elf to this map. std::lock_guard guard(mutex_); if (elf != nullptr) { if (elf->valid()) { - return elf->GetLoadBias(); + cur_load_bias = elf->GetLoadBias(); + load_bias = cur_load_bias; + return cur_load_bias; } else { + load_bias = 0; return 0; } } @@ -137,7 +145,9 @@ uint64_t MapInfo::GetLoadBias(const std::shared_ptr& process_memory) { // Call lightweight static function that will only read enough of the // elf data to get the load bias. std::unique_ptr memory(CreateMemory(process_memory)); - return Elf::GetLoadBias(memory.get()); + cur_load_bias = Elf::GetLoadBias(memory.get()); + load_bias = cur_load_bias; + return cur_load_bias; } } // namespace unwindstack diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp index 56370c196..4c1621284 100644 --- a/libunwindstack/Maps.cpp +++ b/libunwindstack/Maps.cpp @@ -202,6 +202,13 @@ bool Maps::Parse() { return return_value; } +void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, + const std::string& name, uint64_t load_bias) { + MapInfo* map_info = new MapInfo(start, end, offset, flags, name); + map_info->load_bias = load_bias; + maps_.push_back(map_info); +} + Maps::~Maps() { for (auto& map : maps_) { delete map; @@ -235,61 +242,4 @@ const std::string RemoteMaps::GetMapsFile() const { return "/proc/" + std::to_string(pid_) + "/maps"; } -bool OfflineMaps::Parse() { - // Format of maps information: - // StartOffset - // EndOffset - // offset - // flags - // MapNameLength - // MapName - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_.c_str(), O_RDONLY))); - if (fd == -1) { - return false; - } - - std::vector name; - while (true) { - uint64_t start; - ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &start, sizeof(start))); - if (bytes == 0) { - break; - } - if (bytes == -1 || bytes != sizeof(start)) { - return false; - } - uint64_t end; - bytes = TEMP_FAILURE_RETRY(read(fd, &end, sizeof(end))); - if (bytes == -1 || bytes != sizeof(end)) { - return false; - } - uint64_t offset; - bytes = TEMP_FAILURE_RETRY(read(fd, &offset, sizeof(offset))); - if (bytes == -1 || bytes != sizeof(offset)) { - return false; - } - uint16_t flags; - bytes = TEMP_FAILURE_RETRY(read(fd, &flags, sizeof(flags))); - if (bytes == -1 || bytes != sizeof(flags)) { - return false; - } - uint16_t len; - bytes = TEMP_FAILURE_RETRY(read(fd, &len, sizeof(len))); - if (bytes == -1 || bytes != sizeof(len)) { - return false; - } - if (len > 0) { - name.resize(len); - bytes = TEMP_FAILURE_RETRY(read(fd, name.data(), len)); - if (bytes == -1 || bytes != len) { - return false; - } - maps_.push_back(new MapInfo(start, end, offset, flags, std::string(name.data(), len))); - } else { - maps_.push_back(new MapInfo(start, end, offset, flags, "")); - } - } - return true; -} - } // namespace unwindstack diff --git a/libunwindstack/UcontextX86_64.h b/libunwindstack/UcontextX86_64.h index d689796bf..2b8bdc4f0 100644 --- a/libunwindstack/UcontextX86_64.h +++ b/libunwindstack/UcontextX86_64.h @@ -38,6 +38,7 @@ namespace unwindstack { struct x86_64_stack_t { uint64_t ss_sp; // void __user* int32_t ss_flags; // int + int32_t pad; uint64_t ss_size; // size_t }; diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h index 5cfe74dae..5d3cd5ebd 100644 --- a/libunwindstack/include/unwindstack/ElfInterface.h +++ b/libunwindstack/include/unwindstack/ElfInterface.h @@ -60,7 +60,8 @@ class ElfInterface { virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name, uint64_t* offset) = 0; - virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished); + virtual bool Step(uint64_t rel_pc, uint64_t load_bias, Regs* regs, Memory* process_memory, + bool* finished); Memory* CreateGnuDebugdataMemory(); @@ -68,6 +69,8 @@ class ElfInterface { const std::unordered_map& pt_loads() { return pt_loads_; } + void SetGnuDebugdataInterface(ElfInterface* interface) { gnu_debugdata_interface_ = interface; } + uint64_t dynamic_offset() { return dynamic_offset_; } uint64_t dynamic_size() { return dynamic_size_; } uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; } @@ -134,6 +137,8 @@ class ElfInterface { std::unique_ptr eh_frame_; std::unique_ptr debug_frame_; + // The Elf object owns the gnu_debugdata interface object. + ElfInterface* gnu_debugdata_interface_ = nullptr; std::vector symbols_; }; diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h index 6f8ceca51..22e48f71c 100644 --- a/libunwindstack/include/unwindstack/MapInfo.h +++ b/libunwindstack/include/unwindstack/MapInfo.h @@ -19,6 +19,7 @@ #include +#include #include #include @@ -33,7 +34,12 @@ struct MapInfo { MapInfo() = default; MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {} MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name) - : start(start), end(end), offset(offset), flags(flags), name(name) {} + : start(start), + end(end), + offset(offset), + flags(flags), + name(name), + load_bias(static_cast(-1)) {} ~MapInfo() { delete elf; } uint64_t start = 0; @@ -48,6 +54,8 @@ struct MapInfo { // instead of a portion of the file. uint64_t elf_offset = 0; + std::atomic_uint64_t load_bias; + // This function guarantees it will never return nullptr. Elf* GetElf(const std::shared_ptr& process_memory, bool init_gnu_debugdata = false); diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h index 34fef7fac..17a2d28ae 100644 --- a/libunwindstack/include/unwindstack/Maps.h +++ b/libunwindstack/include/unwindstack/Maps.h @@ -42,6 +42,9 @@ class Maps { virtual const std::string GetMapsFile() const { return ""; } + void Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name, + uint64_t load_bias); + typedef std::vector::iterator iterator; iterator begin() { return maps_.begin(); } iterator end() { return maps_.end(); } @@ -100,14 +103,6 @@ class FileMaps : public Maps { const std::string file_; }; -class OfflineMaps : public FileMaps { - public: - OfflineMaps(const std::string& file) : FileMaps(file) {} - virtual ~OfflineMaps() = default; - - bool Parse() override; -}; - } // namespace unwindstack #endif // _LIBUNWINDSTACK_MAPS_H diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp index b94a8a41d..68de797f5 100644 --- a/libunwindstack/tests/ElfFake.cpp +++ b/libunwindstack/tests/ElfFake.cpp @@ -43,7 +43,7 @@ bool ElfInterfaceFake::GetFunctionName(uint64_t, uint64_t, std::string* name, ui return true; } -bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) { +bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) { if (steps_.empty()) { return false; } diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h index 565b13f1d..abf992759 100644 --- a/libunwindstack/tests/ElfFake.h +++ b/libunwindstack/tests/ElfFake.h @@ -68,8 +68,7 @@ class ElfInterfaceFake : public ElfInterface { bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override; - bool Step(uint64_t, Regs*, Memory*, bool*) override; - + bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override; static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); } static void FakePushStepData(const StepData data) { steps_.push_back(data); } diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp index 5f7cf6097..e6763ab01 100644 --- a/libunwindstack/tests/ElfInterfaceArmTest.cpp +++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp @@ -302,7 +302,7 @@ TEST_F(ElfInterfaceArmTest, StepExidx) { // FindEntry fails. bool finished; - ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished)); + ASSERT_FALSE(interface.StepExidx(0x7000, 0, nullptr, nullptr, &finished)); // ExtractEntry should fail. interface.FakeSetStartOffset(0x1000); @@ -315,20 +315,26 @@ TEST_F(ElfInterfaceArmTest, StepExidx) { regs[ARM_REG_LR] = 0x20000; regs.set_sp(regs[ARM_REG_SP]); regs.set_pc(0x1234); - ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished)); + ASSERT_FALSE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished)); // Eval should fail. memory_.SetData32(0x1004, 0x81000000); - ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished)); + ASSERT_FALSE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished)); // Everything should pass. memory_.SetData32(0x1004, 0x80b0b0b0); - ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished)); + ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished)); ASSERT_FALSE(finished); ASSERT_EQ(0x1000U, regs.sp()); ASSERT_EQ(0x1000U, regs[ARM_REG_SP]); ASSERT_EQ(0x20000U, regs.pc()); ASSERT_EQ(0x20000U, regs[ARM_REG_PC]); + + // Load bias is non-zero. + ASSERT_TRUE(interface.StepExidx(0x8000, 0x1000, ®s, &process_memory_, &finished)); + + // Pc too small. + ASSERT_FALSE(interface.StepExidx(0x8000, 0x9000, ®s, &process_memory_, &finished)); } TEST_F(ElfInterfaceArmTest, StepExidx_pc_set) { @@ -349,7 +355,7 @@ TEST_F(ElfInterfaceArmTest, StepExidx_pc_set) { // Everything should pass. bool finished; - ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished)); + ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished)); ASSERT_FALSE(finished); ASSERT_EQ(0x10004U, regs.sp()); ASSERT_EQ(0x10004U, regs[ARM_REG_SP]); @@ -372,7 +378,7 @@ TEST_F(ElfInterfaceArmTest, StepExidx_cant_unwind) { regs.set_pc(0x1234); bool finished; - ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished)); + ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished)); ASSERT_TRUE(finished); ASSERT_EQ(0x10000U, regs.sp()); ASSERT_EQ(0x10000U, regs[ARM_REG_SP]); @@ -394,7 +400,7 @@ TEST_F(ElfInterfaceArmTest, StepExidx_refuse_unwind) { regs.set_pc(0x1234); bool finished; - ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished)); + ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished)); ASSERT_TRUE(finished); ASSERT_EQ(0x10000U, regs.sp()); ASSERT_EQ(0x10000U, regs[ARM_REG_SP]); @@ -420,7 +426,7 @@ TEST_F(ElfInterfaceArmTest, StepExidx_pc_zero) { regs.set_pc(0x1234); bool finished; - ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished)); + ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished)); ASSERT_TRUE(finished); ASSERT_EQ(0U, regs.pc()); @@ -432,7 +438,7 @@ TEST_F(ElfInterfaceArmTest, StepExidx_pc_zero) { regs.set_sp(regs[ARM_REG_SP]); regs.set_pc(0x1234); - ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished)); + ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished)); ASSERT_TRUE(finished); ASSERT_EQ(0U, regs.pc()); } diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp index 7491d4004..5e808ef56 100644 --- a/libunwindstack/tests/ElfTest.cpp +++ b/libunwindstack/tests/ElfTest.cpp @@ -346,7 +346,7 @@ class ElfInterfaceMock : public ElfInterface { void InitHeaders() override {} bool GetSoname(std::string*) override { return false; } bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; } - MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*)); + MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*)); }; TEST_F(ElfTest, step_in_interface) { @@ -361,7 +361,7 @@ TEST_F(ElfTest, step_in_interface) { MemoryFake process_memory; bool finished; - EXPECT_CALL(*interface, Step(0x1000, ®s, &process_memory, &finished)) + EXPECT_CALL(*interface, Step(0x1000, 0, ®s, &process_memory, &finished)) .WillOnce(::testing::Return(true)); ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished)); @@ -382,7 +382,7 @@ TEST_F(ElfTest, step_in_interface_non_zero_load_bias) { bool finished; ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished)); - EXPECT_CALL(*interface, Step(0x3300, ®s, &process_memory, &finished)) + EXPECT_CALL(*interface, Step(0x7300, 0x4000, ®s, &process_memory, &finished)) .WillOnce(::testing::Return(true)); ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished)); diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp index 44a73a8f6..631036b87 100644 --- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp +++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp @@ -68,12 +68,23 @@ TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) { EXPECT_EQ(0U, info.GetLoadBias(process_memory_)); } +TEST_F(MapInfoGetLoadBiasTest, load_bias_cached_from_elf) { + map_info_->elf = elf_container_.release(); + + elf_->FakeSetLoadBias(0); + EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_)); + + elf_->FakeSetLoadBias(0x1000); + EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_)); +} + TEST_F(MapInfoGetLoadBiasTest, elf_exists) { map_info_->elf = elf_container_.release(); elf_->FakeSetLoadBias(0); EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_)); + map_info_->load_bias = static_cast(-1); elf_->FakeSetLoadBias(0x1000); EXPECT_EQ(0x1000U, map_info_->GetLoadBias(process_memory_)); } @@ -141,6 +152,15 @@ TEST_F(MapInfoGetLoadBiasTest, elf_exists_in_memory) { EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_)); } +TEST_F(MapInfoGetLoadBiasTest, elf_exists_in_memory_cached) { + InitElfData(memory_, map_info_->start); + + EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_)); + + memory_->Clear(); + EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_)); +} + TEST_F(MapInfoGetLoadBiasTest, multiple_thread_elf_exists_in_memory) { InitElfData(memory_, map_info_->start); diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp index 8dc884f64..9622ba507 100644 --- a/libunwindstack/tests/MapsTest.cpp +++ b/libunwindstack/tests/MapsTest.cpp @@ -44,6 +44,24 @@ static void VerifyLine(std::string line, MapInfo* info) { } } +TEST(MapsTest, map_add) { + Maps maps; + + maps.Add(0x1000, 0x2000, 0, PROT_READ, "fake_map", 0); + maps.Add(0x3000, 0x4000, 0x10, 0, "", 0x1234); + maps.Add(0x5000, 0x6000, 1, 2, "fake_map2", static_cast(-1)); + + ASSERT_EQ(3U, maps.Total()); + MapInfo* info = maps.Get(0); + ASSERT_EQ(0x1000U, info->start); + ASSERT_EQ(0x2000U, info->end); + ASSERT_EQ(0U, info->offset); + ASSERT_EQ(PROT_READ, info->flags); + ASSERT_EQ("fake_map", info->name); + ASSERT_EQ(0U, info->elf_offset); + ASSERT_EQ(0U, info->load_bias.load()); +} + TEST(MapsTest, verify_parse_line) { MapInfo info; diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp index 962f74455..8f2803690 100644 --- a/libunwindstack/tests/UnwindOfflineTest.cpp +++ b/libunwindstack/tests/UnwindOfflineTest.cpp @@ -96,6 +96,54 @@ TEST(UnwindOfflineTest, pc_straddle_arm32) { frame_info); } +TEST(UnwindOfflineTest, pc_in_gnu_debugdata_arm32) { + std::string dir(TestGetFileDirectory() + "offline/gnu_debugdata_arm32/"); + + MemoryOffline* memory = new MemoryOffline; + ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0)); + + FILE* fp = fopen((dir + "regs.txt").c_str(), "r"); + ASSERT_TRUE(fp != nullptr); + RegsArm regs; + uint64_t reg_value; + ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", ®_value)); + regs[ARM_REG_PC] = reg_value; + ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", ®_value)); + regs[ARM_REG_SP] = reg_value; + regs.SetFromRaw(); + fclose(fp); + + fp = fopen((dir + "maps.txt").c_str(), "r"); + ASSERT_TRUE(fp != nullptr); + // The file is guaranteed to be less than 4096 bytes. + std::vector buffer(4096); + ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp)); + fclose(fp); + + BufferMaps maps(buffer.data()); + ASSERT_TRUE(maps.Parse()); + + ASSERT_EQ(ARCH_ARM, regs.Arch()); + + std::shared_ptr process_memory(memory); + + char* cwd = getcwd(nullptr, 0); + ASSERT_EQ(0, chdir(dir.c_str())); + Unwinder unwinder(128, &maps, ®s, process_memory); + unwinder.Unwind(); + ASSERT_EQ(0, chdir(cwd)); + free(cwd); + + std::string frame_info(DumpFrames(unwinder)); + ASSERT_EQ(2U, unwinder.NumFrames()) << "Unwind:\n" << frame_info; + EXPECT_EQ( + " #00 pc 0006dc49 libandroid_runtime.so " + "(_ZN7android14AndroidRuntime15javaThreadShellEPv+80)\n" + " #01 pc 0006dce5 libandroid_runtime.so " + "(_ZN7android14AndroidRuntime19javaCreateThreadEtcEPFiPvES1_PKcijPS1_)\n", + frame_info); +} + TEST(UnwindOfflineTest, pc_straddle_arm64) { std::string dir(TestGetFileDirectory() + "offline/straddle_arm64/"); diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/libandroid_runtime.so b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/libandroid_runtime.so new file mode 100644 index 000000000..e4283e63b Binary files /dev/null and b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/libandroid_runtime.so differ diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/maps.txt b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/maps.txt new file mode 100644 index 000000000..1bcddb6b8 --- /dev/null +++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/maps.txt @@ -0,0 +1 @@ +f1f10000-f2049000 r-xp 00000000 00:00 0 libandroid_runtime.so diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/regs.txt b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/regs.txt new file mode 100644 index 000000000..c6a93dce5 --- /dev/null +++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/regs.txt @@ -0,0 +1,2 @@ +pc: f1f6dc49 +sp: d8fe6930 diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/stack.data b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/stack.data new file mode 100644 index 000000000..19cdf2ddf Binary files /dev/null and b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/stack.data differ diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp index a00b2ee48..7f2d11d67 100644 --- a/libunwindstack/tools/unwind_info.cpp +++ b/libunwindstack/tools/unwind_info.cpp @@ -87,7 +87,7 @@ void DumpDwarfSection(ElfInterface* interface, DwarfSection* section, uint64_t l for (const DwarfFde* fde : *section) { // Sometimes there are entries that have empty length, skip those since // they don't contain any interesting information. - if (fde->pc_start == fde->pc_end) { + if (fde == nullptr || fde->pc_start == fde->pc_end) { continue; } printf("\n PC 0x%" PRIx64, fde->pc_start + load_bias);