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/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