Skip to content

[lldb] Add constant value mode for RegisterLocation in UnwindPlans #100624

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

felipepiovezan
Copy link
Contributor

This is useful for language runtimes that compute register values by inspecting the state of the currently running process. Currently, there are no mechanisms enabling these runtimes to set register values to arbitrary values.

The alternative considered would involve creating a dwarf expression that produces an arbitrary integer (e.g. using OP_constu). However, the current data structure for Rows is such that they do not own any memory associated with dwarf expressions, which implies any such expression would need to have static storage and therefore could not contain a runtime value.

Adding a new rule for constants leads to a simpler implementation. It's also worth noting that this does not make the "Location" union any bigger, since it already contains a pointer+size pair.

This is useful for language runtimes that compute register values by inspecting
the state of the currently running process. Currently, there are no mechanisms
enabling these runtimes to set register values to arbitrary values.

The alternative considered would involve creating a dwarf expression that
produces an arbitrary integer (e.g. using OP_constu). However, the current data
structure for Rows is such that they do not own any memory associated with dwarf
expressions, which implies any such expression would need to have static storage
and therefore could not contain a runtime value.

Adding a new rule for constants leads to a simpler implementation. It's also
worth noting that this does not make the "Location" union any bigger, since it
already contains a pointer+size pair.
@llvmbot
Copy link
Member

llvmbot commented Jul 25, 2024

@llvm/pr-subscribers-lldb

Author: Felipe de Azevedo Piovezan (felipepiovezan)

Changes

This is useful for language runtimes that compute register values by inspecting the state of the currently running process. Currently, there are no mechanisms enabling these runtimes to set register values to arbitrary values.

The alternative considered would involve creating a dwarf expression that produces an arbitrary integer (e.g. using OP_constu). However, the current data structure for Rows is such that they do not own any memory associated with dwarf expressions, which implies any such expression would need to have static storage and therefore could not contain a runtime value.

Adding a new rule for constants leads to a simpler implementation. It's also worth noting that this does not make the "Location" union any bigger, since it already contains a pointer+size pair.


Full diff: https://github.com/llvm/llvm-project/pull/100624.diff

3 Files Affected:

  • (modified) lldb/include/lldb/Symbol/UnwindPlan.h (+16-1)
  • (modified) lldb/source/Symbol/UnwindPlan.cpp (+17)
  • (modified) lldb/source/Target/RegisterContextUnwind.cpp (+9)
diff --git a/lldb/include/lldb/Symbol/UnwindPlan.h b/lldb/include/lldb/Symbol/UnwindPlan.h
index ebb0ec421da72..a9e8406608ff3 100644
--- a/lldb/include/lldb/Symbol/UnwindPlan.h
+++ b/lldb/include/lldb/Symbol/UnwindPlan.h
@@ -68,7 +68,8 @@ class UnwindPlan {
         isAFAPlusOffset,   // reg = AFA + offset
         inOtherRegister,   // reg = other reg
         atDWARFExpression, // reg = deref(eval(dwarf_expr))
-        isDWARFExpression  // reg = eval(dwarf_expr)
+        isDWARFExpression, // reg = eval(dwarf_expr)
+        isConstant         // reg = constant
       };
 
       RegisterLocation() : m_location() {}
@@ -105,6 +106,15 @@ class UnwindPlan {
 
       bool IsDWARFExpression() const { return m_type == isDWARFExpression; }
 
+      bool IsConstant() const { return m_type == isConstant; }
+
+      void SetIsConstant(uint64_t value) {
+        m_type = isConstant;
+        m_location.constant_value = value;
+      }
+
+      uint64_t GetConstant() const { return m_location.constant_value; }
+
       void SetAtCFAPlusOffset(int32_t offset) {
         m_type = atCFAPlusOffset;
         m_location.offset = offset;
@@ -192,6 +202,8 @@ class UnwindPlan {
           const uint8_t *opcodes;
           uint16_t length;
         } expr;
+        // For m_type == isConstant
+        uint64_t constant_value;
       } m_location;
     };
 
@@ -358,6 +370,9 @@ class UnwindPlan {
 
     bool SetRegisterLocationToSame(uint32_t reg_num, bool must_replace);
 
+    bool SetRegisterLocationToIsConstant(uint32_t reg_num, uint64_t constant,
+                                         bool can_replace);
+
     // When this UnspecifiedRegistersAreUndefined mode is
     // set, any register that is not specified by this Row will
     // be described as Undefined.
diff --git a/lldb/source/Symbol/UnwindPlan.cpp b/lldb/source/Symbol/UnwindPlan.cpp
index e258a4e3d82f2..fcd3154a01d82 100644
--- a/lldb/source/Symbol/UnwindPlan.cpp
+++ b/lldb/source/Symbol/UnwindPlan.cpp
@@ -46,6 +46,8 @@ operator==(const UnwindPlan::Row::RegisterLocation &rhs) const {
         return !memcmp(m_location.expr.opcodes, rhs.m_location.expr.opcodes,
                        m_location.expr.length);
       break;
+    case isConstant:
+      return m_location.constant_value == rhs.m_location.constant_value;
     }
   }
   return false;
@@ -153,6 +155,9 @@ void UnwindPlan::Row::RegisterLocation::Dump(Stream &s,
     if (m_type == atDWARFExpression)
       s.PutChar(']');
   } break;
+  case isConstant:
+    s.Printf("=%x", m_location.offset);
+    break;
   }
 }
 
@@ -351,6 +356,18 @@ bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num,
   return true;
 }
 
+bool UnwindPlan::Row::SetRegisterLocationToIsConstant(uint32_t reg_num,
+                                                      uint64_t constant,
+                                                      bool can_replace) {
+  if (!can_replace &&
+      m_register_locations.find(reg_num) != m_register_locations.end())
+    return false;
+  RegisterLocation reg_loc;
+  reg_loc.SetIsConstant(constant);
+  m_register_locations[reg_num] = reg_loc;
+  return true;
+}
+
 bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const {
   return m_offset == rhs.m_offset && m_cfa_value == rhs.m_cfa_value &&
          m_afa_value == rhs.m_afa_value &&
diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp
index 95e8abd763d53..f74f1dc0e1b80 100644
--- a/lldb/source/Target/RegisterContextUnwind.cpp
+++ b/lldb/source/Target/RegisterContextUnwind.cpp
@@ -1694,6 +1694,15 @@ RegisterContextUnwind::SavedLocationForRegister(
     return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
   }
 
+  if (unwindplan_regloc.IsConstant()) {
+    regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
+    regloc.location.inferred_value = unwindplan_regloc.GetConstant();
+    m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+    UnwindLogMsg("supplying caller's register %s (%d) via constant value",
+                 regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+    return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+  }
+
   UnwindLogMsg("no save location for %s (%d) in this stack frame",
                regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
 

Copy link
Collaborator

@jasonmolenda jasonmolenda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. One small nit on the Dump method.

As a bit of background, this would be very unusual for a normal UnwindPlan that is reused for a function in many different contexts (different places on the stack during the program runtime). Felipe is dealing with the special case when the Swift LanguageRuntime creates an UnwindPlan that is used once in a given stack, and not saved in the UnwindTable for this Module. Felipe could have constructed a DWARF expression, where the register value was stored in memory, or a DWARF expression with a constant value, but then we'd need to add a DataBufferSP to the RegisterLocation to have ownership of this heap-allocated dwarf expression, in addition to hand-creating the DWARF expression bytecodes. It was a lot more straightforward to add this type of "this register is value x" for this one-shot UnwindPlan's that a LanguageRuntime can provide.

@@ -153,6 +155,9 @@ void UnwindPlan::Row::RegisterLocation::Dump(Stream &s,
if (m_type == atDWARFExpression)
s.PutChar(']');
} break;
case isConstant:
s.Printf("=%x", m_location.offset);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the Printf be ("=0x%" PRIx64, m_location.constant_value)? If you do an unwind with one of these register locations in use, we should see this output when the unwind engine shows the unwind rules being used for this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh oops, you're totally right! Fixed!

@felipepiovezan felipepiovezan merged commit 9fe455f into llvm:main Jul 31, 2024
6 checks passed
@felipepiovezan felipepiovezan deleted the felipe/unwinding_constant_rule branch July 31, 2024 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants