Skip to content

[lldb][Expression] Encode Module and DIE UIDs into function AsmLabels #148877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 39 commits into from
Aug 1, 2025
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
87a26ce
[lldb][Expression] Encode Module and DIE UIDs into function AsmLabels
Michael137 Nov 15, 2024
818cf22
fixup! add quotes
Michael137 Jul 21, 2025
53a43aa
fixup! create literal AsmLabels
Michael137 Jul 28, 2025
5078c85
fixup! move to new FindModule API; make module UID atomic
Michael137 Jul 28, 2025
6741c7b
fixup! rename FunctionCallLabel members
Michael137 Jul 28, 2025
96cbb48
fixup! remove unused headers
Michael137 Jul 28, 2025
65f8706
fixup! move FunctionCallLabel APIs into Expression component
Michael137 Jul 28, 2025
3e27e98
fixup! adjust TypeSystemClang::DeclGetMangledName
Michael137 Jul 28, 2025
76ae694
fixup! move label encoding/decoding into FunctionCallLabel structure
Michael137 Jul 28, 2025
1e6e9c0
fixup! make mangled name last component in label; remove need for spl…
Michael137 Jul 28, 2025
73768e6
fixup! rebase on latest ModuleList changes
Michael137 Jul 28, 2025
6eb12d9
fixup! remove mention of mangled name from FunctionCallLabel doc
Michael137 Jul 28, 2025
de9c994
fixup! add format_provider for FunctionCallLabel
Michael137 Jul 28, 2025
308d33d
fixup! only do name lookup for declaration DIE cases
Michael137 Jul 28, 2025
7408359
fixup! add tests; account for ':' in mangled name; adjust error messages
Michael137 Jul 29, 2025
75c5cd0
fixup! drop \01 mangling prefix when searching using IR function name
Michael137 Jul 30, 2025
cc7d462
fixup! search .o files when searching for module by UID
Michael137 Jul 30, 2025
0ce95bd
fixup! revert whitespace change
Michael137 Jul 30, 2025
5789808
fixup! remove stale comment
Michael137 Jul 30, 2025
6973ac2
fixup! implement SymbolFileDWARFDebugMap::ResolveFunctionCallLabel
Michael137 Jul 30, 2025
e43310a
fixup! add test for when definition is in different Module than decla…
Michael137 Jul 30, 2025
e8146d7
fixup! clang-format
Michael137 Jul 30, 2025
e4129ce
fixup! python format
Michael137 Jul 30, 2025
36b39a4
fixup! clang-format
Michael137 Jul 30, 2025
e882765
fixup! mark test NO_DEBUG_INFO_TESTCASE
Michael137 Jul 30, 2025
a8a9890
fixup! fallback to name lookup if definition is in separate module fr…
Michael137 Jul 30, 2025
a5b2e26
fixup! register dylib in test
Michael137 Jul 30, 2025
5aa1332
fixup! fix typo
Michael137 Jul 30, 2025
98d2c4b
fixup! SymbolContextList -> SymbolContext
Michael137 Jul 30, 2025
2a0b799
fixup! simplify label parsing
Michael137 Jul 30, 2025
e080b6d
fixup! remove unused function
Michael137 Jul 30, 2025
24fbf6c
fixup! add TODO
Michael137 Jul 30, 2025
fa0b004
fixup! fix docs
Michael137 Jul 30, 2025
edd781d
fixup! no need for std::optional
Michael137 Jul 30, 2025
f1e3ce1
fixup! add tests for TypeSystemClang::DeclGetMangledName
Michael137 Jul 31, 2025
eabbf72
fixup! fix mangled name test
Michael137 Jul 31, 2025
1f6059c
fixup! fix LLDB_LOG_ERROR argument error
Michael137 Jul 31, 2025
f43ca85
Merge branch 'main' into lldb/reliable-function-call-labels
Michael137 Jul 31, 2025
9269af7
fixup! fix after rebase
Michael137 Jul 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lldb/include/lldb/Core/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ struct ModuleFunctionSearchOptions {
///
/// The module will parse more detailed information as more queries are made.
class Module : public std::enable_shared_from_this<Module>,
public SymbolContextScope {
public SymbolContextScope,
public UserID {
public:
class LookupInfo;
// Static functions that can track the lifetime of module objects. This is
Expand Down
8 changes: 8 additions & 0 deletions lldb/include/lldb/Core/ModuleList.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,14 @@ class ModuleList {
// UUID values is very efficient and accurate.
lldb::ModuleSP FindModule(const UUID &uuid) const;

/// Find a module by LLDB-specific unique identifier.
///
/// \param[in] uid The UID of the module assigned to it on construction.
///
/// \returns ModuleSP of module with \c uid. Returns nullptr if no such
/// module could be found.
lldb::ModuleSP FindModule(lldb::user_id_t uid) const;

/// Finds the first module whose file specification matches \a module_spec.
lldb::ModuleSP FindFirstModule(const ModuleSpec &module_spec) const;

Expand Down
57 changes: 57 additions & 0 deletions lldb/include/lldb/Expression/Expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <string>
#include <vector>

#include "llvm/Support/FormatProviders.h"

#include "lldb/Expression/ExpressionTypeSystemHelper.h"
#include "lldb/lldb-forward.h"
Expand Down Expand Up @@ -96,6 +97,62 @@ class Expression {
///invalid.
};

/// Holds parsed information about a function call label that
/// LLDB attaches as an AsmLabel to function AST nodes it parses
/// from debug-info.
///
/// The format being:
///
/// <prefix>:<module uid>:<symbol uid>:<name>
///
/// The label string needs to stay valid for the entire lifetime
/// of this object.
struct FunctionCallLabel {
/// Unique identifier of the lldb_private::Module
/// which contains the symbol identified by \c symbol_id.
lldb::user_id_t module_id;

/// Unique identifier of the function symbol on which to
/// perform the function call. For example, for DWARF this would
/// be the DIE UID.
lldb::user_id_t symbol_id;

/// Name to use when searching for the function symbol in
/// \c module_id. For most function calls this will be a
/// mangled name. In cases where a mangled name can't be used,
/// this will be the function name.
///
/// NOTE: kept as last element so we don't have to worry about
/// ':' in the mangled name when parsing the label.
llvm::StringRef lookup_name;

/// Decodes the specified function \c label into a \c FunctionCallLabel.
static llvm::Expected<FunctionCallLabel> fromString(llvm::StringRef label);

/// Encode this FunctionCallLabel into its string representation.
///
/// The representation roundtrips through \c fromString:
/// \code{.cpp}
/// llvm::StringRef encoded = "$__lldb_func:0x0:0x0:_Z3foov";
/// FunctionCallLabel label = *fromString(label);
///
/// assert (label.toString() == encoded);
/// assert (*fromString(label.toString()) == label);
/// \endcode
std::string toString() const;
};

/// LLDB attaches this prefix to mangled names of functions that get called
/// from JITted expressions.
inline constexpr llvm::StringRef FunctionCallLabelPrefix = "$__lldb_func";

} // namespace lldb_private

namespace llvm {
template <> struct format_provider<lldb_private::FunctionCallLabel> {
static void format(const lldb_private::FunctionCallLabel &label,
raw_ostream &OS, StringRef Style);
};
} // namespace llvm

#endif // LLDB_EXPRESSION_EXPRESSION_H
13 changes: 13 additions & 0 deletions lldb/include/lldb/Symbol/SymbolFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/SourceModule.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Symbol/TypeList.h"
#include "lldb/Symbol/TypeSystem.h"
Expand Down Expand Up @@ -328,6 +329,18 @@ class SymbolFile : public PluginInterface {
GetMangledNamesForFunction(const std::string &scope_qualified_name,
std::vector<ConstString> &mangled_names);

/// Resolves the function corresponding to the specified LLDB function
/// call \c label.
///
/// \param[in] label The FunctionCallLabel to be resolved.
///
/// \returns An llvm::Error if the specified \c label couldn't be resolved.
/// Returns the resolved function (as a SymbolContext) otherwise.
virtual llvm::Expected<SymbolContext>
ResolveFunctionCallLabel(const FunctionCallLabel &label) {
return llvm::createStringError("Not implemented");
}

virtual void GetTypes(lldb_private::SymbolContextScope *sc_scope,
lldb::TypeClass type_mask,
lldb_private::TypeList &type_list) = 0;
Expand Down
9 changes: 6 additions & 3 deletions lldb/source/Core/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,10 @@ Module *Module::GetAllocatedModuleAtIndex(size_t idx) {
return nullptr;
}

static std::atomic<lldb::user_id_t> g_unique_id = 1;

Module::Module(const ModuleSpec &module_spec)
: m_unwind_table(*this), m_file_has_changed(false),
: UserID(g_unique_id++), m_unwind_table(*this), m_file_has_changed(false),
m_first_file_changed_log(false) {
// Scope for locker below...
{
Expand Down Expand Up @@ -236,7 +238,8 @@ Module::Module(const ModuleSpec &module_spec)
Module::Module(const FileSpec &file_spec, const ArchSpec &arch,
ConstString object_name, lldb::offset_t object_offset,
const llvm::sys::TimePoint<> &object_mod_time)
: m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)),
: UserID(g_unique_id++),
m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)),
m_arch(arch), m_file(file_spec), m_object_name(object_name),
m_object_offset(object_offset), m_object_mod_time(object_mod_time),
m_unwind_table(*this), m_file_has_changed(false),
Expand All @@ -257,7 +260,7 @@ Module::Module(const FileSpec &file_spec, const ArchSpec &arch,
}

Module::Module()
: m_unwind_table(*this), m_file_has_changed(false),
: UserID(g_unique_id++), m_unwind_table(*this), m_file_has_changed(false),
m_first_file_changed_log(false) {
std::lock_guard<std::recursive_mutex> guard(
GetAllocationModuleCollectionMutex());
Expand Down
14 changes: 14 additions & 0 deletions lldb/source/Core/ModuleList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,20 @@ ModuleSP ModuleList::FindModule(const UUID &uuid) const {
return module_sp;
}

ModuleSP ModuleList::FindModule(lldb::user_id_t uid) const {
ModuleSP module_sp;
ForEach([&](const ModuleSP &m) {
if (m->GetID() == uid) {
module_sp = m;
return IterationAction::Stop;
}

return IterationAction::Continue;
});

return module_sp;
}

void ModuleList::FindTypes(Module *search_first, const TypeQuery &query,
TypeResults &results) const {
std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
Expand Down
49 changes: 49 additions & 0 deletions lldb/source/Expression/Expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
#include "lldb/Target/ExecutionContextScope.h"
#include "lldb/Target/Target.h"

#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"

using namespace lldb_private;

Expression::Expression(Target &target)
Expand All @@ -26,3 +31,47 @@ Expression::Expression(ExecutionContextScope &exe_scope)
m_jit_end_addr(LLDB_INVALID_ADDRESS) {
assert(m_target_wp.lock());
}

llvm::Expected<FunctionCallLabel>
lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
llvm::SmallVector<llvm::StringRef, 4> components;
label.split(components, ":", /*MaxSplit=*/3);

if (components.size() != 4)
return llvm::createStringError("malformed function call label.");

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

llvm::StringRef module_label = components[1];
llvm::StringRef die_label = components[2];

lldb::user_id_t module_id = 0;
if (!llvm::to_integer(module_label, module_id))
return llvm::createStringError(
llvm::formatv("failed to parse module ID from '{0}'.", module_label));

lldb::user_id_t die_id;
if (!llvm::to_integer(die_label, die_id))
return llvm::createStringError(
llvm::formatv("failed to parse symbol ID from '{0}'.", die_label));

return FunctionCallLabel{/*.module_id=*/module_id,
/*.symbol_id=*/die_id,
/*.lookup_name=*/components[3]};
}

std::string lldb_private::FunctionCallLabel::toString() const {
return llvm::formatv("{0}:{1:x}:{2:x}:{3}", FunctionCallLabelPrefix,
module_id, symbol_id, lookup_name)
.str();
}

void llvm::format_provider<FunctionCallLabel>::format(
const FunctionCallLabel &label, raw_ostream &OS, StringRef Style) {
OS << llvm::formatv("FunctionCallLabel{ module_id: {0:x}, symbol_id: {1:x}, "
"lookup_name: {2} }",
label.module_id, label.symbol_id, label.lookup_name);
}
65 changes: 65 additions & 0 deletions lldb/source/Expression/IRExecutionUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"

#include "lldb/Core/Debugger.h"
#include "lldb/Core/Disassembler.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/Section.h"
#include "lldb/Expression/Expression.h"
#include "lldb/Expression/IRExecutionUnit.h"
#include "lldb/Expression/ObjectFileJIT.h"
#include "lldb/Host/HostInfo.h"
Expand All @@ -36,6 +38,7 @@
#include "lldb/Utility/LLDBAssert.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/lldb-defines.h"

#include <optional>

Expand Down Expand Up @@ -771,6 +774,40 @@ class LoadAddressResolver {
lldb::addr_t m_best_internal_load_address = LLDB_INVALID_ADDRESS;
};

/// Returns address of the function referred to by the special function call
/// label \c label.
static llvm::Expected<lldb::addr_t>
ResolveFunctionCallLabel(const FunctionCallLabel &label,
const lldb_private::SymbolContext &sc,
bool &symbol_was_missing_weak) {
symbol_was_missing_weak = false;

if (!sc.target_sp)
return llvm::createStringError("target not available.");

auto module_sp = sc.target_sp->GetImages().FindModule(label.module_id);
if (!module_sp)
return llvm::createStringError(
llvm::formatv("failed to find module by UID {0}", label.module_id));

auto *symbol_file = module_sp->GetSymbolFile();
if (!symbol_file)
return llvm::createStringError(
llvm::formatv("no SymbolFile found on module {0:x}.", module_sp.get()));

auto sc_or_err = symbol_file->ResolveFunctionCallLabel(label);
if (!sc_or_err)
return llvm::joinErrors(
llvm::createStringError("failed to resolve function by UID"),
sc_or_err.takeError());

SymbolContextList sc_list;
sc_list.Append(*sc_or_err);

LoadAddressResolver resolver(*sc.target_sp, symbol_was_missing_weak);
return resolver.Resolve(sc_list).value_or(LLDB_INVALID_ADDRESS);
}

lldb::addr_t
IRExecutionUnit::FindInSymbols(const std::vector<ConstString> &names,
const lldb_private::SymbolContext &sc,
Expand Down Expand Up @@ -906,6 +943,34 @@ lldb::addr_t IRExecutionUnit::FindInUserDefinedSymbols(

lldb::addr_t IRExecutionUnit::FindSymbol(lldb_private::ConstString name,
bool &missing_weak) {
if (name.GetStringRef().starts_with(FunctionCallLabelPrefix)) {
auto label_or_err = FunctionCallLabel::fromString(name);
if (!label_or_err) {
LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), label_or_err.takeError(),
"failed to create FunctionCallLabel from '{1}': {0}",
name.GetStringRef());
return LLDB_INVALID_ADDRESS;
}

if (auto addr_or_err =
ResolveFunctionCallLabel(*label_or_err, m_sym_ctx, missing_weak)) {
return *addr_or_err;
} else {
LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), addr_or_err.takeError(),
"Failed to resolve function call label '{1}': {0}",
name.GetStringRef());

// Fall back to lookup by name despite error in resolving the label.
// May happen in practice if the definition of a function lives in
// a different lldb_private::Module than it's declaration. Meaning
// we couldn't pin-point it using the information encoded in the label.
name.SetString(label_or_err->lookup_name);
}
}

// TODO: now with function call labels, do we still need to
// generate alternate manglings?

std::vector<ConstString> candidate_C_names;
std::vector<ConstString> candidate_CPlusPlus_names;

Expand Down
4 changes: 3 additions & 1 deletion lldb/source/Expression/IRInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,9 @@ class InterpreterStackFrame {
break;
case Value::FunctionVal:
if (const Function *constant_func = dyn_cast<Function>(constant)) {
lldb_private::ConstString name(constant_func->getName());
lldb_private::ConstString name(
llvm::GlobalValue::dropLLVMManglingEscape(
constant_func->getName()));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to drop the \01 mangling prefix here since we're not creating literal AsmLabels anymore. Fixed a couple of test failures where we ran expressions like expression func (to print the function pointer). We would try to find the symbol here (including the mangling prefix) and fail.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do this in a separate PR technically

bool missing_weak = false;
lldb::addr_t addr = m_execution_unit.FindSymbol(name, missing_weak);
if (addr == LLDB_INVALID_ADDRESS)
Expand Down
36 changes: 35 additions & 1 deletion lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "Plugins/Language/ObjC/ObjCLanguage.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/Value.h"
#include "lldb/Expression/Expression.h"
#include "lldb/Host/Host.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/Function.h"
Expand Down Expand Up @@ -254,7 +255,40 @@ static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
if (!name)
return {};

return name;
SymbolFileDWARF *dwarf = die.GetDWARF();
if (!dwarf)
return {};

auto get_module_id = [&](SymbolFile *sym) {
if (!sym)
return LLDB_INVALID_UID;

auto *obj = sym->GetMainObjectFile();
if (!obj)
return LLDB_INVALID_UID;

auto module_sp = obj->GetModule();
if (!module_sp)
return LLDB_INVALID_UID;

return module_sp->GetID();
};

lldb::user_id_t module_id = get_module_id(dwarf->GetDebugMapSymfile());
if (module_id == LLDB_INVALID_UID)
module_id = get_module_id(dwarf);

if (module_id == LLDB_INVALID_UID)
return {};

const auto die_id = die.GetID();
if (die_id == LLDB_INVALID_UID)
return {};

return FunctionCallLabel{/*module_id=*/module_id,
/*symbol_id=*/die_id,
/*.lookup_name=*/name}
.toString();
}

TypeSP DWARFASTParserClang::ParseTypeFromClangModule(const SymbolContext &sc,
Expand Down
Loading
Loading