[NFC] Refactor symbol table parsing.
Symbol table parsing has evolved over the years and many plug-ins contained duplicate code in the ObjectFile::GetSymtab() that used to be pure virtual. With this change, the "Symbtab *ObjectFile::GetSymtab()" is no longer virtual and will end up calling a new "void ObjectFile::ParseSymtab(Symtab &symtab)" pure virtual function to actually do the parsing. This helps centralize the code for parsing the symbol table and allows the ObjectFile base class to do all of the common work, like taking the necessary locks and creating the symbol table object itself. Plug-ins now just need to parse when they are asked to parse as the ParseSymtab function will only get called once. This is a retry of the original patch https://reviews.llvm.org/D113965 which was reverted. There was a deadlock in the Manual DWARF indexing code during symbol preloading where the module was asked on the main thread to preload its symbols, and this would in turn cause the DWARF manual indexing to use a thread pool to index all of the compile units, and if there were relocations on the debug information sections, these threads could ask the ObjectFile to load section contents, which could cause a call to ObjectFileELF::RelocateSection() which would ask for the symbol table from the module and it would deadlock. We can't lock the module in ObjectFile::GetSymtab(), so the solution I am using is to use a llvm::once_flag to create the symbol table object once and then lock the Symtab object. Since all APIs on the symbol table use this lock, this will prevent anyone from using the symbol table before it is parsed and finalized and will avoid the deadlock I mentioned. ObjectFileELF::GetSymtab() was never locking the module lock before and would put off creating the symbol table until somewhere inside ObjectFileELF::GetSymtab(). Now we create it one time inside of the ObjectFile::GetSymtab() and immediately lock it which should be safe enough. This avoids the deadlocks and still provides safety. Differential Revision: https://reviews.llvm.org/D114288
This commit is contained in:
@@ -2687,155 +2687,131 @@ unsigned ObjectFileELF::RelocateDebugSections(const ELFSectionHeader *rel_hdr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
Symtab *ObjectFileELF::GetSymtab() {
|
||||
void ObjectFileELF::ParseSymtab(Symtab &lldb_symtab) {
|
||||
ModuleSP module_sp(GetModule());
|
||||
if (!module_sp)
|
||||
return nullptr;
|
||||
return;
|
||||
|
||||
Progress progress(
|
||||
llvm::formatv("Parsing symbol table for {0}",
|
||||
m_file.GetFilename().AsCString("<Unknown>")));
|
||||
ElapsedTime elapsed(module_sp->GetSymtabParseTime());
|
||||
|
||||
// We always want to use the main object file so we (hopefully) only have one
|
||||
// cached copy of our symtab, dynamic sections, etc.
|
||||
ObjectFile *module_obj_file = module_sp->GetObjectFile();
|
||||
if (module_obj_file && module_obj_file != this)
|
||||
return module_obj_file->GetSymtab();
|
||||
return module_obj_file->ParseSymtab(lldb_symtab);
|
||||
|
||||
if (m_symtab_up == nullptr) {
|
||||
Progress progress(
|
||||
llvm::formatv("Parsing symbol table for {0}",
|
||||
m_file.GetFilename().AsCString("<Unknown>")));
|
||||
ElapsedTime elapsed(module_sp->GetSymtabParseTime());
|
||||
SectionList *section_list = module_sp->GetSectionList();
|
||||
if (!section_list)
|
||||
return nullptr;
|
||||
SectionList *section_list = module_sp->GetSectionList();
|
||||
if (!section_list)
|
||||
return;
|
||||
|
||||
uint64_t symbol_id = 0;
|
||||
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
|
||||
uint64_t symbol_id = 0;
|
||||
|
||||
// Sharable objects and dynamic executables usually have 2 distinct symbol
|
||||
// tables, one named ".symtab", and the other ".dynsym". The dynsym is a
|
||||
// smaller version of the symtab that only contains global symbols. The
|
||||
// information found in the dynsym is therefore also found in the symtab,
|
||||
// while the reverse is not necessarily true.
|
||||
Section *symtab =
|
||||
section_list->FindSectionByType(eSectionTypeELFSymbolTable, true).get();
|
||||
if (symtab) {
|
||||
m_symtab_up = std::make_unique<Symtab>(symtab->GetObjectFile());
|
||||
symbol_id += ParseSymbolTable(m_symtab_up.get(), symbol_id, symtab);
|
||||
}
|
||||
// Sharable objects and dynamic executables usually have 2 distinct symbol
|
||||
// tables, one named ".symtab", and the other ".dynsym". The dynsym is a
|
||||
// smaller version of the symtab that only contains global symbols. The
|
||||
// information found in the dynsym is therefore also found in the symtab,
|
||||
// while the reverse is not necessarily true.
|
||||
Section *symtab =
|
||||
section_list->FindSectionByType(eSectionTypeELFSymbolTable, true).get();
|
||||
if (symtab)
|
||||
symbol_id += ParseSymbolTable(&lldb_symtab, symbol_id, symtab);
|
||||
|
||||
// The symtab section is non-allocable and can be stripped, while the
|
||||
// .dynsym section which should always be always be there. To support the
|
||||
// minidebuginfo case we parse .dynsym when there's a .gnu_debuginfo
|
||||
// section, nomatter if .symtab was already parsed or not. This is because
|
||||
// minidebuginfo normally removes the .symtab symbols which have their
|
||||
// matching .dynsym counterparts.
|
||||
if (!symtab ||
|
||||
GetSectionList()->FindSectionByName(ConstString(".gnu_debugdata"))) {
|
||||
Section *dynsym =
|
||||
section_list->FindSectionByType(eSectionTypeELFDynamicSymbols, true)
|
||||
.get();
|
||||
if (dynsym) {
|
||||
if (!m_symtab_up)
|
||||
m_symtab_up = std::make_unique<Symtab>(dynsym->GetObjectFile());
|
||||
symbol_id += ParseSymbolTable(m_symtab_up.get(), symbol_id, dynsym);
|
||||
}
|
||||
}
|
||||
|
||||
// DT_JMPREL
|
||||
// If present, this entry's d_ptr member holds the address of
|
||||
// relocation
|
||||
// entries associated solely with the procedure linkage table.
|
||||
// Separating
|
||||
// these relocation entries lets the dynamic linker ignore them during
|
||||
// process initialization, if lazy binding is enabled. If this entry is
|
||||
// present, the related entries of types DT_PLTRELSZ and DT_PLTREL must
|
||||
// also be present.
|
||||
const ELFDynamic *symbol = FindDynamicSymbol(DT_JMPREL);
|
||||
if (symbol) {
|
||||
// Synthesize trampoline symbols to help navigate the PLT.
|
||||
addr_t addr = symbol->d_ptr;
|
||||
Section *reloc_section =
|
||||
section_list->FindSectionContainingFileAddress(addr).get();
|
||||
if (reloc_section) {
|
||||
user_id_t reloc_id = reloc_section->GetID();
|
||||
const ELFSectionHeaderInfo *reloc_header =
|
||||
GetSectionHeaderByIndex(reloc_id);
|
||||
if (reloc_header) {
|
||||
if (m_symtab_up == nullptr)
|
||||
m_symtab_up =
|
||||
std::make_unique<Symtab>(reloc_section->GetObjectFile());
|
||||
|
||||
ParseTrampolineSymbols(m_symtab_up.get(), symbol_id, reloc_header,
|
||||
reloc_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DWARFCallFrameInfo *eh_frame =
|
||||
GetModule()->GetUnwindTable().GetEHFrameInfo()) {
|
||||
if (m_symtab_up == nullptr)
|
||||
m_symtab_up = std::make_unique<Symtab>(this);
|
||||
ParseUnwindSymbols(m_symtab_up.get(), eh_frame);
|
||||
}
|
||||
|
||||
// If we still don't have any symtab then create an empty instance to avoid
|
||||
// do the section lookup next time.
|
||||
if (m_symtab_up == nullptr)
|
||||
m_symtab_up = std::make_unique<Symtab>(this);
|
||||
|
||||
// In the event that there's no symbol entry for the entry point we'll
|
||||
// artificially create one. We delegate to the symtab object the figuring
|
||||
// out of the proper size, this will usually make it span til the next
|
||||
// symbol it finds in the section. This means that if there are missing
|
||||
// symbols the entry point might span beyond its function definition.
|
||||
// We're fine with this as it doesn't make it worse than not having a
|
||||
// symbol entry at all.
|
||||
if (CalculateType() == eTypeExecutable) {
|
||||
ArchSpec arch = GetArchitecture();
|
||||
auto entry_point_addr = GetEntryPointAddress();
|
||||
bool is_valid_entry_point =
|
||||
entry_point_addr.IsValid() && entry_point_addr.IsSectionOffset();
|
||||
addr_t entry_point_file_addr = entry_point_addr.GetFileAddress();
|
||||
if (is_valid_entry_point && !m_symtab_up->FindSymbolContainingFileAddress(
|
||||
entry_point_file_addr)) {
|
||||
uint64_t symbol_id = m_symtab_up->GetNumSymbols();
|
||||
// Don't set the name for any synthetic symbols, the Symbol
|
||||
// object will generate one if needed when the name is accessed
|
||||
// via accessors.
|
||||
SectionSP section_sp = entry_point_addr.GetSection();
|
||||
Symbol symbol(
|
||||
/*symID=*/symbol_id,
|
||||
/*name=*/llvm::StringRef(), // Name will be auto generated.
|
||||
/*type=*/eSymbolTypeCode,
|
||||
/*external=*/true,
|
||||
/*is_debug=*/false,
|
||||
/*is_trampoline=*/false,
|
||||
/*is_artificial=*/true,
|
||||
/*section_sp=*/section_sp,
|
||||
/*offset=*/0,
|
||||
/*size=*/0, // FDE can span multiple symbols so don't use its size.
|
||||
/*size_is_valid=*/false,
|
||||
/*contains_linker_annotations=*/false,
|
||||
/*flags=*/0);
|
||||
// When the entry point is arm thumb we need to explicitly set its
|
||||
// class address to reflect that. This is important because expression
|
||||
// evaluation relies on correctly setting a breakpoint at this
|
||||
// address.
|
||||
if (arch.GetMachine() == llvm::Triple::arm &&
|
||||
(entry_point_file_addr & 1)) {
|
||||
symbol.GetAddressRef().SetOffset(entry_point_addr.GetOffset() ^ 1);
|
||||
m_address_class_map[entry_point_file_addr ^ 1] =
|
||||
AddressClass::eCodeAlternateISA;
|
||||
} else {
|
||||
m_address_class_map[entry_point_file_addr] = AddressClass::eCode;
|
||||
}
|
||||
m_symtab_up->AddSymbol(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
m_symtab_up->CalculateSymbolSizes();
|
||||
// The symtab section is non-allocable and can be stripped, while the
|
||||
// .dynsym section which should always be always be there. To support the
|
||||
// minidebuginfo case we parse .dynsym when there's a .gnu_debuginfo
|
||||
// section, nomatter if .symtab was already parsed or not. This is because
|
||||
// minidebuginfo normally removes the .symtab symbols which have their
|
||||
// matching .dynsym counterparts.
|
||||
if (!symtab ||
|
||||
GetSectionList()->FindSectionByName(ConstString(".gnu_debugdata"))) {
|
||||
Section *dynsym =
|
||||
section_list->FindSectionByType(eSectionTypeELFDynamicSymbols, true)
|
||||
.get();
|
||||
if (dynsym)
|
||||
symbol_id += ParseSymbolTable(&lldb_symtab, symbol_id, dynsym);
|
||||
}
|
||||
|
||||
return m_symtab_up.get();
|
||||
// DT_JMPREL
|
||||
// If present, this entry's d_ptr member holds the address of
|
||||
// relocation
|
||||
// entries associated solely with the procedure linkage table.
|
||||
// Separating
|
||||
// these relocation entries lets the dynamic linker ignore them during
|
||||
// process initialization, if lazy binding is enabled. If this entry is
|
||||
// present, the related entries of types DT_PLTRELSZ and DT_PLTREL must
|
||||
// also be present.
|
||||
const ELFDynamic *symbol = FindDynamicSymbol(DT_JMPREL);
|
||||
if (symbol) {
|
||||
// Synthesize trampoline symbols to help navigate the PLT.
|
||||
addr_t addr = symbol->d_ptr;
|
||||
Section *reloc_section =
|
||||
section_list->FindSectionContainingFileAddress(addr).get();
|
||||
if (reloc_section) {
|
||||
user_id_t reloc_id = reloc_section->GetID();
|
||||
const ELFSectionHeaderInfo *reloc_header =
|
||||
GetSectionHeaderByIndex(reloc_id);
|
||||
if (reloc_header)
|
||||
ParseTrampolineSymbols(&lldb_symtab, symbol_id, reloc_header, reloc_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (DWARFCallFrameInfo *eh_frame =
|
||||
GetModule()->GetUnwindTable().GetEHFrameInfo()) {
|
||||
ParseUnwindSymbols(&lldb_symtab, eh_frame);
|
||||
}
|
||||
|
||||
// In the event that there's no symbol entry for the entry point we'll
|
||||
// artificially create one. We delegate to the symtab object the figuring
|
||||
// out of the proper size, this will usually make it span til the next
|
||||
// symbol it finds in the section. This means that if there are missing
|
||||
// symbols the entry point might span beyond its function definition.
|
||||
// We're fine with this as it doesn't make it worse than not having a
|
||||
// symbol entry at all.
|
||||
if (CalculateType() == eTypeExecutable) {
|
||||
ArchSpec arch = GetArchitecture();
|
||||
auto entry_point_addr = GetEntryPointAddress();
|
||||
bool is_valid_entry_point =
|
||||
entry_point_addr.IsValid() && entry_point_addr.IsSectionOffset();
|
||||
addr_t entry_point_file_addr = entry_point_addr.GetFileAddress();
|
||||
if (is_valid_entry_point && !lldb_symtab.FindSymbolContainingFileAddress(
|
||||
entry_point_file_addr)) {
|
||||
uint64_t symbol_id = lldb_symtab.GetNumSymbols();
|
||||
// Don't set the name for any synthetic symbols, the Symbol
|
||||
// object will generate one if needed when the name is accessed
|
||||
// via accessors.
|
||||
SectionSP section_sp = entry_point_addr.GetSection();
|
||||
Symbol symbol(
|
||||
/*symID=*/symbol_id,
|
||||
/*name=*/llvm::StringRef(), // Name will be auto generated.
|
||||
/*type=*/eSymbolTypeCode,
|
||||
/*external=*/true,
|
||||
/*is_debug=*/false,
|
||||
/*is_trampoline=*/false,
|
||||
/*is_artificial=*/true,
|
||||
/*section_sp=*/section_sp,
|
||||
/*offset=*/0,
|
||||
/*size=*/0, // FDE can span multiple symbols so don't use its size.
|
||||
/*size_is_valid=*/false,
|
||||
/*contains_linker_annotations=*/false,
|
||||
/*flags=*/0);
|
||||
// When the entry point is arm thumb we need to explicitly set its
|
||||
// class address to reflect that. This is important because expression
|
||||
// evaluation relies on correctly setting a breakpoint at this
|
||||
// address.
|
||||
if (arch.GetMachine() == llvm::Triple::arm &&
|
||||
(entry_point_file_addr & 1)) {
|
||||
symbol.GetAddressRef().SetOffset(entry_point_addr.GetOffset() ^ 1);
|
||||
m_address_class_map[entry_point_file_addr ^ 1] =
|
||||
AddressClass::eCodeAlternateISA;
|
||||
} else {
|
||||
m_address_class_map[entry_point_file_addr] = AddressClass::eCode;
|
||||
}
|
||||
lldb_symtab.AddSymbol(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectFileELF::RelocateSection(lldb_private::Section *section)
|
||||
|
||||
Reference in New Issue
Block a user