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
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 @@
         linux: {
             runtime_libs: ["libdexfile"], // libdexfile_support dependency
         },
+        musl: {
+            cflags: ["-DNT_RISCV_VECTOR=0x901"],
+        },
     },
 
     arch: {
@@ -441,6 +444,7 @@
         "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 @@
 // 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 @@
 
 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 @@
       *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 @@
 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 @@
   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 @@
   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 @@
 
   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 @@
 
   // 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 @@
   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 @@
   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 @@
 
   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 @@
       "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 @@
 
   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 @@
   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 @@
           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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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());
+static void VerifyLibcInitTerminate(AndroidUnwinder& unwinder) {
+  AndroidUnwinderData data;
+  ASSERT_TRUE(unwinder.Unwind(data));
 
-  UnwinderFromPid unwinder(512, getpid());
-  unwinder.SetRegs(regs.get());
-
-  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 @@
   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 @@
   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 @@
     {"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_ = {