Skip to content

Commit 2450471

Browse files
committed
[lldb][Expression] Add structor variant to LLDB's function call labels (llvm#149827)
Depends on * llvm#148877 * llvm#155483 * llvm#155485 * llvm#154137 * llvm#154142 This patch is an implementation of [this discussion](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816/7) about handling ABI-tagged structors during expression evaluation. **Motivation** LLDB encodes the mangled name of a `DW_TAG_subprogram` into `AsmLabel`s on function and method Clang AST nodes. This means that when calls to these functions get lowered into IR (when running JITted expressions), the address resolver can locate the appropriate symbol by mangled name (and it is guaranteed to find the symbol because we got the mangled name from debug-info, instead of letting Clang mangle it based on AST structure). However, we don't do this for `CXXConstructorDecl`s/`CXXDestructorDecl`s because these structor declarations in DWARF don't have a linkage name. This is because there can be multiple variants of a structor, each with a distinct mangling in the Itanium ABI. Each structor variant has its own definition `DW_TAG_subprogram`. So LLDB doesn't know which mangled name to put into the `AsmLabel`. Currently this means using ABI-tagged structors in LLDB expressions won't work (see [this RFC](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816) for concrete examples). **Proposed Solution** The `FunctionCallLabel` encoding that we put into `AsmLabel`s already supports stuffing more info about a DIE into it. So this patch extends the `FunctionCallLabel` to contain an optional discriminator (a sequence of bytes) which the `SymbolFileDWARF` plugin interprets as the constructor/destructor variant of that DIE. So when searching for the definition DIE, LLDB will include the structor variant in its heuristic for determining a match. There's a few subtleties here: 1. At the point at which LLDB first constructs the label, it has no way of knowing (just by looking at the debug-info declaration), which structor variant the expression evaluator is supposed to call. That's something that gets decided when compiling the expression. So we let the Clang mangler inject the correct structor variant into the `AsmLabel` during JITing. I adjusted the `AsmLabelAttr` mangling for this in llvm#155485. An option would've been to create a new Clang attribute which behaved like an `AsmLabel` but with these special semantics for LLDB. My main concern there is that we'd have to adjust all the `AsmLabelAttr` checks around Clang to also now account for this new attribute. 2. The compiler is free to omit the `C1` variant of a constructor if the `C2` variant is sufficient. In that case it may alias `C1` to `C2`, leaving us with only the `C2` `DW_TAG_subprogram` in the object file. Linux is one of the platforms where this occurs. For those cases I added a heuristic in `SymbolFileDWARF` where we pick `C2` if we asked for `C1` but it doesn't exist. This may not always be correct (e.g., if the compiler decided to drop `C1` for other reasons). 3. In llvm#154142 Clang will emit `C4`/`D4` variants of ctors/dtors on declarations. When resolving the `FunctionCallLabel` we will now substitute the actual variant that Clang told us we need to call into the mangled name. We do this using LLDB's `ManglingSubstitutor`. That way we find the definition DIE exactly the same way we do for regular function calls. 4. In cases where declarations and definitions live in separate modules, the DIE ID encoded in the function call label may not be enough to find the definition DIE in the encoded module ID. For those cases we fall back to how LLDB used to work: look up in all images of the target. To make sure we don't use the unified mangled name for the fallback lookup, we change the lookup name to whatever mangled name the FunctionCallLabel resolved to. rdar://104968288 (cherry picked from commit 57a7907)
1 parent 95f26e2 commit 2450471

File tree

18 files changed

+480
-79
lines changed

18 files changed

+480
-79
lines changed

lldb/include/lldb/Expression/Expression.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,15 @@ class Expression {
103103
///
104104
/// The format being:
105105
///
106-
/// <prefix>:<module uid>:<symbol uid>:<name>
106+
/// <prefix>:<discriminator>:<module uid>:<symbol uid>:<name>
107107
///
108108
/// The label string needs to stay valid for the entire lifetime
109109
/// of this object.
110110
struct FunctionCallLabel {
111+
/// Arbitrary string which language plugins can interpret for their
112+
/// own needs.
113+
llvm::StringRef discriminator;
114+
111115
/// Unique identifier of the lldb_private::Module
112116
/// which contains the symbol identified by \c symbol_id.
113117
lldb::user_id_t module_id;
@@ -133,7 +137,7 @@ struct FunctionCallLabel {
133137
///
134138
/// The representation roundtrips through \c fromString:
135139
/// \code{.cpp}
136-
/// llvm::StringRef encoded = "$__lldb_func:0x0:0x0:_Z3foov";
140+
/// llvm::StringRef encoded = "$__lldb_func:blah:0x0:0x0:_Z3foov";
137141
/// FunctionCallLabel label = *fromString(label);
138142
///
139143
/// assert (label.toString() == encoded);

lldb/include/lldb/Symbol/SymbolFile.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,12 +352,12 @@ class SymbolFile : public PluginInterface {
352352
/// Resolves the function corresponding to the specified LLDB function
353353
/// call \c label.
354354
///
355-
/// \param[in] label The FunctionCallLabel to be resolved.
355+
/// \param[in,out] label The FunctionCallLabel to be resolved.
356356
///
357357
/// \returns An llvm::Error if the specified \c label couldn't be resolved.
358358
/// Returns the resolved function (as a SymbolContext) otherwise.
359359
virtual llvm::Expected<SymbolContext>
360-
ResolveFunctionCallLabel(const FunctionCallLabel &label) {
360+
ResolveFunctionCallLabel(FunctionCallLabel &label) {
361361
return llvm::createStringError("Not implemented");
362362
}
363363

lldb/source/Expression/Expression.cpp

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,21 @@ Expression::Expression(ExecutionContextScope &exe_scope)
3434

3535
llvm::Expected<FunctionCallLabel>
3636
lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
37-
llvm::SmallVector<llvm::StringRef, 4> components;
38-
label.split(components, ":", /*MaxSplit=*/3);
37+
llvm::SmallVector<llvm::StringRef, 5> components;
38+
label.split(components, ":", /*MaxSplit=*/4);
3939

40-
if (components.size() != 4)
40+
if (components.size() != 5)
4141
return llvm::createStringError("malformed function call label.");
4242

4343
if (components[0] != FunctionCallLabelPrefix)
4444
return llvm::createStringError(llvm::formatv(
4545
"expected function call label prefix '{0}' but found '{1}' instead.",
4646
FunctionCallLabelPrefix, components[0]));
4747

48-
llvm::StringRef module_label = components[1];
49-
llvm::StringRef die_label = components[2];
48+
llvm::StringRef discriminator = components[1];
49+
llvm::StringRef module_label = components[2];
50+
llvm::StringRef die_label = components[3];
51+
llvm::StringRef lookup_name = components[4];
5052

5153
lldb::user_id_t module_id = 0;
5254
if (!llvm::to_integer(module_label, module_id))
@@ -58,20 +60,23 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
5860
return llvm::createStringError(
5961
llvm::formatv("failed to parse symbol ID from '{0}'.", die_label));
6062

61-
return FunctionCallLabel{/*.module_id=*/module_id,
63+
return FunctionCallLabel{/*.discriminator=*/discriminator,
64+
/*.module_id=*/module_id,
6265
/*.symbol_id=*/die_id,
63-
/*.lookup_name=*/components[3]};
66+
/*.lookup_name=*/lookup_name};
6467
}
6568

6669
std::string lldb_private::FunctionCallLabel::toString() const {
67-
return llvm::formatv("{0}:{1:x}:{2:x}:{3}", FunctionCallLabelPrefix,
68-
module_id, symbol_id, lookup_name)
70+
return llvm::formatv("{0}:{1}:{2:x}:{3:x}:{4}", FunctionCallLabelPrefix,
71+
discriminator, module_id, symbol_id, lookup_name)
6972
.str();
7073
}
7174

7275
void llvm::format_provider<FunctionCallLabel>::format(
7376
const FunctionCallLabel &label, raw_ostream &OS, StringRef Style) {
74-
OS << llvm::formatv("FunctionCallLabel{ module_id: {0:x}, symbol_id: {1:x}, "
75-
"lookup_name: {2} }",
76-
label.module_id, label.symbol_id, label.lookup_name);
77+
OS << llvm::formatv("FunctionCallLabel{ discriminator: {0}, module_id: "
78+
"{1:x}, symbol_id: {2:x}, "
79+
"lookup_name: {3} }",
80+
label.discriminator, label.module_id, label.symbol_id,
81+
label.lookup_name);
7782
}

lldb/source/Expression/IRExecutionUnit.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,7 @@ class LoadAddressResolver {
761761
/// Returns address of the function referred to by the special function call
762762
/// label \c label.
763763
static llvm::Expected<lldb::addr_t>
764-
ResolveFunctionCallLabel(const FunctionCallLabel &label,
764+
ResolveFunctionCallLabel(FunctionCallLabel &label,
765765
const lldb_private::SymbolContext &sc,
766766
bool &symbol_was_missing_weak) {
767767
symbol_was_missing_weak = false;

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram,
257257
}
258258

259259
static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
260-
char const *name = die.GetMangledName(/*substitute_name_allowed*/ false);
260+
const char *name = die.GetMangledName(/*substitute_name_allowed*/ false);
261261
if (!name)
262262
return {};
263263

@@ -291,7 +291,9 @@ static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
291291
if (die_id == LLDB_INVALID_UID)
292292
return {};
293293

294-
return FunctionCallLabel{/*module_id=*/module_id,
294+
// Note, discriminator is added by Clang during mangling.
295+
return FunctionCallLabel{/*discriminator=*/{},
296+
/*module_id=*/module_id,
295297
/*symbol_id=*/die_id,
296298
/*.lookup_name=*/name}
297299
.toString();

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp

Lines changed: 146 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "SymbolFileDWARF.h"
10+
#include "clang/Basic/ABI.h"
1011
#include "llvm/ADT/STLExtras.h"
12+
#include "llvm/ADT/StringExtras.h"
13+
#include "llvm/ADT/StringRef.h"
1114
#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
1215
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
1316
#include "llvm/Support/Casting.h"
17+
#include "llvm/Support/Error.h"
1418
#include "llvm/Support/FileUtilities.h"
1519
#include "llvm/Support/FormatAdapters.h"
1620
#include "llvm/Support/Threading.h"
@@ -22,6 +26,7 @@
2226
#include "lldb/Core/Progress.h"
2327
#include "lldb/Core/Section.h"
2428
#include "lldb/Core/Value.h"
29+
#include "lldb/Expression/Expression.h"
2530
#include "lldb/Utility/ArchSpec.h"
2631
#include "lldb/Utility/LLDBLog.h"
2732
#include "lldb/Utility/OptionParsing.h"
@@ -79,6 +84,7 @@
7984

8085
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
8186
#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"
87+
#include "llvm/Demangle/Demangle.h"
8288
#include "llvm/Support/FileSystem.h"
8389
#include "llvm/Support/FormatVariadic.h"
8490

@@ -2502,34 +2508,148 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die,
25022508
return false;
25032509
}
25042510

2505-
DWARFDIE
2506-
SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label) {
2507-
DWARFDIE definition;
2508-
Module::LookupInfo info(ConstString(label.lookup_name),
2509-
lldb::eFunctionNameTypeFull,
2510-
lldb::eLanguageTypeUnknown);
2511-
2512-
m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
2513-
if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
2514-
return IterationAction::Continue;
2511+
static llvm::StringRef ClangToItaniumCtorKind(clang::CXXCtorType kind) {
2512+
switch (kind) {
2513+
case clang::CXXCtorType::Ctor_Complete:
2514+
return "C1";
2515+
case clang::CXXCtorType::Ctor_Base:
2516+
return "C2";
2517+
case clang::CXXCtorType::Ctor_Unified:
2518+
return "C4";
2519+
case clang::CXXCtorType::Ctor_CopyingClosure:
2520+
case clang::CXXCtorType::Ctor_DefaultClosure:
2521+
case clang::CXXCtorType::Ctor_Comdat:
2522+
llvm_unreachable("Unexpected constructor kind.");
2523+
}
2524+
}
25152525

2516-
// We don't check whether the specification DIE for this function
2517-
// corresponds to the declaration DIE because the declaration might be in
2518-
// a type-unit but the definition in the compile-unit (and it's
2519-
// specifcation would point to the declaration in the compile-unit). We
2520-
// rely on the mangled name within the module to be enough to find us the
2521-
// unique definition.
2522-
definition = entry;
2523-
return IterationAction::Stop;
2524-
});
2526+
static llvm::StringRef ClangToItaniumDtorKind(clang::CXXDtorType kind) {
2527+
switch (kind) {
2528+
case clang::CXXDtorType::Dtor_Deleting:
2529+
return "D0";
2530+
case clang::CXXDtorType::Dtor_Complete:
2531+
return "D1";
2532+
case clang::CXXDtorType::Dtor_Base:
2533+
return "D2";
2534+
case clang::CXXDtorType::Dtor_Unified:
2535+
return "D4";
2536+
case clang::CXXDtorType::Dtor_Comdat:
2537+
llvm_unreachable("Unexpected destructor kind.");
2538+
}
2539+
}
2540+
2541+
static llvm::StringRef
2542+
GetItaniumCtorDtorVariant(llvm::StringRef discriminator) {
2543+
const bool is_ctor = discriminator.consume_front("C");
2544+
if (!is_ctor && !discriminator.consume_front("D"))
2545+
return {};
2546+
2547+
uint64_t structor_kind;
2548+
if (!llvm::to_integer(discriminator, structor_kind))
2549+
return {};
2550+
2551+
if (is_ctor) {
2552+
if (structor_kind > clang::CXXCtorType::Ctor_Unified)
2553+
return {};
2554+
2555+
return ClangToItaniumCtorKind(
2556+
static_cast<clang::CXXCtorType>(structor_kind));
2557+
}
2558+
2559+
if (structor_kind > clang::CXXDtorType::Dtor_Unified)
2560+
return {};
2561+
2562+
return ClangToItaniumDtorKind(static_cast<clang::CXXDtorType>(structor_kind));
2563+
}
2564+
2565+
llvm::Expected<DWARFDIE>
2566+
SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label,
2567+
const DWARFDIE &declaration) {
2568+
auto do_lookup = [this](llvm::StringRef lookup_name) -> DWARFDIE {
2569+
DWARFDIE found;
2570+
Module::LookupInfo info(ConstString(lookup_name),
2571+
lldb::eFunctionNameTypeFull,
2572+
lldb::eLanguageTypeUnknown);
2573+
2574+
m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
2575+
if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
2576+
return IterationAction::Continue;
2577+
2578+
found = entry;
2579+
return IterationAction::Stop;
2580+
});
2581+
2582+
return found;
2583+
};
2584+
2585+
DWARFDIE definition = do_lookup(label.lookup_name);
2586+
if (definition.IsValid())
2587+
return definition;
2588+
2589+
// This is not a structor lookup. Nothing else to be done here.
2590+
if (label.discriminator.empty())
2591+
return llvm::createStringError(
2592+
"no definition DIE found in this SymbolFile");
2593+
2594+
// We're doing a structor lookup. Maybe we didn't find the structor variant
2595+
// because the complete object structor was aliased to the base object
2596+
// structor. Try finding the alias instead.
2597+
//
2598+
// TODO: there are other reasons for why a subprogram definition might be
2599+
// missing. Ideally DWARF would tell us more details about which structor
2600+
// variant a DIE corresponds to and whether it's an alias.
2601+
auto subst_or_err =
2602+
CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle(
2603+
label.lookup_name);
2604+
if (!subst_or_err)
2605+
return subst_or_err.takeError();
2606+
2607+
definition = do_lookup(*subst_or_err);
2608+
2609+
if (!definition.IsValid())
2610+
return llvm::createStringError(
2611+
"failed to find definition DIE for structor alias in fallback lookup");
25252612

25262613
return definition;
25272614
}
25282615

25292616
llvm::Expected<SymbolContext>
2530-
SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
2617+
SymbolFileDWARF::ResolveFunctionCallLabel(FunctionCallLabel &label) {
25312618
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
25322619

2620+
if (!label.discriminator.empty()) {
2621+
llvm::StringRef from = label.discriminator[0] == 'C' ? "C4" : "D4";
2622+
2623+
llvm::StringRef variant = GetItaniumCtorDtorVariant(label.discriminator);
2624+
if (variant.empty())
2625+
return llvm::createStringError(
2626+
"failed to get Itanium variant for discriminator");
2627+
2628+
if (from == variant)
2629+
return llvm::createStringError(
2630+
"tried substituting unified structor variant into label");
2631+
2632+
// If we failed to substitute unified mangled name, don't try to do a lookup
2633+
// using the unified name because there may be multiple definitions for it
2634+
// in the index, and we wouldn't know which one to choose.
2635+
auto subst_or_err = CPlusPlusLanguage::SubstituteStructor_ItaniumMangle(
2636+
label.lookup_name, from, variant);
2637+
if (!subst_or_err)
2638+
return llvm::joinErrors(
2639+
llvm::createStringError(llvm::formatv(
2640+
"failed to substitute {0} for {1} in mangled name {2}:", from,
2641+
variant, label.lookup_name)),
2642+
subst_or_err.takeError());
2643+
2644+
if (!*subst_or_err)
2645+
return llvm::createStringError(
2646+
llvm::formatv("got invalid substituted mangled named (substituted "
2647+
"{0} for {1} in mangled name {2})",
2648+
from, variant, label.lookup_name));
2649+
2650+
label.lookup_name = subst_or_err->GetStringRef();
2651+
}
2652+
25332653
DWARFDIE die = GetDIE(label.symbol_id);
25342654
if (!die.IsValid())
25352655
return llvm::createStringError(
@@ -2538,11 +2658,13 @@ SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
25382658
// Label was created using a declaration DIE. Need to fetch the definition
25392659
// to resolve the function call.
25402660
if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) {
2541-
auto definition = FindFunctionDefinition(label);
2542-
if (!definition)
2543-
return llvm::createStringError("failed to find definition DIE");
2661+
auto die_or_err = FindFunctionDefinition(label, die);
2662+
if (!die_or_err)
2663+
return llvm::joinErrors(
2664+
llvm::createStringError("failed to find definition DIE:"),
2665+
die_or_err.takeError());
25442666

2545-
die = std::move(definition);
2667+
die = std::move(*die_or_err);
25462668
}
25472669

25482670
SymbolContextList sc_list;

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,9 @@ class SymbolFileDWARF : public SymbolFileCommon {
398398
/// SymbolFile.
399399
///
400400
/// \returns A valid definition DIE on success.
401-
DWARFDIE FindFunctionDefinition(const FunctionCallLabel &label);
401+
llvm::Expected<DWARFDIE>
402+
FindFunctionDefinition(const FunctionCallLabel &label,
403+
const DWARFDIE &declaration);
402404

403405
protected:
404406
SymbolFileDWARF(const SymbolFileDWARF &) = delete;
@@ -465,7 +467,7 @@ class SymbolFileDWARF : public SymbolFileCommon {
465467
DIEArray &&variable_dies);
466468

467469
llvm::Expected<SymbolContext>
468-
ResolveFunctionCallLabel(const FunctionCallLabel &label) override;
470+
ResolveFunctionCallLabel(FunctionCallLabel &label) override;
469471

470472
// Given a die_offset, figure out the symbol context representing that die.
471473
bool ResolveFunction(const DWARFDIE &die, bool include_inlines,

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,8 +1716,8 @@ void SymbolFileDWARFDebugMap::GetCompileOptions(
17161716
});
17171717
}
17181718

1719-
llvm::Expected<SymbolContext> SymbolFileDWARFDebugMap::ResolveFunctionCallLabel(
1720-
const FunctionCallLabel &label) {
1719+
llvm::Expected<SymbolContext>
1720+
SymbolFileDWARFDebugMap::ResolveFunctionCallLabel(FunctionCallLabel &label) {
17211721
const uint64_t oso_idx = GetOSOIndexFromUserID(label.symbol_id);
17221722
SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
17231723
if (!oso_dwarf)

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class SymbolFileDWARFDebugMap : public SymbolFileCommon {
158158
GetCompileOptions(std::unordered_map<lldb::CompUnitSP, Args> &args) override;
159159

160160
llvm::Expected<SymbolContext>
161-
ResolveFunctionCallLabel(const FunctionCallLabel &label) override;
161+
ResolveFunctionCallLabel(FunctionCallLabel &label) override;
162162

163163
protected:
164164
enum { kHaveInitializedOSOs = (1 << 0), kNumFlags };
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CXX_SOURCES := main.cpp
2+
3+
include Makefile.rules

0 commit comments

Comments
 (0)