jasonmolenda created this revision.
jasonmolenda added reviewers: jingham, JDevlieghere.
jasonmolenda added a project: LLDB.
Herald added subscribers: pengfei, kristof.beyls.
Herald added a project: All.
jasonmolenda requested review of this revision.
Herald added a subscriber: lldb-commits.

When lldb is looking for a location list entry for a variable, it is using the 
pc value -- or return-pc value when up the stack -- to pick the correct DWARF 
location list entry.  Because it is using return-pc, the entry selected may be 
an entirely different basic block, or function.  We should be using the "pc for 
symbolication" -- return-pc decremented in most cases -- to select the correct 
block we should be looking up.  I added RegisterContext::GetPCForSymbolication 
and StackFrame::GetFrameCodeAddressForSymbolication last year in 
https://reviews.llvm.org/D97644 and this is a simple patch with those in place.

The bug that drove this is in the middle of a complex project, but I can elicit 
incorrect behavior with a small C file that calls abort() as its last 
instruction, with a different function immediately after that.  It must be 
built at -O1 with clang (on x86_64, AArch64) so that the debug info tracks 
variables in different registers during the function lifetime, using a DWARF 
location list.

I constructed a test case that does this, but it depends on clang's current 
codegen at -O1 on these two architectures.  If the codegen were to shift 
around, the test could pass without correctly exercising this codepath.  One 
possibility would be to include a test binary, DWARF for it, and corefile; that 
would eliminate the possibility of codegen changing.  But it's a lot of data, 
and I'm not sure how necessary it is.  Given that I get the same codegen on two 
architectures with clang at -O1, maybe this is a small enough exposure to the 
optimizer that we can use it in the testsuite.  Open to suggestions.

Without this patch, this test case will show argv as unavailable when you try 
to print it.  In the actual bug that I was tracking down, the return-pc pointed 
to a different location list entry for the variable so lldb showed the wrong 
value for that variable.  A much more subtle and confusing bug for the user.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D124597

Files:
  lldb/source/Expression/DWARFExpression.cpp
  lldb/test/API/functionalities/location-list-lookup/Makefile
  lldb/test/API/functionalities/location-list-lookup/TestLocationListLookup.py
  lldb/test/API/functionalities/location-list-lookup/main.c

Index: lldb/test/API/functionalities/location-list-lookup/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/location-list-lookup/main.c
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+// The goal with this test is:
+//  1. Have main() followed by foo()
+//  2. Have the no-return call to abort() in main be the last instruction
+//  3. Have the next instruction be the start of foo()
+//  4. The debug info for argv uses a location list.
+//     clang at -O0 on x86_64 or arm64 has debuginfo like
+//          DW_AT_location	(0x00000049:
+//              [0x0000000100003f15, 0x0000000100003f25): DW_OP_reg4 RSI
+//              [0x0000000100003f25, 0x0000000100003f5b): DW_OP_reg15 R15)
+
+void foo(int);
+int main (int argc, char **argv)
+{
+    char *file = argv[0];
+    char f0 = file[0];
+    printf ("%c\n", f0);
+    foo(f0);
+    printf ("%s %d\n", argv[0], argc);
+    abort();                  /// argv is still be accessible here
+}
+void foo(int in) {
+    printf ("%d\n", in);
+}
Index: lldb/test/API/functionalities/location-list-lookup/TestLocationListLookup.py
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/location-list-lookup/TestLocationListLookup.py
@@ -0,0 +1,40 @@
+"""Test that lldb picks the correct DWARF location list entry with a return-pc out of bounds."""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class LocationListLookupTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+
+    def test_loclist(self):
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+
+        # Create a target by the debugger.
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+        self.dbg.SetAsync(False)
+
+        li = lldb.SBLaunchInfo(["a.out"])
+        error = lldb.SBError()
+        process = target.Launch(li, error)
+        self.assertTrue(process.IsValid())
+        self.assertTrue(process.is_stopped)
+
+        # Find `main` on the stack, then 
+        # find `argv` local variable, then
+        # check that we can read the c-string in argv[0]
+        for f in process.GetSelectedThread().frames:
+            if f.GetDisplayFunctionName() == "main":
+                argv = f.GetValueForVariablePath("argv").GetChildAtIndex(0)
+                strm = lldb.SBStream()
+                argv.GetDescription(strm)
+                self.assertNotEqual(strm.GetData().find('a.out'), -1)
Index: lldb/test/API/functionalities/location-list-lookup/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/location-list-lookup/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+CFLAGS_EXTRAS := -O1
+include Makefile.rules
Index: lldb/source/Expression/DWARFExpression.cpp
===================================================================
--- lldb/source/Expression/DWARFExpression.cpp
+++ lldb/source/Expression/DWARFExpression.cpp
@@ -858,29 +858,28 @@
   ModuleSP module_sp = m_module_wp.lock();
 
   if (IsLocationList()) {
-    addr_t pc;
+    Address pc;
     StackFrame *frame = nullptr;
-    if (reg_ctx)
-      pc = reg_ctx->GetPC();
-    else {
+    if (!reg_ctx || !reg_ctx->GetPCForSymbolication(pc)) {
       frame = exe_ctx->GetFramePtr();
       if (!frame)
         return false;
       RegisterContextSP reg_ctx_sp = frame->GetRegisterContext();
       if (!reg_ctx_sp)
         return false;
-      pc = reg_ctx_sp->GetPC();
+      reg_ctx_sp->GetPCForSymbolication(pc);
     }
 
     if (func_load_addr != LLDB_INVALID_ADDRESS) {
-      if (pc == LLDB_INVALID_ADDRESS) {
+      if (!pc.IsValid()) {
         if (error_ptr)
           error_ptr->SetErrorString("Invalid PC in frame.");
         return false;
       }
 
-      if (llvm::Optional<DataExtractor> expr =
-              GetLocationExpression(func_load_addr, pc)) {
+      Target *target = exe_ctx->GetTargetPtr();
+      if (llvm::Optional<DataExtractor> expr = GetLocationExpression(
+              func_load_addr, pc.GetLoadAddress(target))) {
         return DWARFExpression::Evaluate(
             exe_ctx, reg_ctx, module_sp, *expr, m_dwarf_cu, m_reg_kind,
             initial_value_ptr, object_address_ptr, result, error_ptr);
@@ -2862,7 +2861,7 @@
     if (load_function_start == LLDB_INVALID_ADDRESS)
       return false;
 
-    addr_t pc = frame.GetFrameCodeAddress().GetLoadAddress(
+    addr_t pc = frame.GetFrameCodeAddressForSymbolication().GetLoadAddress(
         frame.CalculateTarget().get());
 
     if (llvm::Optional<DataExtractor> expr =
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to