summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2024-01-17 16:15:19 -0800
committerChristopher Ferris <cferris@google.com>2024-02-02 15:31:00 -0800
commit4b59ea8471e89d01300481a92de3230b79b6d7c7 (patch)
tree5d69b91fec07b97c1521a34eccb557e246faac07
parenta9a7faddda4f209e0df5ae73da706cbe39a40b8f (diff)
downloadunwinding-4b59ea8471e89d01300481a92de3230b79b6d7c7.tar.gz
Add support for riscv 64 vlenb.
The vlenb register is used for some unwinding information, so add gathering it. Also, rename some of the machine constants to use better names. Added an offline test that uses the vlenb register to do the unwind. Added a remote unwind test for the bionic local terminate. Bug: 318768887 Test: Ran unit tests. Test: Ran 137-cfi and verified a full stack generated. Change-Id: Ia72394033b4be46a3f29d519879b88ed933ab5ce
-rw-r--r--libunwindstack/Android.bp4
-rw-r--r--libunwindstack/DwarfOp.cpp4
-rw-r--r--libunwindstack/DwarfSection.cpp2
-rw-r--r--libunwindstack/Regs.cpp8
-rw-r--r--libunwindstack/RegsRiscv64.cpp61
-rw-r--r--libunwindstack/include/unwindstack/MachineRiscv64.h5
-rw-r--r--libunwindstack/include/unwindstack/Regs.h2
-rw-r--r--libunwindstack/include/unwindstack/RegsGetLocal.h2
-rw-r--r--libunwindstack/include/unwindstack/RegsRiscv64.h9
-rw-r--r--libunwindstack/include/unwindstack/UserRiscv64.h9
-rw-r--r--libunwindstack/offline_files/vlenb_riscv64/libc.so.gzbin0 -> 555440 bytes
-rw-r--r--libunwindstack/offline_files/vlenb_riscv64/libunwindstack_unit_test.gzbin0 -> 2551481 bytes
-rw-r--r--libunwindstack/offline_files/vlenb_riscv64/maps.txt4
-rw-r--r--libunwindstack/offline_files/vlenb_riscv64/output.txt8
-rw-r--r--libunwindstack/offline_files/vlenb_riscv64/regs.txt33
-rw-r--r--libunwindstack/offline_files/vlenb_riscv64/stack.databin0 -> 10632 bytes
-rw-r--r--libunwindstack/tests/AndroidUnwinderTest.cpp2
-rw-r--r--libunwindstack/tests/RegsIterateTest.cpp1
-rw-r--r--libunwindstack/tests/RegsTest.cpp24
-rw-r--r--libunwindstack/tests/UnwindOfflineTest.cpp38
-rw-r--r--libunwindstack/tests/VerifyBionicTerminationTest.cpp48
-rw-r--r--libunwindstack/utils/OfflineUnwindUtils.cpp10
22 files changed, 231 insertions, 43 deletions
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 8fb5c3d..9c8a4bb 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -202,6 +202,9 @@ cc_library {
linux: {
runtime_libs: ["libdexfile"], // libdexfile_support dependency
},
+ musl: {
+ cflags: ["-DNT_RISCV_VECTOR=0x901"],
+ },
},
arch: {
@@ -441,6 +444,7 @@ cc_defaults {
"offline_files/maps_compiled_arm64/28648/*",
"offline_files/maps_compiled_arm64/28656_oat_odex_jar/*",
"offline_files/maps_compiled_arm64/28667/*",
+ "offline_files/vlenb_riscv64/*",
"offline_files/zlib_compress_arm/*",
"offline_files/zstd_compress_arm/*",
],
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
index a5ce03f..6833d0f 100644
--- a/libunwindstack/DwarfOp.cpp
+++ b/libunwindstack/DwarfOp.cpp
@@ -1902,7 +1902,7 @@ bool DwarfOp<AddressType>::op_regx() {
// For simplicity, the code will read the value before doing the unwind.
template <typename AddressType>
bool DwarfOp<AddressType>::op_breg() {
- uint16_t reg = cur_op() - 0x70;
+ uint16_t reg = regs_info_->regs->Convert(cur_op() - 0x70);
if (reg >= regs_info_->Total()) {
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
@@ -1913,7 +1913,7 @@ bool DwarfOp<AddressType>::op_breg() {
template <typename AddressType>
bool DwarfOp<AddressType>::op_bregx() {
- AddressType reg = OperandAt(0);
+ uint16_t reg = regs_info_->regs->Convert(OperandAt(0));
if (reg >= regs_info_->Total()) {
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 2d664d6..728390c 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -450,7 +450,7 @@ bool DwarfSectionImpl<AddressType>::EvalRegister(const DwarfLocation* loc, uint3
*reg_ptr = eval_info->cfa + loc->values[0];
break;
case DWARF_LOCATION_REGISTER: {
- uint32_t cur_reg = loc->values[0];
+ uint16_t cur_reg = eval_info->regs_info.regs->Convert(loc->values[0]);
if (cur_reg >= eval_info->regs_info.Total()) {
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index ca5a9f9..3f4a184 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -51,9 +51,9 @@ static constexpr size_t kMaxUserRegsSize = std::max(
Regs* Regs::RemoteGet(pid_t pid, ErrorCode* error_code) {
// Make the buffer large enough to contain the largest registers type.
std::vector<uint64_t> buffer(kMaxUserRegsSize / sizeof(uint64_t));
- struct iovec io;
- io.iov_base = buffer.data();
- io.iov_len = buffer.size() * sizeof(uint64_t);
+ struct iovec io {
+ .iov_base = buffer.data(), .iov_len = buffer.size() * sizeof(uint64_t)
+ };
if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, reinterpret_cast<void*>(&io)) == -1) {
Log::Error("PTRACE_GETREGSET failed for pid %d: %s", pid, strerror(errno));
@@ -74,7 +74,7 @@ Regs* Regs::RemoteGet(pid_t pid, ErrorCode* error_code) {
case sizeof(arm64_user_regs):
return RegsArm64::Read(buffer.data());
case sizeof(riscv64_user_regs):
- return RegsRiscv64::Read(buffer.data());
+ return RegsRiscv64::Read(buffer.data(), pid);
}
Log::Error("No matching size of user regs structure for pid %d: size %zu", pid, io.iov_len);
diff --git a/libunwindstack/RegsRiscv64.cpp b/libunwindstack/RegsRiscv64.cpp
index 6e796a2..a69b07f 100644
--- a/libunwindstack/RegsRiscv64.cpp
+++ b/libunwindstack/RegsRiscv64.cpp
@@ -14,10 +14,14 @@
* limitations under the License.
*/
+#include <elf.h>
#include <stdint.h>
#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/uio.h>
#include <functional>
+#include <vector>
#include <unwindstack/Elf.h>
#include <unwindstack/MachineRiscv64.h>
@@ -29,8 +33,35 @@
namespace unwindstack {
+uint64_t RegsRiscv64::GetVlenbFromLocal() {
+#if defined(__riscv)
+ // Assumes that all cpus have the same value.
+ uint64_t vlenb;
+ asm volatile("csrr %0, 0xc22\n" : "=r"(vlenb)::);
+ return vlenb;
+#else
+ return 0;
+#endif
+}
+
+uint64_t RegsRiscv64::GetVlenbFromRemote(pid_t pid) {
+ if (pid == 0) {
+ return GetVlenbFromLocal();
+ }
+
+ // We only care about these values, no need to get the other vector registers.
+ struct riscv64_v_regset_state regs;
+ struct iovec io = {.iov_base = &regs, .iov_len = sizeof(regs)};
+ if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, reinterpret_cast<void*>(&io)) == -1) {
+ // TODO: Workaround due to some devices not properly returning these values.
+ // This code assumes that all cores on the device have the same vlenb.
+ return GetVlenbFromLocal();
+ }
+ return regs.vlenb;
+}
+
RegsRiscv64::RegsRiscv64()
- : RegsImpl<uint64_t>(RISCV64_REG_MAX, Location(LOCATION_REGISTER, RISCV64_REG_RA)) {}
+ : RegsImpl<uint64_t>(RISCV64_REG_COUNT, Location(LOCATION_REGISTER, RISCV64_REG_RA)) {}
ArchEnum RegsRiscv64::Arch() {
return ARCH_RISCV64;
@@ -95,14 +126,15 @@ void RegsRiscv64::IterateRegisters(std::function<void(const char*, uint64_t)> fn
fn("a5", regs_[RISCV64_REG_A5]);
fn("a6", regs_[RISCV64_REG_A6]);
fn("a7", regs_[RISCV64_REG_A7]);
+ fn("vlenb", regs_[RISCV64_REG_VLENB]);
}
-Regs* RegsRiscv64::Read(const void* remote_data) {
+Regs* RegsRiscv64::Read(const void* remote_data, pid_t pid) {
const riscv64_user_regs* user = reinterpret_cast<const riscv64_user_regs*>(remote_data);
RegsRiscv64* regs = new RegsRiscv64();
- memcpy(regs->RawData(), &user->regs[0], RISCV64_REG_MAX * sizeof(uint64_t));
- // uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+ memcpy(regs->RawData(), &user->regs[0], RISCV64_REG_REAL_COUNT * sizeof(uint64_t));
+ regs->regs_[RISCV64_REG_VLENB] = GetVlenbFromRemote(pid);
return regs;
}
@@ -111,7 +143,13 @@ Regs* RegsRiscv64::CreateFromUcontext(void* ucontext) {
RegsRiscv64* regs = new RegsRiscv64();
memcpy(regs->RawData(), &riscv64_ucontext->uc_mcontext.__gregs[0],
- RISCV64_REG_MAX * sizeof(uint64_t));
+ RISCV64_REG_REAL_COUNT * sizeof(uint64_t));
+
+ // TODO: Until b/323045700 is fixed, this code temporarily assumes
+ // this function will only be called on the same core an unwind occurs.
+ // If not, the vlenb value might be wrong.
+ uint64_t* raw_data = reinterpret_cast<uint64_t*>(regs->RawData());
+ raw_data[RISCV64_REG_VLENB] = GetVlenbFromLocal();
return regs;
}
@@ -134,7 +172,7 @@ bool RegsRiscv64::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* pro
// SP + sizeof(siginfo_t) + uc_mcontext offset + PC offset.
if (!process_memory->ReadFully(regs_[RISCV64_REG_SP] + 0x80 + 0xb0 + 0x00, regs_.data(),
- sizeof(uint64_t) * (RISCV64_REG_MAX))) {
+ sizeof(uint64_t) * (RISCV64_REG_REAL_COUNT))) {
return false;
}
return true;
@@ -144,4 +182,15 @@ Regs* RegsRiscv64::Clone() {
return new RegsRiscv64(*this);
}
+uint16_t RegsRiscv64::Convert(uint16_t reg) {
+ if (reg == 0x1c22) {
+ return RISCV64_REG_VLENB;
+ }
+ if (reg == RISCV64_REG_VLENB) {
+ // It should never be valid for the register to be vlenb naturally.
+ return total_regs();
+ }
+ return reg;
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/MachineRiscv64.h b/libunwindstack/include/unwindstack/MachineRiscv64.h
index 397e680..75dfa86 100644
--- a/libunwindstack/include/unwindstack/MachineRiscv64.h
+++ b/libunwindstack/include/unwindstack/MachineRiscv64.h
@@ -53,7 +53,10 @@ enum Riscv64Reg : uint16_t {
RISCV64_REG_T4,
RISCV64_REG_T5,
RISCV64_REG_T6,
- RISCV64_REG_MAX,
+ RISCV64_REG_REAL_COUNT,
+ // This is the last real register, vlenb is a special register value.
+ RISCV64_REG_VLENB = RISCV64_REG_REAL_COUNT,
+ RISCV64_REG_COUNT,
};
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 7486ade..5d3224c 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -81,6 +81,8 @@ class Regs {
virtual Regs* Clone() = 0;
+ virtual uint16_t Convert(uint16_t reg) { return reg; }
+
static ArchEnum CurrentArch();
static ArchEnum RemoteGetArch(pid_t pid, ErrorCode* error_code = nullptr);
static Regs* RemoteGet(pid_t pid, ErrorCode* error_code = nullptr);
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
index a04ea7b..86aab97 100644
--- a/libunwindstack/include/unwindstack/RegsGetLocal.h
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -120,6 +120,8 @@ inline __attribute__((__always_inline__)) void AsmGetRegs(void* reg_data) {
"sd t4, 232(%[base])\n"
"sd t5, 240(%[base])\n"
"sd t6, 248(%[base])\n"
+ "csrr t1, 0xc22\n"
+ "sd t1, 256(%[base])\n"
"la t1, 1b\n"
"sd t1, 0(%[base])\n"
: [base] "+r"(reg_data)
diff --git a/libunwindstack/include/unwindstack/RegsRiscv64.h b/libunwindstack/include/unwindstack/RegsRiscv64.h
index 711bdb3..e805f76 100644
--- a/libunwindstack/include/unwindstack/RegsRiscv64.h
+++ b/libunwindstack/include/unwindstack/RegsRiscv64.h
@@ -17,6 +17,7 @@
#pragma once
#include <stdint.h>
+#include <sys/types.h>
#include <functional>
@@ -49,9 +50,15 @@ class RegsRiscv64 : public RegsImpl<uint64_t> {
Regs* Clone() override final;
- static Regs* Read(const void* data);
+ uint16_t Convert(uint16_t reg) override;
+
+ static Regs* Read(const void* data, pid_t pid = 0);
static Regs* CreateFromUcontext(void* ucontext);
+
+ static uint64_t GetVlenbFromRemote(pid_t pid);
+
+ static uint64_t GetVlenbFromLocal();
};
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/UserRiscv64.h b/libunwindstack/include/unwindstack/UserRiscv64.h
index 1e91228..c7ad198 100644
--- a/libunwindstack/include/unwindstack/UserRiscv64.h
+++ b/libunwindstack/include/unwindstack/UserRiscv64.h
@@ -34,4 +34,13 @@ struct riscv64_user_regs {
uint64_t regs[32];
};
+struct riscv64_v_regset_state {
+ uint64_t vstart;
+ uint64_t vl;
+ uint64_t vtype;
+ uint64_t vcsr;
+ uint64_t vlenb;
+ // There is more data beyond this, but we don't care about it.
+};
+
} // namespace unwindstack
diff --git a/libunwindstack/offline_files/vlenb_riscv64/libc.so.gz b/libunwindstack/offline_files/vlenb_riscv64/libc.so.gz
new file mode 100644
index 0000000..69d08c1
--- /dev/null
+++ b/libunwindstack/offline_files/vlenb_riscv64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/vlenb_riscv64/libunwindstack_unit_test.gz b/libunwindstack/offline_files/vlenb_riscv64/libunwindstack_unit_test.gz
new file mode 100644
index 0000000..3fb419a
--- /dev/null
+++ b/libunwindstack/offline_files/vlenb_riscv64/libunwindstack_unit_test.gz
Binary files differ
diff --git a/libunwindstack/offline_files/vlenb_riscv64/maps.txt b/libunwindstack/offline_files/vlenb_riscv64/maps.txt
new file mode 100644
index 0000000..e96379b
--- /dev/null
+++ b/libunwindstack/offline_files/vlenb_riscv64/maps.txt
@@ -0,0 +1,4 @@
+555c43ae2000-555c43bf3000 r--p 0 00:00 0 libunwindstack_unit_test
+555c43bf3000-555c440d3000 r-xp 110000 00:00 0 libunwindstack_unit_test
+7ff2fdedd000-7ff2fdf1b000 r--p 0 00:00 0 libc.so
+7ff2fdf1b000-7ff2fdf84000 r-xp 3d000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/vlenb_riscv64/output.txt b/libunwindstack/offline_files/vlenb_riscv64/output.txt
new file mode 100644
index 0000000..01cbd78
--- /dev/null
+++ b/libunwindstack/offline_files/vlenb_riscv64/output.txt
@@ -0,0 +1,8 @@
+ #00 pc 00000000005915dc libunwindstack_unit_test (unwindstack::Verify_test_Test::TestBody()+42)
+ #01 pc 00000000005a9dfc libunwindstack_unit_test (testing::Test::Run()+378)
+ #02 pc 00000000005aac76 libunwindstack_unit_test (testing::TestInfo::Run()+454)
+ #03 pc 00000000005ab604 libunwindstack_unit_test (testing::TestSuite::Run()+708)
+ #04 pc 00000000005b7cd2 libunwindstack_unit_test (testing::internal::UnitTestImpl::RunAllTests()+2034)
+ #05 pc 00000000005b739e libunwindstack_unit_test (testing::UnitTest::Run()+126)
+ #06 pc 00000000005c0c7c libunwindstack_unit_test (IsolateMain+1426)
+ #07 pc 000000000004a394 libc.so (__libc_init+76)
diff --git a/libunwindstack/offline_files/vlenb_riscv64/regs.txt b/libunwindstack/offline_files/vlenb_riscv64/regs.txt
new file mode 100644
index 0000000..b18c3c8
--- /dev/null
+++ b/libunwindstack/offline_files/vlenb_riscv64/regs.txt
@@ -0,0 +1,33 @@
+pc: 555c440735dc
+ra: 555c4408be00
+sp: 7fffe388a680
+gp: 7ff30152c000
+tp: 7ff300879050
+t0: 0
+t1: 555c440d12ac
+t2: 7ff2edc7c050
+t3: 7ff2fde8d56e
+t4: 69cc7bb8c
+t5: 110928
+t6: 7ff12dc80075
+s0: 7fffe388a6a0
+s1: 555c440f7e98
+s2: 294d27e9f8c
+s3: 7ff24dc82ee0
+s4: 555c440f7c9a
+s5: 7ff0fdc7e210
+s6: 7ff24dc82ee0
+s7: 7ff20dcc11d0
+s8: 0
+s9: 555c440f7dd2
+s10: 4
+s11: 1
+a0: 0
+a1: 555c440735b2
+a2: 0
+a3: 555c440f7df8
+a4: 74
+a5: 0
+a6: 0
+a7: 2e8ba2e8ba2e8ba3
+vlenb: 10
diff --git a/libunwindstack/offline_files/vlenb_riscv64/stack.data b/libunwindstack/offline_files/vlenb_riscv64/stack.data
new file mode 100644
index 0000000..eb8d582
--- /dev/null
+++ b/libunwindstack/offline_files/vlenb_riscv64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/AndroidUnwinderTest.cpp b/libunwindstack/tests/AndroidUnwinderTest.cpp
index 28620e2..8017123 100644
--- a/libunwindstack/tests/AndroidUnwinderTest.cpp
+++ b/libunwindstack/tests/AndroidUnwinderTest.cpp
@@ -262,7 +262,7 @@ TEST_F(AndroidUnwinderTest, verify_all_unwind_functions) {
reinterpret_cast<riscv64_ucontext_t*>(malloc(sizeof(riscv64_ucontext_t)));
ucontext = riscv64_ucontext;
memcpy(&riscv64_ucontext->uc_mcontext.__gregs, regs->RawData(),
- RISCV64_REG_MAX * sizeof(uint64_t));
+ RISCV64_REG_REAL_COUNT * sizeof(uint64_t));
} break;
default:
ucontext = nullptr;
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index aeead15..73212d8 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -189,6 +189,7 @@ std::vector<Register> ExpectedRegisters<RegsRiscv64>() {
result.push_back({"a5", RISCV64_REG_A5});
result.push_back({"a6", RISCV64_REG_A6});
result.push_back({"a7", RISCV64_REG_A7});
+ result.push_back({"vlenb", RISCV64_REG_VLENB});
return result;
}
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index a2c5e30..6fb625d 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -22,6 +22,7 @@
#include <unwindstack/Elf.h>
#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MachineRiscv64.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/RegsArm.h>
#include <unwindstack/RegsArm64.h>
@@ -178,6 +179,21 @@ TEST_F(RegsTest, elf_invalid) {
EXPECT_EQ(1U, GetPcAdjustment(0x800U, invalid_elf, ARCH_X86_64));
}
+TEST_F(RegsTest, regs_convert) {
+ RegsArm arm;
+ EXPECT_EQ(0, arm.Convert(0));
+ EXPECT_EQ(0x1c22, arm.Convert(0x1c22));
+ RegsArm64 arm64;
+ EXPECT_EQ(0, arm64.Convert(0));
+ EXPECT_EQ(0x1c22, arm64.Convert(0x1c22));
+ RegsX86 x86;
+ EXPECT_EQ(0, x86.Convert(0));
+ EXPECT_EQ(0x1c22, x86.Convert(0x1c22));
+ RegsX86_64 x86_64;
+ EXPECT_EQ(0, x86_64.Convert(0));
+ EXPECT_EQ(0x1c22, x86_64.Convert(0x1c22));
+}
+
TEST_F(RegsTest, arm_verify_sp_pc) {
RegsArm arm;
uint32_t* regs = reinterpret_cast<uint32_t*>(arm.RawData());
@@ -205,6 +221,14 @@ TEST_F(RegsTest, riscv64_verify_sp_pc) {
EXPECT_EQ(0x1abcd0000U, riscv64.pc());
}
+TEST_F(RegsTest, riscv_convert) {
+ RegsRiscv64 regs;
+ EXPECT_EQ(0, regs.Convert(0));
+ EXPECT_EQ(RISCV64_REG_REAL_COUNT - 1, regs.Convert(RISCV64_REG_REAL_COUNT - 1));
+ EXPECT_EQ(RISCV64_REG_VLENB, regs.Convert(0x1c22));
+ EXPECT_EQ(RISCV64_REG_COUNT, regs.Convert(RISCV64_REG_VLENB));
+}
+
TEST_F(RegsTest, x86_verify_sp_pc) {
RegsX86 x86;
uint32_t* regs = reinterpret_cast<uint32_t*>(x86.RawData());
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 156fac6..7f7dee3 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1761,5 +1761,43 @@ TEST_F(UnwindOfflineTest, zlib_compress_arm) {
EXPECT_EQ(0xff96c4f8ULL, unwinder.frames()[6].sp);
}
+// Make sure that an unwind using vlenb works properly.
+TEST_F(UnwindOfflineTest, vlenb_riscv64) {
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "vlenb_riscv64/", .arch = ARCH_RISCV64},
+ &error_msg))
+ FAIL() << error_msg;
+
+ Regs* regs = offline_utils_.GetRegs();
+ std::unique_ptr<Regs> regs_copy(regs->Clone());
+ Unwinder unwinder(128, offline_utils_.GetMaps(), regs, offline_utils_.GetProcessMemory());
+ unwinder.Unwind();
+
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
+ EXPECT_EQ(0x555c440735dcULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7fffe388a680ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x555c4408bdfcULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7fffe388a6a0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x555c4408cc76ULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7fffe388a6e0ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x555c4408d604ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7fffe388a730ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x555c44099cd2ULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7fffe388a7b0ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x555c4409939eULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7fffe388a930ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x555c440a2c7cULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7fffe388a970ULL, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x7ff2fdf27394ULL, unwinder.frames()[7].pc);
+ EXPECT_EQ(0x7fffe388bb20ULL, unwinder.frames()[7].sp);
+}
+
} // namespace
} // namespace unwindstack
diff --git a/libunwindstack/tests/VerifyBionicTerminationTest.cpp b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
index 4a93e2d..680d7c2 100644
--- a/libunwindstack/tests/VerifyBionicTerminationTest.cpp
+++ b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
@@ -24,12 +24,12 @@
#include <gtest/gtest.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <unwindstack/DwarfSection.h>
#include <unwindstack/Elf.h>
#include <unwindstack/ElfInterface.h>
-#include <unwindstack/Regs.h>
-#include <unwindstack/RegsGetLocal.h>
-#include <unwindstack/Unwinder.h>
+
+#include "ForkTest.h"
// This test is specific to bionic to verify that __libc_init is
// properly setting the return address to undefined so that the
@@ -37,11 +37,13 @@
namespace unwindstack {
-static std::string DumpFrames(const UnwinderFromPid& unwinder) {
+using VerifyBionicTermination = ForkTest;
+
+static std::string DumpFrames(const AndroidUnwinderData& data, AndroidUnwinder& unwinder) {
// Init this way so that the first frame of the backtrace starts on a new line.
std::string unwind("\n");
- for (size_t i = 0; i < unwinder.NumFrames(); i++) {
- unwind += unwinder.FormatFrame(i) + '\n';
+ for (auto& frame : data.frames) {
+ unwind += unwinder.FormatFrame(frame) + '\n';
}
return unwind;
}
@@ -90,31 +92,25 @@ static void VerifyReturnAddress(const FrameData& frame) {
ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location);
}
-// This test assumes that it starts from the main thread, and that the
+// This assumes that the function starts from the main thread, and that the
// libc.so on device will include symbols so that function names can
// be resolved.
-TEST(VerifyBionicTermination, local_terminate) {
- std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
-
- UnwinderFromPid unwinder(512, getpid());
- unwinder.SetRegs(regs.get());
+static void VerifyLibcInitTerminate(AndroidUnwinder& unwinder) {
+ AndroidUnwinderData data;
+ ASSERT_TRUE(unwinder.Unwind(data));
- RegsGetLocal(regs.get());
- unwinder.Unwind();
- ASSERT_LT(0U, unwinder.NumFrames());
-
- SCOPED_TRACE(DumpFrames(unwinder));
+ SCOPED_TRACE(DumpFrames(data, unwinder));
// Look for the frame that includes __libc_init, there should only
// be one and it should be the last.
bool found = false;
- const std::vector<FrameData>& frames = unwinder.frames();
- for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ const std::vector<FrameData>& frames = data.frames;
+ for (size_t i = 0; i < frames.size(); i++) {
const FrameData& frame = frames[i];
if (frame.function_name == "__libc_init" && frame.map_info != nullptr &&
!frame.map_info->name().empty() &&
std::string("libc.so") == basename(frame.map_info->name().c_str())) {
- ASSERT_EQ(unwinder.NumFrames(), i + 1) << "__libc_init is not last frame.";
+ ASSERT_EQ(frames.size(), i + 1) << "__libc_init is not last frame.";
ASSERT_NO_FATAL_FAILURE(VerifyReturnAddress(frame));
found = true;
}
@@ -122,6 +118,18 @@ TEST(VerifyBionicTermination, local_terminate) {
ASSERT_TRUE(found) << "Unable to find libc.so:__libc_init frame\n";
}
+TEST_F(VerifyBionicTermination, local_terminate) {
+ AndroidLocalUnwinder unwinder;
+ VerifyLibcInitTerminate(unwinder);
+}
+
+TEST_F(VerifyBionicTermination, remote_terminate) {
+ ASSERT_NO_FATAL_FAILURE(Fork());
+
+ AndroidRemoteUnwinder unwinder(pid_);
+ VerifyLibcInitTerminate(unwinder);
+}
+
} // namespace unwindstack
#endif
diff --git a/libunwindstack/utils/OfflineUnwindUtils.cpp b/libunwindstack/utils/OfflineUnwindUtils.cpp
index 3547120..0d5e15c 100644
--- a/libunwindstack/utils/OfflineUnwindUtils.cpp
+++ b/libunwindstack/utils/OfflineUnwindUtils.cpp
@@ -384,20 +384,16 @@ bool ReadRegs(RegsImpl<AddressType>* regs,
while (!feof(fp)) {
uint64_t value;
char reg_name[100];
- if (fscanf(fp, "%s %" SCNx64 "\n", reg_name, &value) != 2) {
+ if (fscanf(fp, "%[^:]: %" SCNx64 "\n", reg_name, &value) != 2) {
err_stream << "Failed to read in register name/values from '" << offline_files_path
<< "regs.txt'.";
*error_msg = err_stream.str();
return false;
}
std::string name(reg_name);
- if (!name.empty()) {
- // Remove the : from the end.
- name.resize(name.size() - 1);
- }
auto entry = name_to_reg.find(name);
if (entry == name_to_reg.end()) {
- err_stream << "Unknown register named " << reg_name;
+ err_stream << "Unknown register named " << name;
*error_msg = err_stream.str();
return false;
}
@@ -513,7 +509,7 @@ std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::riscv64_regs_ = {
{"s8", RISCV64_REG_S8}, {"s9", RISCV64_REG_S9}, {"s10", RISCV64_REG_S10},
{"s11", RISCV64_REG_S11}, {"t0", RISCV64_REG_T0}, {"t1", RISCV64_REG_T1},
{"t2", RISCV64_REG_T2}, {"t3", RISCV64_REG_T3}, {"t4", RISCV64_REG_T4},
- {"t5", RISCV64_REG_T5}, {"t6", RISCV64_REG_T6},
+ {"t5", RISCV64_REG_T5}, {"t6", RISCV64_REG_T6}, {"vlenb", RISCV64_REG_VLENB},
};
std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::x86_regs_ = {