1""" 2Make sure the getting a variable path works and doesn't crash. 3""" 4 5import lldb 6import lldbsuite.test.lldbutil as lldbutil 7from lldbsuite.test.decorators import * 8from lldbsuite.test.lldbtest import * 9 10 11class TestVTableValue(TestBase): 12 # If your test case doesn't stress debug info, then 13 # set this to true. That way it won't be run once for 14 # each debug info format. 15 NO_DEBUG_INFO_TESTCASE = True 16 17 @skipIf(compiler="clang", compiler_version=["<", "9.0"]) 18 @skipUnlessPlatform(["linux", "macosx"]) 19 def test_vtable(self): 20 self.build() 21 lldbutil.run_to_source_breakpoint( 22 self, "At the end", lldb.SBFileSpec("main.cpp") 23 ) 24 25 # Test a shape instance to make sure we get the vtable correctly. 26 shape = self.frame().FindVariable("shape") 27 vtable = shape.GetVTable() 28 self.assertEqual(vtable.GetName(), "vtable for Shape") 29 self.assertEqual(vtable.GetTypeName(), "vtable for Shape") 30 # Make sure we have the right number of virtual functions in our vtable 31 # for the shape class. 32 self.assertEqual(vtable.GetNumChildren(), 4) 33 34 # Verify vtable address 35 vtable_addr = vtable.GetValueAsUnsigned(0) 36 expected_addr = self.expected_vtable_addr(shape) 37 self.assertEqual(vtable_addr, expected_addr) 38 39 for idx, vtable_entry in enumerate(vtable.children): 40 self.verify_vtable_entry(vtable_entry, vtable_addr, idx) 41 42 # Test a shape reference to make sure we get the vtable correctly. 43 shape = self.frame().FindVariable("shape_ref") 44 vtable = shape.GetVTable() 45 self.assertEqual(vtable.GetName(), "vtable for Shape") 46 self.assertEqual(vtable.GetTypeName(), "vtable for Shape") 47 # Make sure we have the right number of virtual functions in our vtable 48 # for the shape class. 49 self.assertEqual(vtable.GetNumChildren(), 4) 50 51 # Verify vtable address 52 vtable_addr = vtable.GetValueAsUnsigned(0) 53 expected_addr = self.expected_vtable_addr(shape) 54 self.assertEqual(vtable_addr, expected_addr) 55 56 for idx, vtable_entry in enumerate(vtable.children): 57 self.verify_vtable_entry(vtable_entry, vtable_addr, idx) 58 59 # Test we get the right vtable for the Rectangle instance. 60 rect = self.frame().FindVariable("rect") 61 vtable = rect.GetVTable() 62 self.assertEqual(vtable.GetName(), "vtable for Rectangle") 63 self.assertEqual(vtable.GetTypeName(), "vtable for Rectangle") 64 65 # Make sure we have the right number of virtual functions in our vtable 66 # with the extra virtual function added by the Rectangle class 67 self.assertEqual(vtable.GetNumChildren(), 5) 68 69 # Verify vtable address 70 vtable_addr = vtable.GetValueAsUnsigned() 71 expected_addr = self.expected_vtable_addr(rect) 72 self.assertEqual(vtable_addr, expected_addr) 73 74 for idx, vtable_entry in enumerate(vtable.children): 75 self.verify_vtable_entry(vtable_entry, vtable_addr, idx) 76 77 @skipIf(compiler="clang", compiler_version=["<", "9.0"]) 78 @skipUnlessPlatform(["linux", "macosx"]) 79 def test_base_class_ptr(self): 80 self.build() 81 (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( 82 self, "Shape is Rectangle", lldb.SBFileSpec("main.cpp") 83 ) 84 85 shape = self.frame().FindVariable("shape") 86 rect = self.frame().FindVariable("rect") 87 88 shape_ptr = self.frame().FindVariable("shape_ptr") 89 shape_ptr_vtable = shape_ptr.GetVTable() 90 self.assertEqual(shape_ptr_vtable.GetName(), "vtable for Rectangle") 91 self.assertEqual(shape_ptr_vtable.GetNumChildren(), 5) 92 self.assertEqual(shape_ptr.GetValueAsUnsigned(0), rect.GetLoadAddress()) 93 lldbutil.continue_to_source_breakpoint( 94 self, process, "Shape is Shape", lldb.SBFileSpec("main.cpp") 95 ) 96 self.assertEqual(shape_ptr.GetValueAsUnsigned(0), shape.GetLoadAddress()) 97 self.assertEqual(shape_ptr_vtable.GetNumChildren(), 4) 98 self.assertEqual(shape_ptr_vtable.GetName(), "vtable for Shape") 99 100 @skipUnlessPlatform(["linux", "macosx"]) 101 def test_no_vtable(self): 102 self.build() 103 lldbutil.run_to_source_breakpoint( 104 self, "At the end", lldb.SBFileSpec("main.cpp") 105 ) 106 107 var = self.frame().FindVariable("not_virtual") 108 self.assertEqual( 109 var.GetVTable().GetError().GetCString(), 110 'type "NotVirtual" doesn\'t have a vtable', 111 ) 112 113 var = self.frame().FindVariable("argc") 114 self.assertEqual( 115 var.GetVTable().GetError().GetCString(), 116 'no language runtime support for the language "c"', 117 ) 118 119 @skipUnlessPlatform(["linux", "macosx"]) 120 def test_overwrite_vtable(self): 121 self.build() 122 (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( 123 self, "At the end", lldb.SBFileSpec("main.cpp") 124 ) 125 126 # Test a shape instance to make sure we get the vtable correctly. 127 shape = self.frame().FindVariable("shape") 128 vtable = shape.GetVTable() 129 self.assertEqual(vtable.GetName(), "vtable for Shape") 130 self.assertEqual(vtable.GetTypeName(), "vtable for Shape") 131 # Make sure we have the right number of virtual functions in our vtable 132 # for the shape class. 133 self.assertEqual(vtable.GetNumChildren(), 4) 134 135 # Overwrite the first entry in the vtable and make sure we can still 136 # see the bogus value which should have no summary 137 vtable_addr = vtable.GetValueAsUnsigned() 138 139 is_64bit = self.process().GetAddressByteSize() == 8 140 data = str( 141 "\x01\x01\x01\x01\x01\x01\x01\x01" if is_64bit else "\x01\x01\x01\x01" 142 ) 143 error = lldb.SBError() 144 bytes_written = process.WriteMemory(vtable_addr, data, error) 145 146 self.assertSuccess(error) 147 self.assertGreater( 148 bytes_written, 0, "Failed to overwrite first entry in vtable" 149 ) 150 151 scribbled_child = vtable.GetChildAtIndex(0) 152 self.assertEqual( 153 scribbled_child.GetValueAsUnsigned(0), 154 0x0101010101010101 if is_64bit else 0x01010101, 155 ) 156 self.assertEqual(scribbled_child.GetSummary(), None) 157 158 def expected_vtable_addr(self, var: lldb.SBValue) -> int: 159 load_addr = var.GetLoadAddress() 160 read_from_memory_error = lldb.SBError() 161 vtable_addr = self.process().ReadPointerFromMemory( 162 load_addr, read_from_memory_error 163 ) 164 self.assertTrue(read_from_memory_error.Success()) 165 return vtable_addr 166 167 def expected_vtable_entry_func_ptr(self, vtable_addr: int, idx: int): 168 vtable_entry_addr = vtable_addr + idx * self.process().GetAddressByteSize() 169 read_func_ptr_error = lldb.SBError() 170 func_ptr = self.process().ReadPointerFromMemory( 171 vtable_entry_addr, read_func_ptr_error 172 ) 173 self.assertTrue(read_func_ptr_error.Success()) 174 return func_ptr 175 176 def verify_vtable_entry( 177 self, vtable_entry: lldb.SBValue, vtable_addr: int, idx: int 178 ): 179 """Verify the vtable entry looks something like: 180 181 (double ()) [0] = 0x0000000100003a10 a.out`Rectangle::Area() at main.cpp:14 182 183 """ 184 # Check function ptr 185 vtable_entry_func_ptr = vtable_entry.GetValueAsUnsigned(0) 186 self.assertEqual( 187 vtable_entry_func_ptr, 188 self.expected_vtable_entry_func_ptr(vtable_addr, idx), 189 ) 190 191 sb_addr = self.target().ResolveLoadAddress(vtable_entry_func_ptr) 192 sym_ctx = sb_addr.GetSymbolContext(lldb.eSymbolContextEverything) 193 194 # Make sure the type is the same as the function type 195 func_type = sym_ctx.GetFunction().GetType() 196 if func_type.IsValid(): 197 self.assertEqual(vtable_entry.GetType(), func_type.GetPointerType()) 198 199 # The summary should be the address description of the function pointer 200 summary = vtable_entry.GetSummary() 201 self.assertEqual(str(sb_addr), summary) 202