diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 40364fe40..75aa427f5 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -130,6 +130,7 @@ cc_test { "tests/RegsStepIfSignalHandlerTest.cpp", "tests/RegsTest.cpp", "tests/SymbolsTest.cpp", + "tests/UnwindOfflineTest.cpp", "tests/UnwindTest.cpp", "tests/UnwinderTest.cpp", ], @@ -153,6 +154,8 @@ cc_test { data: [ "tests/files/elf32.xz", "tests/files/elf64.xz", + "tests/files/offline/straddle_arm32/*", + "tests/files/offline/straddle_arm64/*", ], } diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h index 561d23a88..2589c89f2 100644 --- a/libunwindstack/DwarfEhFrame.h +++ b/libunwindstack/DwarfEhFrame.h @@ -40,7 +40,7 @@ class DwarfEhFrame : public DwarfSectionImpl { uint64_t AdjustPcFromFde(uint64_t pc) override { // The eh_frame uses relative pcs. - return pc + this->memory_.cur_offset(); + return pc + this->memory_.cur_offset() - 4; } }; diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp index 0c65895b9..48e33ee2b 100644 --- a/libunwindstack/Elf.cpp +++ b/libunwindstack/Elf.cpp @@ -104,8 +104,8 @@ bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offse } // The relative pc is always relative to the start of the map from which it comes. -bool Elf::Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process_memory, - bool* finished) { +bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs, + Memory* process_memory, bool* finished) { if (!valid_) { return false; } @@ -117,16 +117,16 @@ bool Elf::Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process } // Adjust the load bias to get the real relative pc. - if (rel_pc < load_bias_) { + if (adjusted_rel_pc < load_bias_) { return false; } - rel_pc -= load_bias_; + adjusted_rel_pc -= load_bias_; // Lock during the step which can update information in the object. std::lock_guard guard(lock_); - return interface_->Step(rel_pc, regs, process_memory, finished) || + return interface_->Step(adjusted_rel_pc, regs, process_memory, finished) || (gnu_debugdata_interface_ && - gnu_debugdata_interface_->Step(rel_pc, regs, process_memory, finished)); + gnu_debugdata_interface_->Step(adjusted_rel_pc, regs, process_memory, finished)); } bool Elf::IsValidElf(Memory* memory) { diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp index 5012ffda1..56370c196 100644 --- a/libunwindstack/Maps.cpp +++ b/libunwindstack/Maps.cpp @@ -64,13 +64,13 @@ static MapInfo* InternalParseLine(const char* line) { // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so char* str; const char* old_str = line; - uint64_t start = strtoul(old_str, &str, 16); + uint64_t start = strtoull(old_str, &str, 16); if (old_str == str || *str++ != '-') { return nullptr; } old_str = str; - uint64_t end = strtoul(old_str, &str, 16); + uint64_t end = strtoull(old_str, &str, 16); if (old_str == str || !std::isspace(*str++)) { return nullptr; } @@ -112,14 +112,14 @@ static MapInfo* InternalParseLine(const char* line) { } old_str = str; - uint64_t offset = strtoul(old_str, &str, 16); + uint64_t offset = strtoull(old_str, &str, 16); if (old_str == str || !std::isspace(*str)) { return nullptr; } // Ignore the 00:00 values. old_str = str; - (void)strtoul(old_str, &str, 16); + (void)strtoull(old_str, &str, 16); if (old_str == str || *str++ != ':') { return nullptr; } @@ -129,14 +129,14 @@ static MapInfo* InternalParseLine(const char* line) { // Skip the inode. old_str = str; - (void)strtoul(str, &str, 16); + (void)strtoull(str, &str, 16); if (old_str == str || !std::isspace(*str++)) { return nullptr; } // Skip decimal digit. old_str = str; - (void)strtoul(old_str, &str, 10); + (void)strtoull(old_str, &str, 10); if (old_str == str || (!std::isspace(*str) && *str != '\0')) { return nullptr; } diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp index 2e46a115e..3092a62c2 100644 --- a/libunwindstack/Unwinder.cpp +++ b/libunwindstack/Unwinder.cpp @@ -32,27 +32,20 @@ namespace unwindstack { -void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc) { +void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc) { size_t frame_num = frames_.size(); frames_.resize(frame_num + 1); FrameData* frame = &frames_.at(frame_num); frame->num = frame_num; - frame->pc = regs_->pc(); frame->sp = regs_->sp(); - frame->rel_pc = rel_pc; + frame->rel_pc = adjusted_rel_pc; if (map_info == nullptr) { + frame->pc = regs_->pc(); return; } - if (adjust_pc) { - // Don't adjust the first frame pc. - frame->rel_pc = regs_->GetAdjustedPc(rel_pc, elf); - - // Adjust the original pc. - frame->pc -= rel_pc - frame->rel_pc; - } - + frame->pc = map_info->start + adjusted_rel_pc; frame->map_name = map_info->name; frame->map_offset = map_info->offset; frame->map_start = map_info->start; @@ -92,21 +85,29 @@ void Unwinder::Unwind(const std::vector* initial_map_names_to_skip, MapInfo* map_info = maps_->Find(regs_->pc()); uint64_t rel_pc; + uint64_t adjusted_rel_pc; Elf* elf; if (map_info == nullptr) { rel_pc = regs_->pc(); + adjusted_rel_pc = rel_pc; } else { if (ShouldStop(map_suffixes_to_ignore, map_info->name)) { break; } elf = map_info->GetElf(process_memory_, true); rel_pc = elf->GetRelPc(regs_->pc(), map_info); + if (adjust_pc) { + adjusted_rel_pc = regs_->GetAdjustedPc(rel_pc, elf); + } else { + adjusted_rel_pc = rel_pc; + } } if (map_info == nullptr || initial_map_names_to_skip == nullptr || std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(), basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) { - FillInFrame(map_info, elf, rel_pc, adjust_pc); + FillInFrame(map_info, elf, adjusted_rel_pc); + // Once a frame is added, stop skipping frames. initial_map_names_to_skip = nullptr; } @@ -133,7 +134,8 @@ void Unwinder::Unwind(const std::vector* initial_map_names_to_skip, in_device_map = true; } else { bool finished; - stepped = elf->Step(rel_pc, map_info->elf_offset, regs_, process_memory_.get(), &finished); + stepped = elf->Step(rel_pc, adjusted_rel_pc, map_info->elf_offset, regs_, + process_memory_.get(), &finished); if (stepped && finished) { break; } diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h index 71d7aca8f..da2ddc027 100644 --- a/libunwindstack/include/unwindstack/Elf.h +++ b/libunwindstack/include/unwindstack/Elf.h @@ -51,8 +51,8 @@ class Elf { uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info); - bool Step(uint64_t rel_pc, uint64_t elf_offset, Regs* regs, Memory* process_memory, - bool* finished); + bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs, + Memory* process_memory, bool* finished); ElfInterface* CreateInterfaceFromMemory(Memory* memory); diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h index 37a76b203..b64d46036 100644 --- a/libunwindstack/include/unwindstack/Unwinder.h +++ b/libunwindstack/include/unwindstack/Unwinder.h @@ -70,7 +70,7 @@ class Unwinder { static std::string FormatFrame(const FrameData& frame, bool bits32); private: - void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc); + void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc); size_t max_frames_; Maps* maps_; diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp index 53ee719d2..3a629f886 100644 --- a/libunwindstack/tests/DwarfEhFrameTest.cpp +++ b/libunwindstack/tests/DwarfEhFrameTest.cpp @@ -109,23 +109,23 @@ TYPED_TEST_P(DwarfEhFrameTest, Init32) { this->eh_frame_->TestGetFdeInfo(0, &info); EXPECT_EQ(0x5100U, info.offset); - EXPECT_EQ(0x660cU, info.start); - EXPECT_EQ(0x680cU, info.end); + EXPECT_EQ(0x6608U, info.start); + EXPECT_EQ(0x6808U, info.end); this->eh_frame_->TestGetFdeInfo(1, &info); EXPECT_EQ(0x5200U, info.offset); - EXPECT_EQ(0x770cU, info.start); - EXPECT_EQ(0x7a0cU, info.end); + EXPECT_EQ(0x7708U, info.start); + EXPECT_EQ(0x7a08U, info.end); this->eh_frame_->TestGetFdeInfo(2, &info); EXPECT_EQ(0x5400U, info.offset); - EXPECT_EQ(0x890cU, info.start); - EXPECT_EQ(0x8d0cU, info.end); + EXPECT_EQ(0x8908U, info.start); + EXPECT_EQ(0x8d08U, info.end); this->eh_frame_->TestGetFdeInfo(3, &info); EXPECT_EQ(0x5500U, info.offset); - EXPECT_EQ(0x9a0cU, info.start); - EXPECT_EQ(0x9f0cU, info.end); + EXPECT_EQ(0x9a08U, info.start); + EXPECT_EQ(0x9f08U, info.end); } TYPED_TEST_P(DwarfEhFrameTest, Init32_fde_not_following_cie) { @@ -193,23 +193,23 @@ TYPED_TEST_P(DwarfEhFrameTest, Init64) { this->eh_frame_->TestGetFdeInfo(0, &info); EXPECT_EQ(0x5100U, info.offset); - EXPECT_EQ(0x661cU, info.start); - EXPECT_EQ(0x681cU, info.end); + EXPECT_EQ(0x6618U, info.start); + EXPECT_EQ(0x6818U, info.end); this->eh_frame_->TestGetFdeInfo(1, &info); EXPECT_EQ(0x5200U, info.offset); - EXPECT_EQ(0x771cU, info.start); - EXPECT_EQ(0x7a1cU, info.end); + EXPECT_EQ(0x7718U, info.start); + EXPECT_EQ(0x7a18U, info.end); this->eh_frame_->TestGetFdeInfo(2, &info); EXPECT_EQ(0x5400U, info.offset); - EXPECT_EQ(0x891cU, info.start); - EXPECT_EQ(0x8d1cU, info.end); + EXPECT_EQ(0x8918U, info.start); + EXPECT_EQ(0x8d18U, info.end); this->eh_frame_->TestGetFdeInfo(3, &info); EXPECT_EQ(0x5500U, info.offset); - EXPECT_EQ(0x9a1cU, info.start); - EXPECT_EQ(0x9f1cU, info.end); + EXPECT_EQ(0x9a18U, info.start); + EXPECT_EQ(0x9f18U, info.end); } TYPED_TEST_P(DwarfEhFrameTest, Init64_fde_not_following_cie) { @@ -261,8 +261,8 @@ TYPED_TEST_P(DwarfEhFrameTest, Init_version1) { typename DwarfEhFrame::FdeInfo info(0, 0, 0); this->eh_frame_->TestGetFdeInfo(0, &info); EXPECT_EQ(0x5100U, info.offset); - EXPECT_EQ(0x660aU, info.start); - EXPECT_EQ(0x680aU, info.end); + EXPECT_EQ(0x6606U, info.start); + EXPECT_EQ(0x6806U, info.end); } TYPED_TEST_P(DwarfEhFrameTest, Init_version4) { @@ -304,8 +304,8 @@ TYPED_TEST_P(DwarfEhFrameTest, Init_version4) { typename DwarfEhFrame::FdeInfo info(0, 0, 0); this->eh_frame_->TestGetFdeInfo(0, &info); EXPECT_EQ(0x5100U, info.offset); - EXPECT_EQ(0x660aU, info.start); - EXPECT_EQ(0x680aU, info.end); + EXPECT_EQ(0x6606U, info.start); + EXPECT_EQ(0x6806U, info.end); } TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc) { @@ -384,8 +384,8 @@ TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) { ASSERT_TRUE(fde != nullptr); EXPECT_EQ(0x14010U, fde->cfa_instructions_offset); EXPECT_EQ(0x14024U, fde->cfa_instructions_end); - EXPECT_EQ(0x1d00cU, fde->pc_start); - EXPECT_EQ(0x1d10cU, fde->pc_end); + EXPECT_EQ(0x1d008U, fde->pc_start); + EXPECT_EQ(0x1d108U, fde->pc_end); EXPECT_EQ(0xf000U, fde->cie_offset); EXPECT_EQ(0U, fde->lsda_address); @@ -428,8 +428,8 @@ TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) { ASSERT_TRUE(fde != nullptr); EXPECT_EQ(0x8024U, fde->cfa_instructions_offset); EXPECT_EQ(0x820cU, fde->cfa_instructions_end); - EXPECT_EQ(0xd01cU, fde->pc_start); - EXPECT_EQ(0xd31cU, fde->pc_end); + EXPECT_EQ(0xd018U, fde->pc_start); + EXPECT_EQ(0xd318U, fde->pc_end); EXPECT_EQ(0x6000U, fde->cie_offset); EXPECT_EQ(0U, fde->lsda_address); diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp index 1028ab9fe..64b325b20 100644 --- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp +++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp @@ -345,8 +345,8 @@ TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde32) { ASSERT_TRUE(fde != nullptr); EXPECT_EQ(0x14010U, fde->cfa_instructions_offset); EXPECT_EQ(0x14024U, fde->cfa_instructions_end); - EXPECT_EQ(0x1d00cU, fde->pc_start); - EXPECT_EQ(0x1d10cU, fde->pc_end); + EXPECT_EQ(0x1d008U, fde->pc_start); + EXPECT_EQ(0x1d108U, fde->pc_end); EXPECT_EQ(0xf000U, fde->cie_offset); EXPECT_EQ(0U, fde->lsda_address); @@ -387,8 +387,8 @@ TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde64) { ASSERT_TRUE(fde != nullptr); EXPECT_EQ(0x8024U, fde->cfa_instructions_offset); EXPECT_EQ(0x820cU, fde->cfa_instructions_end); - EXPECT_EQ(0xd01cU, fde->pc_start); - EXPECT_EQ(0xd31cU, fde->pc_end); + EXPECT_EQ(0xd018U, fde->pc_start); + EXPECT_EQ(0xd318U, fde->pc_end); EXPECT_EQ(0x6000U, fde->cie_offset); EXPECT_EQ(0U, fde->lsda_address); diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp index 098f5f494..afd113d0d 100644 --- a/libunwindstack/tests/ElfTest.cpp +++ b/libunwindstack/tests/ElfTest.cpp @@ -132,7 +132,7 @@ TEST_F(ElfTest, elf_invalid) { ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset)); bool finished; - ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished)); + ASSERT_FALSE(elf.Step(0, 0, 0, nullptr, nullptr, &finished)); } TEST_F(ElfTest, elf32_invalid_machine) { @@ -306,7 +306,7 @@ TEST_F(ElfTest, step_in_signal_map) { elf.FakeSetValid(true); elf.FakeSetLoadBias(0); bool finished; - ASSERT_TRUE(elf.Step(0x1000, 0x2000, ®s, &process_memory, &finished)); + ASSERT_TRUE(elf.Step(0x1000, 0x1000, 0x2000, ®s, &process_memory, &finished)); EXPECT_FALSE(finished); EXPECT_EQ(15U, regs.pc()); EXPECT_EQ(13U, regs.sp()); @@ -339,7 +339,7 @@ TEST_F(ElfTest, step_in_interface) { EXPECT_CALL(*interface, Step(0x1000, ®s, &process_memory, &finished)) .WillOnce(::testing::Return(true)); - ASSERT_TRUE(elf.Step(0x1000, 0x2000, ®s, &process_memory, &finished)); + ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished)); } TEST_F(ElfTest, step_in_interface_non_zero_load_bias) { @@ -355,12 +355,12 @@ TEST_F(ElfTest, step_in_interface_non_zero_load_bias) { // Invalid relative pc given load_bias. bool finished; - ASSERT_FALSE(elf.Step(0x1000, 0x2000, ®s, &process_memory, &finished)); + ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished)); EXPECT_CALL(*interface, Step(0x3300, ®s, &process_memory, &finished)) .WillOnce(::testing::Return(true)); - ASSERT_TRUE(elf.Step(0x7300, 0x2000, ®s, &process_memory, &finished)); + ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished)); } } // namespace unwindstack diff --git a/libunwindstack/tests/ElfTestUtils.cpp b/libunwindstack/tests/ElfTestUtils.cpp index 069386b4e..69163ac94 100644 --- a/libunwindstack/tests/ElfTestUtils.cpp +++ b/libunwindstack/tests/ElfTestUtils.cpp @@ -47,7 +47,7 @@ void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type) { ehdr->e_ehsize = sizeof(Ehdr); } -static std::string GetTestFileDirectory() { +std::string TestGetFileDirectory() { std::string exec(testing::internal::GetArgvs()[0]); auto const value = exec.find_last_of('/'); if (value == std::string::npos) { @@ -102,7 +102,7 @@ void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine, bool init_gnu_de offset = symtab_offset + 0x100; if (init_gnu_debugdata) { // Read in the compressed elf data and copy it in. - name = GetTestFileDirectory(); + name = TestGetFileDirectory(); if (elf_class == ELFCLASS32) { name += "elf32.xz"; } else { diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h index 6ef00e1d9..62cd59a29 100644 --- a/libunwindstack/tests/ElfTestUtils.h +++ b/libunwindstack/tests/ElfTestUtils.h @@ -18,6 +18,7 @@ #define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H #include +#include namespace unwindstack { @@ -30,6 +31,8 @@ template void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata, TestCopyFuncType copy_func); +std::string TestGetFileDirectory(); + } // namespace unwindstack #endif // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp new file mode 100644 index 000000000..d24abe493 --- /dev/null +++ b/libunwindstack/tests/UnwindOfflineTest.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include "Machine.h" + +#include "ElfTestUtils.h" + +namespace unwindstack { + +static std::string DumpFrames(Unwinder& unwinder) { + std::string str; + for (size_t i = 0; i < unwinder.NumFrames(); i++) { + str += unwinder.FormatFrame(i) + "\n"; + } + return str; +} + +TEST(UnwindOfflineTest, pc_straddle_arm32) { + std::string dir(TestGetFileDirectory() + "offline/straddle_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; + ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", ®_value)); + regs[ARM_REG_LR] = 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(static_cast(EM_ARM), regs.MachineType()); + + 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(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info; + EXPECT_EQ( + " #00 pc 0001a9f8 libc.so (abort+63)\n" + " #01 pc 00006a1b libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n" + " #02 pc 00007441 libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n" + " #03 pc 00015149 /does/not/exist/libhidlbase.so\n", + frame_info); +} + +TEST(UnwindOfflineTest, pc_straddle_arm64) { + std::string dir(TestGetFileDirectory() + "offline/straddle_arm64/"); + + 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); + RegsArm64 regs; + uint64_t reg_value; + ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", ®_value)); + regs[ARM64_REG_PC] = reg_value; + ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", ®_value)); + regs[ARM64_REG_SP] = reg_value; + ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", ®_value)); + regs[ARM64_REG_LR] = reg_value; + ASSERT_EQ(1, fscanf(fp, "x29: %" SCNx64 "\n", ®_value)); + regs[ARM64_REG_R29] = 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(static_cast(EM_AARCH64), regs.MachineType()); + + 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(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info; + EXPECT_EQ( + " #00 pc 0000000000429fd8 libunwindstack_test (SignalInnerFunction+24)\n" + " #01 pc 000000000042a078 libunwindstack_test (SignalMiddleFunction+8)\n" + " #02 pc 000000000042a08c libunwindstack_test (SignalOuterFunction+8)\n" + " #03 pc 000000000042d8fc libunwindstack_test " + "(_ZN11unwindstackL19RemoteThroughSignalEij+20)\n" + " #04 pc 000000000042d8d8 libunwindstack_test " + "(_ZN11unwindstack37UnwindTest_remote_through_signal_Test8TestBodyEv+32)\n" + " #05 pc 0000000000455d70 libunwindstack_test (_ZN7testing4Test3RunEv+392)\n", + frame_info); +} + +} // namespace unwindstack diff --git a/libunwindstack/tests/files/offline/straddle_arm32/libbase.so b/libunwindstack/tests/files/offline/straddle_arm32/libbase.so new file mode 100644 index 000000000..d1f16ee8a Binary files /dev/null and b/libunwindstack/tests/files/offline/straddle_arm32/libbase.so differ diff --git a/libunwindstack/tests/files/offline/straddle_arm32/libc.so b/libunwindstack/tests/files/offline/straddle_arm32/libc.so new file mode 100644 index 000000000..4dc19ca6c Binary files /dev/null and b/libunwindstack/tests/files/offline/straddle_arm32/libc.so differ diff --git a/libunwindstack/tests/files/offline/straddle_arm32/maps.txt b/libunwindstack/tests/files/offline/straddle_arm32/maps.txt new file mode 100644 index 000000000..8c26479fa --- /dev/null +++ b/libunwindstack/tests/files/offline/straddle_arm32/maps.txt @@ -0,0 +1,4 @@ +f2d9a000-f2da7fff r-xp 00000000 00:00 0 libbase.so +f3002000-f3005fff rw-p 00000000 00:00 0 [stack:25941] +f31d0000-f326bfff r-xp 00000000 00:00 0 libc.so +f3352000-f336bfff r-xp 00000000 00:00 0 /does/not/exist/libhidlbase.so diff --git a/libunwindstack/tests/files/offline/straddle_arm32/regs.txt b/libunwindstack/tests/files/offline/straddle_arm32/regs.txt new file mode 100644 index 000000000..3baedf365 --- /dev/null +++ b/libunwindstack/tests/files/offline/straddle_arm32/regs.txt @@ -0,0 +1,3 @@ +pc: f31ea9f8 +sp: e9c866f8 +lr: f31f179f diff --git a/libunwindstack/tests/files/offline/straddle_arm32/stack.data b/libunwindstack/tests/files/offline/straddle_arm32/stack.data new file mode 100644 index 000000000..83aeb4aaa Binary files /dev/null and b/libunwindstack/tests/files/offline/straddle_arm32/stack.data differ diff --git a/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test b/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test new file mode 100644 index 000000000..092fc3aec Binary files /dev/null and b/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test differ diff --git a/libunwindstack/tests/files/offline/straddle_arm64/maps.txt b/libunwindstack/tests/files/offline/straddle_arm64/maps.txt new file mode 100644 index 000000000..bdf29b5fe --- /dev/null +++ b/libunwindstack/tests/files/offline/straddle_arm64/maps.txt @@ -0,0 +1,2 @@ +00000064d05ab000-00000064d0a6cfff r-xp 00000000 00:00 0 libunwindstack_test +0000007fe0d64000-0000007fe0d84fff rw-p 00000000 00:00 0 [stack] diff --git a/libunwindstack/tests/files/offline/straddle_arm64/regs.txt b/libunwindstack/tests/files/offline/straddle_arm64/regs.txt new file mode 100644 index 000000000..ff8a936d2 --- /dev/null +++ b/libunwindstack/tests/files/offline/straddle_arm64/regs.txt @@ -0,0 +1,4 @@ +pc: 00000064d09d4fd8 +sp: 0000007fe0d84040 +lr: 00000064d09d507c +x29: 0000007fe0d84070 diff --git a/libunwindstack/tests/files/offline/straddle_arm64/stack.data b/libunwindstack/tests/files/offline/straddle_arm64/stack.data new file mode 100644 index 000000000..824d0e2c3 Binary files /dev/null and b/libunwindstack/tests/files/offline/straddle_arm64/stack.data differ