202 lines · python
1"""2Make sure the getting a variable path works and doesn't crash.3"""4 5import lldb6import lldbsuite.test.lldbutil as lldbutil7from lldbsuite.test.decorators import *8from lldbsuite.test.lldbtest import *9 10 11class TestVTableValue(TestBase):12 # If your test case doesn't stress debug info, then13 # set this to true. That way it won't be run once for14 # each debug info format.15 NO_DEBUG_INFO_TESTCASE = True16 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 vtable31 # for the shape class.32 self.assertEqual(vtable.GetNumChildren(), 4)33 34 # Verify vtable address35 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 vtable48 # for the shape class.49 self.assertEqual(vtable.GetNumChildren(), 4)50 51 # Verify vtable address52 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 vtable66 # with the extra virtual function added by the Rectangle class67 self.assertEqual(vtable.GetNumChildren(), 5)68 69 # Verify vtable address70 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 vtable132 # for the shape class.133 self.assertEqual(vtable.GetNumChildren(), 4)134 135 # Overwrite the first entry in the vtable and make sure we can still136 # see the bogus value which should have no summary137 vtable_addr = vtable.GetValueAsUnsigned()138 139 is_64bit = self.process().GetAddressByteSize() == 8140 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_error163 )164 self.assertTrue(read_from_memory_error.Success())165 return vtable_addr166 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_error172 )173 self.assertTrue(read_func_ptr_error.Success())174 return func_ptr175 176 def verify_vtable_entry(177 self, vtable_entry: lldb.SBValue, vtable_addr: int, idx: int178 ):179 """Verify the vtable entry looks something like:180 181 (double ()) [0] = 0x0000000100003a10 a.out`Rectangle::Area() at main.cpp:14182 183 """184 # Check function ptr185 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 type195 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 pointer200 summary = vtable_entry.GetSummary()201 self.assertEqual(str(sb_addr), summary)202