Skip to content

Commit e9c8f75

Browse files
authored
[LLDB][Minidump] Have Minidumps save off and properly read TLS data (#109477)
This patch adds the support to `Process.cpp` to automatically save off TLS sections, either via loading the memory region for the module, or via reading `fs_base` via generic register. Then when Minidumps are loaded, we now specify we want the dynamic loader to be the `POSIXDYLD` so we can leverage the same TLS accessor code as `ProcessELFCore`. Being able to access TLS Data is an important step for LLDB generated minidumps to have feature parity with ELF Core dumps.
1 parent 2ff4c25 commit e9c8f75

File tree

10 files changed

+248
-16
lines changed

10 files changed

+248
-16
lines changed

lldb/include/lldb/Target/DynamicLoader.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "lldb/Core/Address.h"
1313
#include "lldb/Core/PluginInterface.h"
14+
#include "lldb/Target/CoreFileMemoryRanges.h"
1415
#include "lldb/Utility/FileSpec.h"
1516
#include "lldb/Utility/Status.h"
1617
#include "lldb/Utility/UUID.h"
@@ -337,6 +338,17 @@ class DynamicLoader : public PluginInterface {
337338
return std::nullopt;
338339
}
339340

341+
/// Returns a list of memory ranges that should be saved in the core file,
342+
/// specific for this dynamic loader.
343+
///
344+
/// For example, an implementation of this function can save the thread
345+
/// local data of a given thread.
346+
virtual void CalculateDynamicSaveCoreRanges(
347+
lldb_private::Process &process,
348+
std::vector<lldb_private::MemoryRegionInfo> &ranges,
349+
llvm::function_ref<bool(const lldb_private::Thread &)>
350+
save_thread_predicate) {};
351+
340352
protected:
341353
// Utility methods for derived classes
342354

lldb/source/Core/DynamicLoader.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,11 @@ ModuleSP DynamicLoader::GetTargetExecutable() {
8383
ModuleSpec module_spec(executable->GetFileSpec(),
8484
executable->GetArchitecture());
8585
auto module_sp = std::make_shared<Module>(module_spec);
86-
86+
// If we're a coredump and we already have a main executable, we don't
87+
// need to reload the module list that target already has
88+
if (!m_process->IsLiveDebugSession()) {
89+
return executable;
90+
}
8791
// Check if the executable has changed and set it to the target
8892
// executable if they differ.
8993
if (module_sp && module_sp->GetUUID().IsValid() &&
@@ -369,4 +373,3 @@ void DynamicLoader::LoadOperatingSystemPlugin(bool flush)
369373
if (m_process)
370374
m_process->LoadOperatingSystemPlugin(flush);
371375
}
372-

lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "lldb/Symbol/ObjectFile.h"
1919
#include "lldb/Target/MemoryRegionInfo.h"
2020
#include "lldb/Target/Platform.h"
21+
#include "lldb/Target/RegisterContext.h"
2122
#include "lldb/Target/Target.h"
2223
#include "lldb/Target/Thread.h"
2324
#include "lldb/Target/ThreadPlanRunToAddress.h"
@@ -866,3 +867,82 @@ bool DynamicLoaderPOSIXDYLD::AlwaysRelyOnEHUnwindInfo(
866867
bool DynamicLoaderPOSIXDYLD::IsCoreFile() const {
867868
return !m_process->IsLiveDebugSession();
868869
}
870+
871+
// For our ELF/POSIX builds save off the fs_base/gs_base regions
872+
static void AddThreadLocalMemoryRegions(Process &process, ThreadSP &thread_sp,
873+
std::vector<MemoryRegionInfo> &ranges) {
874+
lldb::RegisterContextSP reg_ctx = thread_sp->GetRegisterContext();
875+
if (!reg_ctx)
876+
return;
877+
878+
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
879+
lldb::RegisterKind::eRegisterKindGeneric, LLDB_REGNUM_GENERIC_TP);
880+
if (!reg_info)
881+
return;
882+
883+
lldb_private::RegisterValue thread_local_register_value;
884+
bool success = reg_ctx->ReadRegister(reg_info, thread_local_register_value);
885+
if (!success)
886+
return;
887+
888+
const uint64_t fail_value = UINT64_MAX;
889+
bool readSuccess = false;
890+
const lldb::addr_t reg_value_addr =
891+
thread_local_register_value.GetAsUInt64(fail_value, &readSuccess);
892+
if (!readSuccess || reg_value_addr == fail_value)
893+
return;
894+
895+
MemoryRegionInfo thread_local_region;
896+
Status err = process.GetMemoryRegionInfo(reg_value_addr, thread_local_region);
897+
if (err.Fail())
898+
return;
899+
900+
ranges.push_back(thread_local_region);
901+
}
902+
903+
// Save off the link map for core files.
904+
static void AddLinkMapSections(Process &process,
905+
std::vector<MemoryRegionInfo> &ranges) {
906+
ModuleList &module_list = process.GetTarget().GetImages();
907+
Target *target = &process.GetTarget();
908+
for (size_t idx = 0; idx < module_list.GetSize(); idx++) {
909+
ModuleSP module_sp = module_list.GetModuleAtIndex(idx);
910+
if (!module_sp)
911+
continue;
912+
913+
ObjectFile *obj = module_sp->GetObjectFile();
914+
if (!obj)
915+
continue;
916+
Address addr = obj->GetImageInfoAddress(target);
917+
addr_t load_addr = addr.GetLoadAddress(target);
918+
if (load_addr == LLDB_INVALID_ADDRESS)
919+
continue;
920+
921+
MemoryRegionInfo link_map_section;
922+
Status err = process.GetMemoryRegionInfo(load_addr, link_map_section);
923+
if (err.Fail())
924+
continue;
925+
926+
ranges.push_back(link_map_section);
927+
}
928+
}
929+
930+
void DynamicLoaderPOSIXDYLD::CalculateDynamicSaveCoreRanges(
931+
lldb_private::Process &process,
932+
std::vector<lldb_private::MemoryRegionInfo> &ranges,
933+
llvm::function_ref<bool(const lldb_private::Thread &)>
934+
save_thread_predicate) {
935+
ThreadList &thread_list = process.GetThreadList();
936+
for (size_t idx = 0; idx < thread_list.GetSize(); idx++) {
937+
ThreadSP thread_sp = thread_list.GetThreadAtIndex(idx);
938+
if (!thread_sp)
939+
continue;
940+
941+
if (!save_thread_predicate(*thread_sp))
942+
continue;
943+
944+
AddThreadLocalMemoryRegions(process, thread_sp, ranges);
945+
}
946+
947+
AddLinkMapSections(process, ranges);
948+
}

lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ class DynamicLoaderPOSIXDYLD : public lldb_private::DynamicLoader {
6060
lldb::addr_t base_addr,
6161
bool base_addr_is_offset) override;
6262

63+
void CalculateDynamicSaveCoreRanges(
64+
lldb_private::Process &process,
65+
std::vector<lldb_private::MemoryRegionInfo> &ranges,
66+
llvm::function_ref<bool(const lldb_private::Thread &)>
67+
save_thread_predicate) override;
68+
6369
protected:
6470
/// Runtime linker rendezvous structure.
6571
DYLDRendezvous m_rendezvous;

lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121
#include "lldb/Interpreter/CommandReturnObject.h"
2222
#include "lldb/Interpreter/OptionArgParser.h"
2323
#include "lldb/Interpreter/OptionGroupBoolean.h"
24+
#include "lldb/Target/DynamicLoader.h"
2425
#include "lldb/Target/JITLoaderList.h"
2526
#include "lldb/Target/MemoryRegionInfo.h"
2627
#include "lldb/Target/SectionLoadList.h"
2728
#include "lldb/Target/Target.h"
2829
#include "lldb/Target/UnixSignals.h"
30+
#include "lldb/Utility/DataBufferHeap.h"
2931
#include "lldb/Utility/LLDBAssert.h"
3032
#include "lldb/Utility/LLDBLog.h"
3133
#include "lldb/Utility/Log.h"
@@ -34,6 +36,7 @@
3436
#include "llvm/Support/MemoryBuffer.h"
3537
#include "llvm/Support/Threading.h"
3638

39+
#include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h"
3740
#include "Plugins/ObjectFile/Placeholder/ObjectFilePlaceholder.h"
3841
#include "Plugins/Process/Utility/StopInfoMachException.h"
3942

@@ -333,6 +336,16 @@ ArchSpec ProcessMinidump::GetArchitecture() {
333336
return ArchSpec(triple);
334337
}
335338

339+
DataExtractor ProcessMinidump::GetAuxvData() {
340+
std::optional<llvm::ArrayRef<uint8_t>> auxv =
341+
m_minidump_parser->GetStream(StreamType::LinuxAuxv);
342+
if (!auxv)
343+
return DataExtractor();
344+
345+
return DataExtractor(auxv->data(), auxv->size(), GetByteOrder(),
346+
GetAddressByteSize(), GetAddressByteSize());
347+
}
348+
336349
void ProcessMinidump::BuildMemoryRegions() {
337350
if (m_memory_regions)
338351
return;
@@ -534,7 +547,12 @@ void ProcessMinidump::ReadModuleList() {
534547

535548
module_sp = Module::CreateModuleFromObjectFile<ObjectFilePlaceholder>(
536549
module_spec, load_addr, load_size);
537-
GetTarget().GetImages().Append(module_sp, true /* notify */);
550+
// If we haven't loaded a main executable yet, set the first module to be
551+
// main executable
552+
if (!GetTarget().GetExecutableModule())
553+
GetTarget().SetExecutableModule(module_sp);
554+
else
555+
GetTarget().GetImages().Append(module_sp, true /* notify */);
538556
}
539557

540558
bool load_addr_changed = false;

lldb/source/Plugins/Process/minidump/ProcessMinidump.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,11 @@ class ProcessMinidump : public PostMortemProcess {
5353

5454
Status DoLoadCore() override;
5555

56-
DynamicLoader *GetDynamicLoader() override { return nullptr; }
56+
// Returns AUXV structure found in the core file
57+
lldb_private::DataExtractor GetAuxvData() override;
5758

5859
llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
5960

60-
SystemRuntime *GetSystemRuntime() override { return nullptr; }
61-
6261
Status DoDestroy() override;
6362

6463
void RefreshStateAfterStop() override;

lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,17 @@ static void writeRegister(const void *reg_src, uint8_t *context,
4444
memcpy(reg_dest.data(), reg_src, reg_dest.size());
4545
}
4646

47+
// TODO: Fix the registers in this file!
48+
// writeRegister checks x86_64 registers without base registers. This causes
49+
// an overlap in the register enum values. So we were truncating fs_base.
50+
// We should standardize to the x86_64_with_base registers.
51+
static void writeBaseRegister(const void *reg_src, uint8_t *context,
52+
const RegisterInfo &reg) {
53+
auto bytes = reg.mutable_data(context);
54+
llvm::MutableArrayRef<uint8_t> reg_dest = bytes.take_front(8);
55+
memcpy(reg_dest.data(), reg_src, reg_dest.size());
56+
}
57+
4758
lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContext_x86_64(
4859
llvm::ArrayRef<uint8_t> source_data,
4960
RegisterInfoInterface *target_reg_interface) {
@@ -105,11 +116,12 @@ lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContext_x86_64(
105116
writeRegister(&context->r15, result_base, reg_info[lldb_r15_x86_64]);
106117
}
107118

119+
// See comment on base regsiter
108120
if ((context_flags & LLDBSpecificFlag) == LLDBSpecificFlag) {
109-
writeRegister(&context->fs_base, result_base,
110-
reg_info[x86_64_with_base::lldb_fs_base]);
111-
writeRegister(&context->gs_base, result_base,
112-
reg_info[x86_64_with_base::lldb_gs_base]);
121+
writeBaseRegister(&context->fs_base, result_base,
122+
reg_info[x86_64_with_base::lldb_fs_base]);
123+
writeBaseRegister(&context->gs_base, result_base,
124+
reg_info[x86_64_with_base::lldb_gs_base]);
113125
}
114126

115127
// TODO parse the floating point registers

lldb/source/Target/Process.cpp

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6528,6 +6528,29 @@ static void AddRegion(const MemoryRegionInfo &region, bool try_dirty_pages,
65286528
CreateCoreFileMemoryRange(region));
65296529
}
65306530

6531+
static void SaveDynamicLoaderSections(Process &process,
6532+
const SaveCoreOptions &options,
6533+
CoreFileMemoryRanges &ranges,
6534+
std::set<addr_t> &stack_ends) {
6535+
DynamicLoader *dyld = process.GetDynamicLoader();
6536+
if (!dyld)
6537+
return;
6538+
6539+
std::vector<MemoryRegionInfo> dynamic_loader_mem_regions;
6540+
std::function<bool(const lldb_private::Thread &)> save_thread_predicate =
6541+
[&](const lldb_private::Thread &t) -> bool {
6542+
return options.ShouldThreadBeSaved(t.GetID());
6543+
};
6544+
dyld->CalculateDynamicSaveCoreRanges(process, dynamic_loader_mem_regions,
6545+
save_thread_predicate);
6546+
for (const auto &region : dynamic_loader_mem_regions) {
6547+
// The Dynamic Loader can give us regions that could include a truncated
6548+
// stack
6549+
if (stack_ends.count(region.GetRange().GetRangeEnd()) == 0)
6550+
AddRegion(region, true, ranges);
6551+
}
6552+
}
6553+
65316554
static void SaveOffRegionsWithStackPointers(Process &process,
65326555
const SaveCoreOptions &core_options,
65336556
const MemoryRegionInfos &regions,
@@ -6559,11 +6582,13 @@ static void SaveOffRegionsWithStackPointers(Process &process,
65596582
// off in other calls
65606583
sp_region.GetRange().SetRangeBase(stack_head);
65616584
sp_region.GetRange().SetByteSize(stack_size);
6562-
stack_ends.insert(sp_region.GetRange().GetRangeEnd());
6585+
const addr_t range_end = sp_region.GetRange().GetRangeEnd();
6586+
stack_ends.insert(range_end);
65636587
// This will return true if the threadlist the user specified is empty,
65646588
// or contains the thread id from thread_sp.
6565-
if (core_options.ShouldThreadBeSaved(thread_sp->GetID()))
6589+
if (core_options.ShouldThreadBeSaved(thread_sp->GetID())) {
65666590
AddRegion(sp_region, try_dirty_pages, ranges);
6591+
}
65676592
}
65686593
}
65696594
}
@@ -6672,9 +6697,14 @@ Status Process::CalculateCoreFileSaveRanges(const SaveCoreOptions &options,
66726697
std::set<addr_t> stack_ends;
66736698
// For fully custom set ups, we don't want to even look at threads if there
66746699
// are no threads specified.
6675-
if (core_style != lldb::eSaveCoreCustomOnly || options.HasSpecifiedThreads())
6700+
if (core_style != lldb::eSaveCoreCustomOnly ||
6701+
options.HasSpecifiedThreads()) {
66766702
SaveOffRegionsWithStackPointers(*this, options, regions, ranges,
66776703
stack_ends);
6704+
// Save off the dynamic loader sections, so if we are on an architecture
6705+
// that supports Thread Locals, that we include those as well.
6706+
SaveDynamicLoaderSections(*this, options, ranges, stack_ends);
6707+
}
66786708

66796709
switch (core_style) {
66806710
case eSaveCoreUnspecified:

0 commit comments

Comments
 (0)