[Lldb-commits] [lldb] e28157e - [lldb][AArch64][Linux] Add field information for the CPSR register (#70300)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Nov 8 02:17:42 PST 2023
Author: David Spickett
Date: 2023-11-08T10:17:38Z
New Revision: e28157e778423fd9d39c9065ef06e841fc320f09
URL: https://github.com/llvm/llvm-project/commit/e28157e778423fd9d39c9065ef06e841fc320f09
DIFF: https://github.com/llvm/llvm-project/commit/e28157e778423fd9d39c9065ef06e841fc320f09.diff
LOG: [lldb][AArch64][Linux] Add field information for the CPSR register (#70300)
The contents of which are mostly SPSR_EL1 as shown in the Arm manual,
with a few adjustments for things Linux says userspace shouldn't concern
itself with.
```
(lldb) register read cpsr
cpsr = 0x80001000
= (N = 1, Z = 0, C = 0, V = 0, SS = 0, IL = 0, ...
```
Some fields are always present, some depend on extensions. I've checked
for those extensions using HWCAP and HWCAP2.
To provide this for core files and live processes I've added a new class
LinuxArm64RegisterFlags. This is a container for all the registers we'll
want to have fields and handles detecting fields and updating register
info.
This is used by the native process as follows:
* There is a global LinuxArm64RegisterFlags object.
* The first thread takes a mutex on it, and updates the fields.
* Subsequent threads see that detection is already done, and skip it.
* All threads then update their own copy of the register information
with pointers to the field information contained in the global object.
This means that even though every thread will have the same fields, we
only detect them once and have one copy of the information.
Core files instead have a LinuxArm64RegisterFlags as a member, because
each core file could have different saved capabilities. The logic from
there is the same but we get HWACP values from the corefile note.
This handler class is Linux specific right now, but it can easily be
made more generic if needed. For example by using LLVM's FeatureBitset
instead of HWCAPs.
Updating register info is done with string comparison, which isn't
ideal. For CPSR, we do know the register number ahead of time but we do
not for other registers in dynamic register sets. So in the interest of
consistency, I'm going to use string comparison for all registers
including cpsr.
I've added tests with a core file and live process. Only checking for
fields that are always present to account for CPU variance.
Added:
lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.cpp
lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.h
Modified:
lldb/include/lldb/Target/RegisterFlags.h
lldb/include/lldb/lldb-private-types.h
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
lldb/source/Plugins/Process/Utility/CMakeLists.txt
lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp
lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h
lldb/source/Target/RegisterFlags.cpp
lldb/test/API/commands/register/register/register_command/TestRegisters.py
lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
Removed:
################################################################################
diff --git a/lldb/include/lldb/Target/RegisterFlags.h b/lldb/include/lldb/Target/RegisterFlags.h
index a088981918cb349..9b343e445678ab3 100644
--- a/lldb/include/lldb/Target/RegisterFlags.h
+++ b/lldb/include/lldb/Target/RegisterFlags.h
@@ -84,6 +84,11 @@ class RegisterFlags {
RegisterFlags(std::string id, unsigned size,
const std::vector<Field> &fields);
+ /// Replace all the fields with the new set of fields. All the assumptions
+ /// and checks apply as when you use the constructor. Intended to only be used
+ /// when runtime field detection is needed.
+ void SetFields(const std::vector<Field> &fields);
+
// Reverse the order of the fields, keeping their values the same.
// For example a field from bit 31 to 30 with value 0b10 will become bits
// 1 to 0, with the same 0b10 value.
diff --git a/lldb/include/lldb/lldb-private-types.h b/lldb/include/lldb/lldb-private-types.h
index e6717836331f590..7d301666df1a174 100644
--- a/lldb/include/lldb/lldb-private-types.h
+++ b/lldb/include/lldb/lldb-private-types.h
@@ -62,7 +62,11 @@ struct RegisterInfo {
/// rax ax, ah, and al.
uint32_t *invalidate_regs;
/// If not nullptr, a type defined by XML descriptions.
- const RegisterFlags *flags_type;
+ /// Register info tables are constructed as const, but this field may need to
+ /// be updated if a specific target OS has a
diff erent layout. To enable that,
+ /// this is mutable. The data pointed to is still const, so you must swap a
+ /// whole set of flags for another.
+ mutable const RegisterFlags *flags_type;
llvm::ArrayRef<uint8_t> data(const uint8_t *context_base) const {
return llvm::ArrayRef<uint8_t>(context_base + byte_offset, byte_size);
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
index e23165933c221cf..9b5f7aef1efe535 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
@@ -23,6 +23,7 @@
#include "Plugins/Process/Linux/Procfs.h"
#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
#include "Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h"
+#include "Plugins/Process/Utility/RegisterFlagsLinux_arm64.h"
#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
// System includes - They have to be included after framework includes because
@@ -30,6 +31,7 @@
#include <sys/uio.h>
// NT_PRSTATUS and NT_FPREGSET definition
#include <elf.h>
+#include <mutex>
#include <optional>
#ifndef NT_ARM_SVE
@@ -59,12 +61,20 @@
#endif
#define HWCAP_PACA (1 << 30)
+
#define HWCAP2_MTE (1 << 18)
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::process_linux;
+// A NativeRegisterContext is constructed per thread, but all threads' registers
+// will contain the same fields. Therefore this mutex prevents each instance
+// competing with the other, and subsequent instances from having to detect the
+// fields all over again.
+static std::mutex g_register_flags_mutex;
+static LinuxArm64RegisterFlags g_register_flags;
+
std::unique_ptr<NativeRegisterContextLinux>
NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
const ArchSpec &target_arch, NativeThreadLinux &native_thread) {
@@ -134,6 +144,11 @@ NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskTLS);
+ std::lock_guard<std::mutex> lock(g_register_flags_mutex);
+ if (!g_register_flags.HasDetected())
+ g_register_flags.DetectFields(auxv_at_hwcap.value_or(0),
+ auxv_at_hwcap2.value_or(0));
+
auto register_info_up =
std::make_unique<RegisterInfoPOSIX_arm64>(target_arch, opt_regsets);
return std::make_unique<NativeRegisterContextLinux_arm64>(
@@ -156,6 +171,10 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
: NativeRegisterContextRegisterInfo(native_thread,
register_info_up.release()),
NativeRegisterContextLinux(native_thread) {
+ g_register_flags.UpdateRegisterInfo(
+ GetRegisterInfoInterface().GetRegisterInfo(),
+ GetRegisterInfoInterface().GetRegisterCount());
+
::memset(&m_fpr, 0, sizeof(m_fpr));
::memset(&m_gpr_arm64, 0, sizeof(m_gpr_arm64));
::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
diff --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt
index 1ebd0484f0210a6..37b53b7e3e7edde 100644
--- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt
@@ -47,6 +47,7 @@ add_lldb_library(lldbPluginProcessUtility
RegisterContextThreadMemory.cpp
RegisterContextWindows_i386.cpp
RegisterContextWindows_x86_64.cpp
+ RegisterFlagsLinux_arm64.cpp
RegisterInfos_x86_64_with_base_shared.cpp
RegisterInfoPOSIX_arm.cpp
RegisterInfoPOSIX_arm64.cpp
diff --git a/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.cpp b/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.cpp
new file mode 100644
index 000000000000000..043789bd6d21e47
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.cpp
@@ -0,0 +1,116 @@
+//===-- RegisterFlagsLinux_arm64.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterFlagsLinux_arm64.h"
+#include "lldb/lldb-private-types.h"
+
+// This file is built on all systems because it is used by native processes and
+// core files, so we manually define the needed HWCAP values here.
+
+#define HWCAP_DIT (1 << 24)
+#define HWCAP_SSBS (1 << 28)
+
+#define HWCAP2_BTI (1 << 17)
+#define HWCAP2_MTE (1 << 18)
+
+using namespace lldb_private;
+
+LinuxArm64RegisterFlags::Fields
+LinuxArm64RegisterFlags::DetectCPSRFields(uint64_t hwcap, uint64_t hwcap2) {
+ // The fields here are a combination of the Arm manual's SPSR_EL1,
+ // plus a few changes where Linux has decided not to make use of them at all,
+ // or at least not from userspace.
+
+ // Status bits that are always present.
+ std::vector<RegisterFlags::Field> cpsr_fields{
+ {"N", 31}, {"Z", 30}, {"C", 29}, {"V", 28},
+ // Bits 27-26 reserved.
+ };
+
+ if (hwcap2 & HWCAP2_MTE)
+ cpsr_fields.push_back({"TCO", 25});
+ if (hwcap & HWCAP_DIT)
+ cpsr_fields.push_back({"DIT", 24});
+
+ // UAO and PAN are bits 23 and 22 and have no meaning for userspace so
+ // are treated as reserved by the kernel.
+
+ cpsr_fields.push_back({"SS", 21});
+ cpsr_fields.push_back({"IL", 20});
+ // Bits 19-14 reserved.
+
+ // Bit 13, ALLINT, requires FEAT_NMI that isn't relevant to userspace, and we
+ // can't detect either, don't show this field.
+ if (hwcap & HWCAP_SSBS)
+ cpsr_fields.push_back({"SSBS", 12});
+ if (hwcap2 & HWCAP2_BTI)
+ cpsr_fields.push_back({"BTYPE", 10, 11});
+
+ cpsr_fields.push_back({"D", 9});
+ cpsr_fields.push_back({"A", 8});
+ cpsr_fields.push_back({"I", 7});
+ cpsr_fields.push_back({"F", 6});
+ // Bit 5 reserved
+ // Called "M" in the ARMARM.
+ cpsr_fields.push_back({"nRW", 4});
+ // This is a 4 bit field M[3:0] in the ARMARM, we split it into parts.
+ cpsr_fields.push_back({"EL", 2, 3});
+ // Bit 1 is unused and expected to be 0.
+ cpsr_fields.push_back({"SP", 0});
+
+ return cpsr_fields;
+}
+
+void LinuxArm64RegisterFlags::DetectFields(uint64_t hwcap, uint64_t hwcap2) {
+ for (auto ® : m_registers)
+ reg.m_flags.SetFields(reg.m_detector(hwcap, hwcap2));
+ m_has_detected = true;
+}
+
+void LinuxArm64RegisterFlags::UpdateRegisterInfo(const RegisterInfo *reg_info,
+ uint32_t num_regs) {
+ assert(m_has_detected &&
+ "Must call DetectFields before updating register info.");
+
+ // Register names will not be duplicated, so we do not want to compare against
+ // one if it has already been found. Each time we find one, we erase it from
+ // this list.
+ std::vector<std::pair<llvm::StringRef, const RegisterFlags *>>
+ search_registers;
+ for (const auto ® : m_registers) {
+ // It is possible that a register is all extension dependent fields, and
+ // none of them are present.
+ if (reg.m_flags.GetFields().size())
+ search_registers.push_back({reg.m_name, ®.m_flags});
+ }
+
+ // Walk register information while there are registers we know need
+ // to be updated. Example:
+ // Register information: [a, b, c, d]
+ // To be patched: [b, c]
+ // * a != b, a != c, do nothing and move on.
+ // * b == b, patch b, new patch list is [c], move on.
+ // * c == c, patch c, patch list is empty, exit early without looking at d.
+ for (uint32_t idx = 0; idx < num_regs && search_registers.size();
+ ++idx, ++reg_info) {
+ auto reg_it = std::find_if(
+ search_registers.cbegin(), search_registers.cend(),
+ [reg_info](auto reg) { return reg.first == reg_info->name; });
+
+ if (reg_it != search_registers.end()) {
+ // Attach the field information.
+ reg_info->flags_type = reg_it->second;
+ // We do not expect to see this name again so don't look for it again.
+ search_registers.erase(reg_it);
+ }
+ }
+
+ // We do not assert that search_registers is empty here, because it may
+ // contain registers from optional extensions that are not present on the
+ // current target.
+}
\ No newline at end of file
diff --git a/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.h b/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.h
new file mode 100644
index 000000000000000..6c7a3b61a1425ee
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.h
@@ -0,0 +1,78 @@
+//===-- RegisterFlagsLinux_arm64.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERFLAGSLINUX_ARM64_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERFLAGSLINUX_ARM64_H
+
+#include "lldb/Target/RegisterFlags.h"
+#include "llvm/ADT/StringRef.h"
+#include <functional>
+
+namespace lldb_private {
+
+struct RegisterInfo;
+
+/// This class manages the storage and detection of register field information
+/// for Arm64 Linux registers. The same register may have
diff erent fields on
+///
diff erent CPUs. This class abstracts out the field detection process so we
+/// can use it on live processes and core files.
+///
+/// The general way to use this class is:
+/// * Make an instance somewhere that will last as long as the debug session
+/// (because your final register info will point to this instance).
+/// * Read hardware capabilities from a core note, binary, prctl, etc.
+/// * Pass those to DetectFields.
+/// * Call UpdateRegisterInfo with your RegisterInfo to add pointers
+/// to the detected fields for all registers listed in this class.
+///
+/// This must be done in that order, and you should ensure that if multiple
+/// threads will reference the information, a mutex is used to make sure only
+/// one calls DetectFields.
+class LinuxArm64RegisterFlags {
+public:
+ /// For the registers listed in this class, detect which fields are
+ /// present. Must be called before UpdateRegisterInfos.
+ /// If called more than once, fields will be redetected each time from
+ /// scratch. If you do not have access to hwcap, just pass 0 for each one, you
+ /// will only get unconditional fields.
+ void DetectFields(uint64_t hwcap, uint64_t hwcap2);
+
+ /// Add the field information of any registers named in this class,
+ /// to the relevant RegisterInfo instances. Note that this will be done
+ /// with a pointer to the instance of this class that you call this on, so
+ /// the lifetime of that instance must be at least that of the register info.
+ void UpdateRegisterInfo(const RegisterInfo *reg_info, uint32_t num_regs);
+
+ /// Returns true if field detection has been run at least once.
+ bool HasDetected() const { return m_has_detected; }
+
+private:
+ using Fields = std::vector<RegisterFlags::Field>;
+ using DetectorFn = std::function<Fields(uint64_t, uint64_t)>;
+
+ static Fields DetectCPSRFields(uint64_t hwcap, uint64_t hwcap2);
+
+ struct RegisterEntry {
+ RegisterEntry(llvm::StringRef name, unsigned size, DetectorFn detector)
+ : m_name(name), m_flags(std::string(name) + "_flags", size, {{"", 0}}),
+ m_detector(detector) {}
+
+ llvm::StringRef m_name;
+ RegisterFlags m_flags;
+ DetectorFn m_detector;
+ } m_registers[1] = {
+ RegisterEntry("cpsr", 4, DetectCPSRFields),
+ };
+
+ // Becomes true once field detection has been run for all registers.
+ bool m_has_detected = false;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERFLAGSLINUX_ARM64_H
\ No newline at end of file
diff --git a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp
index 85073b56f64bf79..07501c10ec3c928 100644
--- a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp
+++ b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp
@@ -9,6 +9,9 @@
#include "RegisterContextPOSIXCore_arm64.h"
#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
+#include "Plugins/Process/Utility/AuxVector.h"
+#include "Plugins/Process/Utility/RegisterFlagsLinux_arm64.h"
+#include "Plugins/Process/elf-core/ProcessElfCore.h"
#include "Plugins/Process/elf-core/RegisterUtilities.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/RegisterValue.h"
@@ -74,6 +77,21 @@ RegisterContextCorePOSIX_arm64::RegisterContextCorePOSIX_arm64(
: RegisterContextPOSIX_arm64(thread, std::move(register_info)) {
::memset(&m_sme_pseudo_regs, 0, sizeof(m_sme_pseudo_regs));
+ ProcessElfCore *process =
+ static_cast<ProcessElfCore *>(thread.GetProcess().get());
+ if (process->GetArchitecture().GetTriple().getOS() == llvm::Triple::Linux) {
+ AuxVector aux_vec(process->GetAuxvData());
+ std::optional<uint64_t> auxv_at_hwcap =
+ aux_vec.GetAuxValue(AuxVector::AUXV_AT_HWCAP);
+ std::optional<uint64_t> auxv_at_hwcap2 =
+ aux_vec.GetAuxValue(AuxVector::AUXV_AT_HWCAP2);
+
+ m_linux_register_flags.DetectFields(auxv_at_hwcap.value_or(0),
+ auxv_at_hwcap2.value_or(0));
+ m_linux_register_flags.UpdateRegisterInfo(GetRegisterInfo(),
+ GetRegisterCount());
+ }
+
m_gpr_data.SetData(std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
gpregset.GetByteSize()));
m_gpr_data.SetByteOrder(gpregset.GetByteOrder());
diff --git a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h
index 95527af74fb5334..38e958851dfe23e 100644
--- a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h
+++ b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h
@@ -11,6 +11,7 @@
#include "Plugins/Process/Utility/LinuxPTraceDefines_arm64sve.h"
#include "Plugins/Process/Utility/RegisterContextPOSIX_arm64.h"
+#include "Plugins/Process/Utility/RegisterFlagsLinux_arm64.h"
#include "Plugins/Process/elf-core/RegisterUtilities.h"
#include "lldb/Utility/DataBufferHeap.h"
@@ -74,6 +75,8 @@ class RegisterContextCorePOSIX_arm64 : public RegisterContextPOSIX_arm64 {
struct sme_pseudo_regs m_sme_pseudo_regs;
+ lldb_private::LinuxArm64RegisterFlags m_linux_register_flags;
+
const uint8_t *GetSVEBuffer(uint64_t offset = 0);
void ConfigureRegisterContext();
diff --git a/lldb/source/Target/RegisterFlags.cpp b/lldb/source/Target/RegisterFlags.cpp
index 49974718ccb514a..b1669b85fd2fe7b 100644
--- a/lldb/source/Target/RegisterFlags.cpp
+++ b/lldb/source/Target/RegisterFlags.cpp
@@ -53,9 +53,7 @@ unsigned RegisterFlags::Field::PaddingDistance(const Field &other) const {
return lhs_start - rhs_end - 1;
}
-RegisterFlags::RegisterFlags(std::string id, unsigned size,
- const std::vector<Field> &fields)
- : m_id(std::move(id)), m_size(size) {
+void RegisterFlags::SetFields(const std::vector<Field> &fields) {
// We expect that the XML processor will discard anything describing flags but
// with no fields.
assert(fields.size() && "Some fields must be provided.");
@@ -63,6 +61,8 @@ RegisterFlags::RegisterFlags(std::string id, unsigned size,
// We expect that these are unsorted but do not overlap.
// They could fill the register but may have gaps.
std::vector<Field> provided_fields = fields;
+
+ m_fields.clear();
m_fields.reserve(provided_fields.size());
// ProcessGDBRemote should have sorted these in descending order already.
@@ -71,7 +71,7 @@ RegisterFlags::RegisterFlags(std::string id, unsigned size,
// Build a new list of fields that includes anonymous (empty name) fields
// wherever there is a gap. This will simplify processing later.
std::optional<Field> previous_field;
- unsigned register_msb = (size * 8) - 1;
+ unsigned register_msb = (m_size * 8) - 1;
for (auto field : provided_fields) {
if (previous_field) {
unsigned padding = previous_field->PaddingDistance(field);
@@ -96,6 +96,12 @@ RegisterFlags::RegisterFlags(std::string id, unsigned size,
m_fields.push_back(Field("", 0, previous_field->GetStart() - 1));
}
+RegisterFlags::RegisterFlags(std::string id, unsigned size,
+ const std::vector<Field> &fields)
+ : m_id(std::move(id)), m_size(size) {
+ SetFields(fields);
+}
+
void RegisterFlags::log(Log *log) const {
LLDB_LOG(log, "ID: \"{0}\" Size: {1}", m_id.c_str(), m_size);
for (const Field &field : m_fields)
diff --git a/lldb/test/API/commands/register/register/register_command/TestRegisters.py b/lldb/test/API/commands/register/register/register_command/TestRegisters.py
index f2ee3c4a047a269..f29b2ab5d8f259d 100644
--- a/lldb/test/API/commands/register/register/register_command/TestRegisters.py
+++ b/lldb/test/API/commands/register/register/register_command/TestRegisters.py
@@ -618,6 +618,18 @@ def test_info_register(self):
# This has an alternative name according to the ABI.
self.expect("register info x30", substrs=["Name: lr (x30)"])
+ @skipIfXmlSupportMissing
+ @skipUnlessPlatform(["linux"])
+ @skipIf(archs=no_match(["aarch64"]))
+ def test_register_read_fields(self):
+ """Test that when debugging a live process, we see the fields of the
+ CPSR register."""
+ self.build()
+ self.common_setup()
+
+ # N/Z/C/V bits will always be present, so check only for those.
+ self.expect("register read cpsr", substrs=["= (N = 0, Z = 1, C = 1, V = 0"])
+
@skipUnlessPlatform(["linux"])
@skipIf(archs=no_match(["x86_64"]))
def test_fs_gs_base(self):
diff --git a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
index 4ff288ad49c0155..a083fab18eabcbc 100644
--- a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
+++ b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
@@ -574,6 +574,10 @@ def test_aarch64_sve_regs_full(self):
self.expect("register read --all")
+ # Register field information should work with core files as it does a live process.
+ # The N/Z/C/V bits are always present so just check for those.
+ self.expect("register read cpsr", substrs=["= (N = 0, Z = 0, C = 0, V = 0"])
+
@skipIfLLVMTargetMissing("AArch64")
def test_aarch64_pac_regs(self):
# Test AArch64/Linux Pointer Authentication register read
More information about the lldb-commits
mailing list