Fix issues in libunwindstack.
- Add a load_bias field in MapInfo so that it can be loaded offline,
and also so it can be cached.
- Add an Add function to the Maps class so that it's possible to manually
create a map.
- Remove the OfflineMaps class since I haven't found a reason for this to
exist.
- Add a pointer to the gnu debugdata compressed section in the interface
itself and modify the step path to try eh_frame, then debug_frame, then
gnu_debugdata. This way arm can add exidx as the last step behind
gnu_debugdata. Add an offline test to verify the order of unwind.
- Fix x86_64_ucontext_t since it was a different size on 32 bit and 64 bit
systems.
Test: Pass new unit tests.
Change-Id: I978b70d6c244bd307c62a29886d24c1a8cb2af23
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 21dd306..5e94388 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -167,6 +167,7 @@
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 f486e23..5ec4a3d 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -79,6 +79,7 @@
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 @@
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<std::mutex> 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 334cf76..df1642e 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -386,16 +386,29 @@
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 9841e24..5d99bd7 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -92,16 +92,24 @@
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<RegsArm*>(regs);
uint64_t entry_offset;
if (!FindEntry(pc, &entry_offset)) {
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index eeb2e17..9c067ba 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -70,9 +70,11 @@
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 51bce8e..89fe038 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -122,13 +122,21 @@
}
uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
+ uint64_t cur_load_bias = load_bias.load();
+ if (cur_load_bias != static_cast<uint64_t>(-1)) {
+ return cur_load_bias;
+ }
+
{
// Make sure no other thread is trying to add the elf to this map.
std::lock_guard<std::mutex> 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 @@
// Call lightweight static function that will only read enough of the
// elf data to get the load bias.
std::unique_ptr<Memory> 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 56370c1..4c16212 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -202,6 +202,13 @@
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 @@
return "/proc/" + std::to_string(pid_) + "/maps";
}
-bool OfflineMaps::Parse() {
- // Format of maps information:
- // <uint64_t> StartOffset
- // <uint64_t> EndOffset
- // <uint64_t> offset
- // <uint16_t> flags
- // <uint16_t> MapNameLength
- // <VariableLengthValue> MapName
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_.c_str(), O_RDONLY)));
- if (fd == -1) {
- return false;
- }
-
- std::vector<char> 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 d689796..2b8bdc4 100644
--- a/libunwindstack/UcontextX86_64.h
+++ b/libunwindstack/UcontextX86_64.h
@@ -38,6 +38,7 @@
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 5cfe74d..5d3cd5e 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -60,7 +60,8 @@
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 @@
const std::unordered_map<uint64_t, LoadInfo>& 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 @@
std::unique_ptr<DwarfSection> eh_frame_;
std::unique_ptr<DwarfSection> debug_frame_;
+ // The Elf object owns the gnu_debugdata interface object.
+ ElfInterface* gnu_debugdata_interface_ = nullptr;
std::vector<Symbols*> symbols_;
};
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 6f8ceca..22e48f7 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -19,6 +19,7 @@
#include <stdint.h>
+#include <atomic>
#include <mutex>
#include <string>
@@ -33,7 +34,12 @@
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<uint64_t>(-1)) {}
~MapInfo() { delete elf; }
uint64_t start = 0;
@@ -48,6 +54,8 @@
// 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<Memory>& process_memory, bool init_gnu_debugdata = false);
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 34fef7f..17a2d28 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -42,6 +42,9 @@
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<MapInfo*>::iterator iterator;
iterator begin() { return maps_.begin(); }
iterator end() { return maps_.end(); }
@@ -100,14 +103,6 @@
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 b94a8a4..68de797 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -43,7 +43,7 @@
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 565b13f..abf9927 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -68,8 +68,7 @@
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 5f7cf60..e6763ab 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -302,7 +302,7 @@
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 7491d40..5e808ef 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -346,7 +346,7 @@
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 @@
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 @@
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 44a73a8..631036b 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -68,12 +68,23 @@
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<uint64_t>(-1);
elf_->FakeSetLoadBias(0x1000);
EXPECT_EQ(0x1000U, map_info_->GetLoadBias(process_memory_));
}
@@ -141,6 +152,15 @@
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 8dc884f..9622ba5 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -44,6 +44,24 @@
}
}
+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<uint64_t>(-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 962f744..8f28036 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -96,6 +96,54 @@
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<char> 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<Memory> 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 0000000..e4283e6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/libandroid_runtime.so
Binary files 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 0000000..1bcddb6
--- /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 0000000..c6a93dc
--- /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 0000000..19cdf2d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index a00b2ee..7f2d11d 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -87,7 +87,7 @@
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);