Skip to content

Commit 663db32

Browse files
committed
[lldb] Fix dynamic type resolutions for core files
We're reading from the object's vtable to determine the pointer to the full object. The vtable is normally in the "rodata" section of the executable, which is often not included in the core file because it's not supposed to change and the debugger can extrapolate its contents from the executable file. We weren't doing that. This patch changes the read operation to use the target class (which falls back onto the executable module as expected) and adds the missing ReadSignedIntegerFromMemory API. The fix is tested by creating a core (minidump) file which deliberately omits the vtable pointer.
1 parent 3035bcc commit 663db32

File tree

4 files changed

+67
-1
lines changed

4 files changed

+67
-1
lines changed

lldb/include/lldb/Target/Target.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,11 @@ class Target : public std::enable_shared_from_this<Target>,
11581158
Status &error,
11591159
bool force_live_memory = false);
11601160

1161+
int64_t ReadSignedIntegerFromMemory(const Address &addr,
1162+
size_t integer_byte_size,
1163+
int64_t fail_value, Status &error,
1164+
bool force_live_memory = false);
1165+
11611166
uint64_t ReadUnsignedIntegerFromMemory(const Address &addr,
11621167
size_t integer_byte_size,
11631168
uint64_t fail_value, Status &error,

lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress(
350350
if (offset_to_top_location >= vtable_load_addr)
351351
return false;
352352
Status error;
353-
const int64_t offset_to_top = m_process->ReadSignedIntegerFromMemory(
353+
const int64_t offset_to_top = target.ReadSignedIntegerFromMemory(
354354
offset_to_top_location, addr_byte_size, INT64_MIN, error);
355355

356356
if (offset_to_top == INT64_MIN)

lldb/source/Target/Target.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2270,6 +2270,17 @@ size_t Target::ReadScalarIntegerFromMemory(const Address &addr, uint32_t byte_si
22702270
return 0;
22712271
}
22722272

2273+
int64_t Target::ReadSignedIntegerFromMemory(const Address &addr,
2274+
size_t integer_byte_size,
2275+
int64_t fail_value, Status &error,
2276+
bool force_live_memory) {
2277+
Scalar scalar;
2278+
if (ReadScalarIntegerFromMemory(addr, integer_byte_size, false, scalar, error,
2279+
force_live_memory))
2280+
return scalar.SLongLong(fail_value);
2281+
return fail_value;
2282+
}
2283+
22732284
uint64_t Target::ReadUnsignedIntegerFromMemory(const Address &addr,
22742285
size_t integer_byte_size,
22752286
uint64_t fail_value, Status &error,

lldb/test/API/lang/cpp/dynamic-value/TestDynamicValue.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,53 @@ def test_from_forward_decl(self):
279279
"frame var -d run-target --ptr-depth=1 --show-types a",
280280
substrs=["(B *) a", "m_b_value = 10"],
281281
)
282+
283+
@no_debug_info_test
284+
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663")
285+
def test_from_core_file(self):
286+
"""Test fetching C++ dynamic values from core files. Specifically, test
287+
that we can determine the dynamic type of the value if the core file
288+
does not contain the type vtable."""
289+
self.build()
290+
lldbutil.run_to_name_breakpoint(self, "take_A")
291+
292+
# Get the address of our object and its vtable
293+
a = self.frame().FindVariable("a")
294+
self.assertSuccess(a.GetError())
295+
vtable = a.GetVTable()
296+
self.assertSuccess(vtable.GetError())
297+
a = a.GetValueAsAddress()
298+
vtable = vtable.GetValueAsAddress()
299+
300+
# Create a core file which will only contain the memory region
301+
# containing `a`. The object is on the stack, so this will automatically
302+
# include the stack of the main thread.
303+
core = self.getBuildArtifact("a.dmp")
304+
options = lldb.SBSaveCoreOptions()
305+
options.SetPluginName("minidump")
306+
options.SetStyle(lldb.eSaveCoreCustomOnly)
307+
options.SetOutputFile(lldb.SBFileSpec(core))
308+
region = lldb.SBMemoryRegionInfo()
309+
self.assertSuccess(self.process().GetMemoryRegionInfo(a, region))
310+
self.assertSuccess(options.AddMemoryRegionToSave(region))
311+
312+
# Save the core file and load it.
313+
self.assertSuccess(self.process().SaveCore(options))
314+
self.process().Kill()
315+
error = lldb.SBError()
316+
self.target().LoadCore(core, error)
317+
self.assertSuccess(error)
318+
319+
# Sanity check -- the process should be able to read the object but not
320+
# its vtable..
321+
self.process().ReadPointerFromMemory(a, error)
322+
self.assertSuccess(error)
323+
self.process().ReadPointerFromMemory(vtable, error)
324+
self.assertTrue(error.Fail())
325+
326+
# .. but we should still be able to see the dynamic type by reading the
327+
# vtable from the executable file.
328+
self.expect(
329+
"frame var -d run-target --ptr-depth=1 --show-types a",
330+
substrs=["(B *) a", "m_b_value = 10"],
331+
)

0 commit comments

Comments
 (0)