Skip to content

Commit 21501d1

Browse files
authored
[lldb] Fix dynamic type resolutions for core files (#138698)
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 0db0405 commit 21501d1

File tree

4 files changed

+68
-1
lines changed

4 files changed

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

0 commit comments

Comments
 (0)