Change last-ditch magic address in IRMemoryMap::FindSpace
authorJason Molenda <jason@molenda.com>
Mon, 14 Nov 2022 17:50:58 +0000 (09:50 -0800)
committerJason Molenda <jason@molenda.com>
Mon, 14 Nov 2022 17:54:05 +0000 (09:54 -0800)
When we cannot allocate memory in the inferior process, the IR
interpreter's IRMemoryMap::FindSpace will create an lldb local
buffer and assign it an address range in the inferior address
space.  When the interpreter sees an address in that range, it
will read/write from the local buffer instead of the target.  If
this magic address overlaps with actual data in the target, the
target cannot be accessed through expressions.

Instead of using a high memory address that is validly addressable,
this patch uses an address that cannot be accessed on 64-bit systems
that don't actually use all 64 bits of the virtual address.

Differential Revision: https://reviews.llvm.org/D137682
rdar://96248287

lldb/source/Expression/DWARFExpression.cpp
lldb/source/Expression/IRMemoryMap.cpp
lldb/test/API/lang/c/high-mem-global/Makefile [new file with mode: 0644]
lldb/test/API/lang/c/high-mem-global/TestHighMemGlobal.py [new file with mode: 0644]
lldb/test/API/lang/c/high-mem-global/main.c [new file with mode: 0644]

index 3f302e5..3c36587 100644 (file)
@@ -917,9 +917,8 @@ bool DWARFExpression::Evaluate(
         stack.back().SetValueType(Value::ValueType::FileAddress);
         // Convert the file address to a load address, so subsequent
         // DWARF operators can operate on it.
-        if (frame)
-          stack.back().ConvertToLoadAddress(module_sp.get(),
-                                            frame->CalculateTarget().get());
+        if (target)
+          stack.back().ConvertToLoadAddress(module_sp.get(), target);
       }
       break;
 
index 6b8f7ba..3c102dd 100644 (file)
@@ -143,7 +143,7 @@ lldb::addr_t IRMemoryMap::FindSpace(size_t size) {
     if (address_byte_size != UINT32_MAX) {
       switch (address_byte_size) {
       case 8:
-        ret = 0xffffffff00000000ull;
+        ret = 0xdead0fff00000000ull;
         break;
       case 4:
         ret = 0xee000000ull;
diff --git a/lldb/test/API/lang/c/high-mem-global/Makefile b/lldb/test/API/lang/c/high-mem-global/Makefile
new file mode 100644 (file)
index 0000000..1049594
--- /dev/null
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/c/high-mem-global/TestHighMemGlobal.py b/lldb/test/API/lang/c/high-mem-global/TestHighMemGlobal.py
new file mode 100644 (file)
index 0000000..b0df19a
--- /dev/null
@@ -0,0 +1,59 @@
+"""Look that lldb can display a global loaded in high memory at an addressable address."""
+
+
+import lldb
+from lldbsuite.test.lldbtest import *
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.decorators import *
+
+class TestHighMemGlobal(TestBase):
+
+    NO_DEBUG_INFO_TESTCASE = True
+
+    @skipUnlessDarwin  # hardcoding of __DATA segment name
+    def test_command_line(self):
+        """Test that we can display a global variable loaded in high memory."""
+        self.build()
+
+        exe = self.getBuildArtifact("a.out")
+        err = lldb.SBError()
+
+        target = self.dbg.CreateTarget(exe, '', '', False, err)
+        self.assertTrue(target.IsValid())
+        module = target.GetModuleAtIndex(0)
+        self.assertTrue(module.IsValid())
+        data_segment = module.FindSection("__DATA")
+        self.assertTrue(data_segment.IsValid())
+        err.Clear()
+
+        self.expect("expr -- global.c", substrs=[' = 1'])
+        self.expect("expr -- global.d", substrs=[' = 2'])
+        self.expect("expr -- global.e", substrs=[' = 3'])
+
+        err = target.SetSectionLoadAddress(data_segment, 0xffffffff00000000)
+        self.assertTrue(err.Success())
+        self.expect("expr -- global.c", substrs=[' = 1'])
+        self.expect("expr -- global.d", substrs=[' = 2'])
+        self.expect("expr -- global.e", substrs=[' = 3'])
+
+        err = target.SetSectionLoadAddress(data_segment, 0x0000088100004000)
+        self.assertTrue(err.Success())
+        self.expect("expr -- global.c", substrs=[' = 1'])
+        self.expect("expr -- global.d", substrs=[' = 2'])
+        self.expect("expr -- global.e", substrs=[' = 3'])
+
+        # This is an address in IRMemoryMap::FindSpace where it has an 
+        # lldb-side buffer of memory that's used in IR interpreters when
+        # memory cannot be allocated in the inferior / functions cannot
+        # be jitted.
+        err = target.SetSectionLoadAddress(data_segment, 0xdead0fff00000000)
+        self.assertTrue(err.Success())
+
+        # The global variable `global` is now overlayed by this 
+        # IRMemoryMap special buffer, and now we cannot see the variable.
+        # Testing that we get the incorrect values at this address ensures 
+        # that IRMemoryMap::FindSpace and this test stay in sync.
+        self.runCmd("expr -- int $global_c = global.c")
+        self.runCmd("expr -- int $global_d = global.d")
+        self.runCmd("expr -- int $global_e = global.e")
+        self.expect("expr -- $global_c != 1 || $global_d != 2 || $global_e != 3", substrs=[' = true'])
diff --git a/lldb/test/API/lang/c/high-mem-global/main.c b/lldb/test/API/lang/c/high-mem-global/main.c
new file mode 100644 (file)
index 0000000..ae75f64
--- /dev/null
@@ -0,0 +1,9 @@
+
+struct mystruct {
+  int c, d, e;
+} global = {1, 2, 3};
+
+int main ()
+{
+  return global.c; // break here
+}