Fix incorrect usage of relative pcs.
When stepping, it's necessary to use both the unaltered relative pc
and the adjusted relative pc. If the adjusted pc is not used, the
wrong unwind information can be used.
Added new offline unit tests that take real data and verifies that it
unwinds properly.
Fix a bug in the map code that would not properly parse map data for
a 64 bit process when done in a 32 bit process.
Fix bug in eh_frame processing that didn't adjust the pc correctly.
Fix unit tests related to the pc adjustment.
Bug: 69475565
Test: Passes libbacktrace/libunwindstack unit tests.
Test: Run debuggerd -b on processes on a hikey.
Change-Id: Ic501a1c4549c5f61d2742a7105c42a960f2c892b
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 53ee719..3a629f8 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -109,23 +109,23 @@
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 @@
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 @@
typename DwarfEhFrame<TypeParam>::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 @@
typename DwarfEhFrame<TypeParam>::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 @@
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 @@
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 1028ab9..64b325b 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -345,8 +345,8 @@
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 @@
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 098f5f4..afd113d 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -132,7 +132,7 @@
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 @@
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 @@
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 @@
// 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 069386b..69163ac 100644
--- a/libunwindstack/tests/ElfTestUtils.cpp
+++ b/libunwindstack/tests/ElfTestUtils.cpp
@@ -47,7 +47,7 @@
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 @@
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 6ef00e1..62cd59a 100644
--- a/libunwindstack/tests/ElfTestUtils.h
+++ b/libunwindstack/tests/ElfTestUtils.h
@@ -18,6 +18,7 @@
#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
#include <functional>
+#include <string>
namespace unwindstack {
@@ -30,6 +31,8 @@
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 0000000..d24abe4
--- /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 <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+#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<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(static_cast<uint32_t>(EM_ARM), regs.MachineType());
+
+ 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(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<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(static_cast<uint32_t>(EM_AARCH64), regs.MachineType());
+
+ 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(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 0000000..d1f16ee
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm32/libbase.so
Binary files 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 0000000..4dc19ca
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm32/libc.so
Binary files 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 0000000..8c26479
--- /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 0000000..3baedf3
--- /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 0000000..83aeb4a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm32/stack.data
Binary files 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 0000000..092fc3a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test
Binary files 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 0000000..bdf29b5
--- /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 0000000..ff8a936
--- /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 0000000..824d0e2
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/stack.data
Binary files differ