
Currently a Module has a std::optional<UnwindTable> which is created when the UnwindTable is requested from outside the Module. The idea is to delay its creation until the Module has an ObjectFile initialized, which will have been done by the time we're doing an unwind. However, Module::GetUnwindTable wasn't doing any locking, so it was possible for two threads to ask for the UnwindTable for the first time, one would be created and returned while another thread would create one, destroy the first in the process of emplacing it. It was an uncommon crash, but it was possible. Grabbing the Module's mutex would be one way to address it, but when loading ELF binaries, we start creating the SymbolTable on one thread (ObjectFileELF) grabbing the Module's mutex, and then spin up worker threads to parse the individual DWARF compilation units, which then try to also get the UnwindTable and deadlock if they try to get the Module's mutex. This changes Module to have a concrete UnwindTable as an ivar, and when it adds an ObjectFile or SymbolFileVendor, it will call the Update method on it, which will re-evaluate which sections exist in the ObjectFile/SymbolFile. UnwindTable used to have an Initialize method which set all the sections, and an Update method which would set some of them if they weren't set. I unified these with the Initialize method taking a `force` option to re-initialize the section pointers even if they had been done already before. This is addressing a rare crash report we've received, and also a failure Adrian spotted on the -fsanitize=address CI bot last week, it's still uncommon with ASAN but it can happen with the standard testsuite. rdar://128876433
214 lines
6.7 KiB
C++
214 lines
6.7 KiB
C++
//===-- UnwindTable.cpp ---------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Symbol/UnwindTable.h"
|
|
|
|
#include <cstdio>
|
|
#include <optional>
|
|
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Core/Section.h"
|
|
#include "lldb/Symbol/ArmUnwindInfo.h"
|
|
#include "lldb/Symbol/CallFrameInfo.h"
|
|
#include "lldb/Symbol/CompactUnwindInfo.h"
|
|
#include "lldb/Symbol/DWARFCallFrameInfo.h"
|
|
#include "lldb/Symbol/FuncUnwinders.h"
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Symbol/SymbolContext.h"
|
|
#include "lldb/Symbol/SymbolVendor.h"
|
|
|
|
// There is one UnwindTable object per ObjectFile. It contains a list of Unwind
|
|
// objects -- one per function, populated lazily -- for the ObjectFile. Each
|
|
// Unwind object has multiple UnwindPlans for different scenarios.
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
UnwindTable::UnwindTable(Module &module)
|
|
: m_module(module), m_unwinds(), m_scanned_all_unwind_sources(false),
|
|
m_mutex(), m_object_file_unwind_up(), m_eh_frame_up(),
|
|
m_compact_unwind_up(), m_arm_unwind_up() {}
|
|
|
|
// We can't do some of this initialization when the ObjectFile is running its
|
|
// ctor; delay doing it until needed for something.
|
|
void UnwindTable::Initialize() {
|
|
if (m_scanned_all_unwind_sources)
|
|
return;
|
|
|
|
std::lock_guard<std::mutex> guard(m_mutex);
|
|
|
|
if (m_scanned_all_unwind_sources) // check again once we've acquired the lock
|
|
return;
|
|
|
|
ObjectFile *object_file = m_module.GetObjectFile();
|
|
if (!object_file)
|
|
return;
|
|
|
|
m_scanned_all_unwind_sources = true;
|
|
|
|
if (!m_object_file_unwind_up)
|
|
m_object_file_unwind_up = object_file->CreateCallFrameInfo();
|
|
|
|
SectionList *sl = m_module.GetSectionList();
|
|
if (!sl)
|
|
return;
|
|
|
|
SectionSP sect = sl->FindSectionByType(eSectionTypeEHFrame, true);
|
|
if (!m_eh_frame_up && sect)
|
|
m_eh_frame_up = std::make_unique<DWARFCallFrameInfo>(
|
|
*object_file, sect, DWARFCallFrameInfo::EH);
|
|
|
|
sect = sl->FindSectionByType(eSectionTypeDWARFDebugFrame, true);
|
|
if (!m_debug_frame_up && sect)
|
|
m_debug_frame_up = std::make_unique<DWARFCallFrameInfo>(
|
|
*object_file, sect, DWARFCallFrameInfo::DWARF);
|
|
|
|
sect = sl->FindSectionByType(eSectionTypeCompactUnwind, true);
|
|
if (!m_compact_unwind_up && sect)
|
|
m_compact_unwind_up =
|
|
std::make_unique<CompactUnwindInfo>(*object_file, sect);
|
|
|
|
sect = sl->FindSectionByType(eSectionTypeARMexidx, true);
|
|
if (!m_arm_unwind_up && sect) {
|
|
SectionSP sect_extab = sl->FindSectionByType(eSectionTypeARMextab, true);
|
|
if (sect_extab.get()) {
|
|
m_arm_unwind_up =
|
|
std::make_unique<ArmUnwindInfo>(*object_file, sect, sect_extab);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UnwindTable::ModuleWasUpdated() {
|
|
std::lock_guard<std::mutex> guard(m_mutex);
|
|
m_scanned_all_unwind_sources = false;
|
|
}
|
|
|
|
UnwindTable::~UnwindTable() = default;
|
|
|
|
std::optional<AddressRange>
|
|
UnwindTable::GetAddressRange(const Address &addr, const SymbolContext &sc) {
|
|
AddressRange range;
|
|
|
|
// First check the unwind info from the object file plugin
|
|
if (m_object_file_unwind_up &&
|
|
m_object_file_unwind_up->GetAddressRange(addr, range))
|
|
return range;
|
|
|
|
// Check the symbol context
|
|
if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
|
|
false, range) &&
|
|
range.GetBaseAddress().IsValid())
|
|
return range;
|
|
|
|
// Does the eh_frame unwind info has a function bounds for this addr?
|
|
if (m_eh_frame_up && m_eh_frame_up->GetAddressRange(addr, range))
|
|
return range;
|
|
|
|
// Try debug_frame as well
|
|
if (m_debug_frame_up && m_debug_frame_up->GetAddressRange(addr, range))
|
|
return range;
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
FuncUnwindersSP
|
|
UnwindTable::GetFuncUnwindersContainingAddress(const Address &addr,
|
|
SymbolContext &sc) {
|
|
Initialize();
|
|
|
|
std::lock_guard<std::mutex> guard(m_mutex);
|
|
|
|
// There is an UnwindTable per object file, so we can safely use file handles
|
|
addr_t file_addr = addr.GetFileAddress();
|
|
iterator end = m_unwinds.end();
|
|
iterator insert_pos = end;
|
|
if (!m_unwinds.empty()) {
|
|
insert_pos = m_unwinds.lower_bound(file_addr);
|
|
iterator pos = insert_pos;
|
|
if ((pos == m_unwinds.end()) ||
|
|
(pos != m_unwinds.begin() &&
|
|
pos->second->GetFunctionStartAddress() != addr))
|
|
--pos;
|
|
|
|
if (pos->second->ContainsAddress(addr))
|
|
return pos->second;
|
|
}
|
|
|
|
auto range_or = GetAddressRange(addr, sc);
|
|
if (!range_or)
|
|
return nullptr;
|
|
|
|
FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, *range_or));
|
|
m_unwinds.insert(insert_pos,
|
|
std::make_pair(range_or->GetBaseAddress().GetFileAddress(),
|
|
func_unwinder_sp));
|
|
return func_unwinder_sp;
|
|
}
|
|
|
|
// Ignore any existing FuncUnwinders for this function, create a new one and
|
|
// don't add it to the UnwindTable. This is intended for use by target modules
|
|
// show-unwind where we want to create new UnwindPlans, not re-use existing
|
|
// ones.
|
|
FuncUnwindersSP UnwindTable::GetUncachedFuncUnwindersContainingAddress(
|
|
const Address &addr, const SymbolContext &sc) {
|
|
Initialize();
|
|
|
|
auto range_or = GetAddressRange(addr, sc);
|
|
if (!range_or)
|
|
return nullptr;
|
|
|
|
return std::make_shared<FuncUnwinders>(*this, *range_or);
|
|
}
|
|
|
|
void UnwindTable::Dump(Stream &s) {
|
|
std::lock_guard<std::mutex> guard(m_mutex);
|
|
s.Format("UnwindTable for '{0}':\n", m_module.GetFileSpec());
|
|
const_iterator begin = m_unwinds.begin();
|
|
const_iterator end = m_unwinds.end();
|
|
for (const_iterator pos = begin; pos != end; ++pos) {
|
|
s.Printf("[%u] 0x%16.16" PRIx64 "\n", (unsigned)std::distance(begin, pos),
|
|
pos->first);
|
|
}
|
|
s.EOL();
|
|
}
|
|
|
|
lldb_private::CallFrameInfo *UnwindTable::GetObjectFileUnwindInfo() {
|
|
Initialize();
|
|
return m_object_file_unwind_up.get();
|
|
}
|
|
|
|
DWARFCallFrameInfo *UnwindTable::GetEHFrameInfo() {
|
|
Initialize();
|
|
return m_eh_frame_up.get();
|
|
}
|
|
|
|
DWARFCallFrameInfo *UnwindTable::GetDebugFrameInfo() {
|
|
Initialize();
|
|
return m_debug_frame_up.get();
|
|
}
|
|
|
|
CompactUnwindInfo *UnwindTable::GetCompactUnwindInfo() {
|
|
Initialize();
|
|
return m_compact_unwind_up.get();
|
|
}
|
|
|
|
ArmUnwindInfo *UnwindTable::GetArmUnwindInfo() {
|
|
Initialize();
|
|
return m_arm_unwind_up.get();
|
|
}
|
|
|
|
SymbolFile *UnwindTable::GetSymbolFile() { return m_module.GetSymbolFile(); }
|
|
|
|
ArchSpec UnwindTable::GetArchitecture() { return m_module.GetArchitecture(); }
|
|
|
|
bool UnwindTable::GetAllowAssemblyEmulationUnwindPlans() {
|
|
if (ObjectFile *object_file = m_module.GetObjectFile())
|
|
return object_file->AllowAssemblyEmulationUnwindPlans();
|
|
return false;
|
|
}
|