[Lldb-commits] [lldb] [lldb] Filter overridden functions from expression name lookup (PR #233545)
Longjun Luo via lldb-commits
lldb-commits at lists.llvm.org
Tue Oct 20 02:08:02 PDT 2026
https://github.com/anatasluo updated https://github.com/llvm/llvm-project/pull/233545
>From 3a4743e48464f01f2cf3565e6b7536db3b7321ac Mon Sep 17 00:00:00 2001
From: Longjun Luo <luolongjuna at gmail.com>
Date: Mon, 19 Oct 2026 19:16:46 +0000
Subject: [PATCH] [lldb] Filter overridden functions from expression name
lookup
An overridden weak function can retain its body and DWARF in an ELF
executable. Expression lookup may select that definition, causing a
call by name to execute the overridden body instead of the definition
selected by the linker.
Filter external DWARF function candidates in `LookupFunction` using
the executable's ELF symbol table before scope filtering and type
deduplication.
Attach a `FunctionCallLabel` to the selected declaration so JIT symbol
resolution preserves that choice (falling back to the ELF symbol's
address if the linked definition has no DWARF).
Fixes #189539
Assisted-by: OpenAI Codex
---
lldb/include/lldb/Expression/Expression.h | 14 ++
lldb/include/lldb/Symbol/Function.h | 13 +-
lldb/include/lldb/Symbol/ObjectFile.h | 6 +
lldb/source/Expression/Expression.cpp | 50 ++++++
lldb/source/Expression/IRExecutionUnit.cpp | 18 ++
.../Clang/ClangExpressionDeclMap.cpp | 72 +++++++-
.../Clang/NameSearchContext.cpp | 2 +
.../Plugins/ObjectFile/ELF/ObjectFileELF.h | 4 +
.../SymbolFile/DWARF/SymbolFileDWARF.cpp | 7 +-
.../overridden-weak-symbols/Makefile | 10 ++
.../TestOverriddenWeakSymbols.py | 155 ++++++++++++++++++
.../overridden-weak-symbols/cxx-main.cpp | 1 +
.../overridden-weak-symbols/cxx-weak.cpp | 1 +
.../overridden-weak-symbols/localized-input.c | 10 ++
.../overridden-weak-symbols/localized-main.c | 5 +
.../expression/overridden-weak-symbols/main.c | 34 ++++
.../overridden-weak-symbols/no-debug-main.c | 8 +
.../overridden-weak-symbols/no-debug-strong.c | 1 +
.../overridden-weak-symbols/shared-main.c | 12 ++
.../overridden-weak-symbols/shared-strong.c | 1 +
.../overridden-weak-symbols/shared-weak.c | 2 +
.../overridden-weak-symbols/symbol-only.c | 8 +
.../expression/overridden-weak-symbols/weak.c | 35 ++++
lldb/unittests/Expression/ExpressionTest.cpp | 40 +++++
24 files changed, 505 insertions(+), 4 deletions(-)
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/Makefile
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/TestOverriddenWeakSymbols.py
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/cxx-main.cpp
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/cxx-weak.cpp
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/localized-input.c
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/localized-main.c
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/main.c
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/no-debug-main.c
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/no-debug-strong.c
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/shared-main.c
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/shared-strong.c
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/shared-weak.c
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/symbol-only.c
create mode 100644 lldb/test/API/commands/expression/overridden-weak-symbols/weak.c
diff --git a/lldb/include/lldb/Expression/Expression.h b/lldb/include/lldb/Expression/Expression.h
index 847226167d584a..7c7f339490e182 100644
--- a/lldb/include/lldb/Expression/Expression.h
+++ b/lldb/include/lldb/Expression/Expression.h
@@ -13,6 +13,7 @@
#include <string>
#include <vector>
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/FormatProviders.h"
#include "lldb/Expression/ExpressionTypeSystemHelper.h"
@@ -21,6 +22,19 @@
namespace lldb_private {
+/// Find all exact-name code symbols in the main ELF executable's symbol table.
+std::vector<Symbol *>
+FindELFFunctionSymbolsWithName(Target &target, const lldb::ModuleSP &module,
+ ConstString name);
+
+/// Return the first symbol in name_matches at file_address, or nullptr.
+Symbol *FindSymbolAtFileAddress(llvm::ArrayRef<Symbol *> name_matches,
+ lldb::addr_t file_address);
+
+/// Return an external or weak symbol when all such symbols with valid file
+/// addresses share one address; otherwise return nullptr.
+Symbol *FindUniqueExternalFunctionSymbol(llvm::ArrayRef<Symbol *> symbols);
+
/// \class Expression Expression.h "lldb/Expression/Expression.h" Encapsulates
/// a single expression for use in lldb
///
diff --git a/lldb/include/lldb/Symbol/Function.h b/lldb/include/lldb/Symbol/Function.h
index 04a8e9a89c90ec..e95c91631c7810 100644
--- a/lldb/include/lldb/Symbol/Function.h
+++ b/lldb/include/lldb/Symbol/Function.h
@@ -540,6 +540,16 @@ class Function : public UserID, public SymbolContextScope {
const Mangled &GetMangled() const { return m_mangled; }
+ /// Whether debug information marks this definition as visible outside its CU.
+ bool IsExternal() const { return m_flags.Test(flagsExternal); }
+
+ void SetIsExternal(bool external) {
+ if (external)
+ m_flags.Set(flagsExternal);
+ else
+ m_flags.Clear(flagsExternal);
+ }
+
/// Get the DeclContext for this function, if available.
///
/// \return
@@ -634,7 +644,8 @@ class Function : public UserID, public SymbolContextScope {
protected:
enum {
/// Whether we already tried to calculate the prologue size.
- flagsCalculatedPrologueSize = (1 << 0)
+ flagsCalculatedPrologueSize = (1 << 0),
+ flagsExternal = (1 << 1)
};
/// The compile unit that owns this function.
diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h
index 81ef424275271a..ca649743a64051 100644
--- a/lldb/include/lldb/Symbol/ObjectFile.h
+++ b/lldb/include/lldb/Symbol/ObjectFile.h
@@ -24,6 +24,7 @@
#include "lldb/lldb-private.h"
#include "llvm/Support/Threading.h"
#include "llvm/Support/VersionTuple.h"
+#include "llvm/TargetParser/Triple.h"
#include <optional>
namespace lldb_private {
@@ -293,6 +294,11 @@ class ObjectFile : public std::enable_shared_from_this<ObjectFile>,
/// ArchSpec object is returned.
virtual ArchSpec GetArchitecture() = 0;
+ /// Return the file format, or UnknownObjectFormat if not reported.
+ virtual llvm::Triple::ObjectFormatType GetFileFormat() const {
+ return llvm::Triple::UnknownObjectFormat;
+ }
+
/// Gets the section list for the currently selected architecture (and
/// object for archives).
///
diff --git a/lldb/source/Expression/Expression.cpp b/lldb/source/Expression/Expression.cpp
index 2fe0a8c743ef55..fd658ea3ae6c67 100644
--- a/lldb/source/Expression/Expression.cpp
+++ b/lldb/source/Expression/Expression.cpp
@@ -7,9 +7,13 @@
//===----------------------------------------------------------------------===//
#include "lldb/Expression/Expression.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/ExecutionContextScope.h"
#include "lldb/Target/Target.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
@@ -18,6 +22,52 @@
using namespace lldb_private;
+std::vector<Symbol *> lldb_private::FindELFFunctionSymbolsWithName(
+ Target &target, const lldb::ModuleSP &module, ConstString name) {
+ if (!module || module != target.GetExecutableModule())
+ return {};
+ ObjectFile *object = module->GetObjectFile();
+ if (!object || object->GetFileFormat() != llvm::Triple::ELF)
+ return {};
+
+ // Query eSymbolTypeCode only; IFUNC resolvers (eSymbolTypeResolver) do not
+ // carry the final target address.
+ SymbolContextList candidates;
+ module->FindSymbolsWithNameAndType(name, lldb::eSymbolTypeCode, candidates);
+ std::vector<Symbol *> matches;
+ // Name lookup also matches C++ basenames; require an exact symbol name.
+ for (const SymbolContext &context : candidates) {
+ Symbol *symbol = context.symbol;
+ if (symbol->GetMangled().GetName(Mangled::ePreferMangled) == name)
+ matches.push_back(symbol);
+ }
+ return matches;
+}
+
+Symbol *
+lldb_private::FindSymbolAtFileAddress(llvm::ArrayRef<Symbol *> name_matches,
+ lldb::addr_t file_address) {
+ auto match = llvm::find_if(name_matches, [&](Symbol *symbol) {
+ return symbol->GetFileAddress() == file_address;
+ });
+ return match == name_matches.end() ? nullptr : *match;
+}
+
+Symbol *lldb_private::FindUniqueExternalFunctionSymbol(
+ llvm::ArrayRef<Symbol *> symbols) {
+ Symbol *selected = nullptr;
+ for (Symbol *symbol : symbols) {
+ // ELF weak symbols have IsWeak() set, rather than IsExternal().
+ if ((!symbol->IsExternal() && !symbol->IsWeak()) ||
+ symbol->GetFileAddress() == LLDB_INVALID_ADDRESS)
+ continue;
+ if (selected && selected->GetFileAddress() != symbol->GetFileAddress())
+ return nullptr;
+ selected = symbol;
+ }
+ return selected;
+}
+
Expression::Expression(Target &target)
: m_target_wp(target.shared_from_this()),
m_jit_start_addr(LLDB_INVALID_ADDRESS),
diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp
index 17e131512ef0f6..c3de65219ea960 100644
--- a/lldb/source/Expression/IRExecutionUnit.cpp
+++ b/lldb/source/Expression/IRExecutionUnit.cpp
@@ -26,6 +26,7 @@
#include "lldb/Expression/ObjectFileJIT.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/SymbolFile.h"
#include "lldb/Symbol/SymbolVendor.h"
@@ -615,6 +616,23 @@ class LoadAddressResolver {
m_symbol_was_missing_weak = true;
for (auto candidate_sc : sc_list.SymbolContexts()) {
+ if (!candidate_sc.symbol && candidate_sc.function &&
+ candidate_sc.function->IsExternal()) {
+ Function *function = candidate_sc.function;
+ auto symbols = FindELFFunctionSymbolsWithName(
+ m_target, function->GetAddress().GetModule(),
+ function->GetMangled().GetName(Mangled::ePreferMangled));
+ // When a FunctionCallLabel resolves to a DWARF Function,
+ // candidate_sc.symbol is not set. Prefer a symbol at the function's
+ // own address; otherwise (e.g. an overridden weak DIE kept for its
+ // type when the strong definition has no DWARF), use the winning ELF
+ // symbol.
+ candidate_sc.symbol = FindSymbolAtFileAddress(
+ symbols, function->GetAddress().GetFileAddress());
+ if (!candidate_sc.symbol)
+ candidate_sc.symbol = FindUniqueExternalFunctionSymbol(symbols);
+ }
+
// Only symbols can be weak undefined.
if (!candidate_sc.symbol ||
candidate_sc.symbol->GetType() != lldb::eSymbolTypeUndefined ||
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
index 351afdbbb2609d..517fda86808b6f 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
@@ -22,12 +22,14 @@
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/Expression.h"
#include "lldb/Expression/Materializer.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/CompilerDecl.h"
#include "lldb/Symbol/CompilerDeclContext.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/SymbolFile.h"
#include "lldb/Symbol/SymbolVendor.h"
@@ -57,9 +59,12 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/RecursiveASTVisitor.h"
+#include "llvm/ADT/DenseSet.h"
#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+#include <tuple>
+
using namespace lldb;
using namespace lldb_private;
using namespace clang;
@@ -67,6 +72,48 @@ using namespace clang;
static const char *g_lldb_local_vars_namespace_cstr = "$__lldb_local_vars";
namespace {
+SymbolContextList
+FilterOverriddenELFFunctions(Target &target,
+ const SymbolContextList &functions) {
+ llvm::DenseSet<std::tuple<lldb_private::Module *, ConstString, addr_t>>
+ definitions;
+ for (const SymbolContext &context : functions) {
+ Function *function = context.function;
+ if (function && function->IsExternal()) {
+ definitions.insert(
+ {context.module_sp.get(),
+ function->GetMangled().GetName(Mangled::ePreferMangled),
+ function->GetAddress().GetFileAddress()});
+ }
+ }
+
+ SymbolContextList filtered;
+ for (const SymbolContext &context : functions) {
+ Function *function = context.function;
+ if (function && function->IsExternal()) {
+ ConstString name =
+ function->GetMangled().GetName(Mangled::ePreferMangled);
+ auto symbols = FindELFFunctionSymbolsWithName(
+ target, function->GetAddress().GetModule(), name);
+ Symbol *selected = FindUniqueExternalFunctionSymbol(symbols);
+ // Drop this DWARF candidate if:
+ // 1. The name resolves to a unique external/weak ELF symbol address,
+ // 2. This candidate has no symbol of its own at its address (neither the
+ // selected symbol nor a localized symbol), and
+ // 3. Another DWARF candidate exists at the selected address (otherwise,
+ // keep this candidate so its declaration still provides the type).
+ if (selected &&
+ !FindSymbolAtFileAddress(symbols,
+ function->GetAddress().GetFileAddress()) &&
+ definitions.contains(
+ {context.module_sp.get(), name, selected->GetFileAddress()}))
+ continue;
+ }
+ filtered.Append(context);
+ }
+ return filtered;
+}
+
/// A lambda is represented by Clang as an artifical class whose
/// members are the lambda captures. If we capture a 'this' pointer,
/// the artifical class will contain a member variable named 'this'.
@@ -643,6 +690,10 @@ lldb::VariableSP ClangExpressionDeclMap::FindGlobalVariable(
if (vars.GetSize() == 0)
return VariableSP();
+
+ // FIXME: This blindly picks the first match without checking ELF symbol
+ // binding. For overridden weak variables, this currently works by accident
+ // only when the weak DIE's DW_AT_location resolves to the strong storage.
return vars.GetVariableAtIndex(0);
}
@@ -1281,6 +1332,11 @@ bool ClangExpressionDeclMap::LookupFunction(
sc_list);
}
+ // Select the linked definition before scope filtering or type deduplication
+ // can discard it in favor of a retained, overridden definition.
+ if (target)
+ sc_list = FilterOverriddenELFFunctions(*target, sc_list);
+
// If we found more than one function, see if we can use the frame's decl
// context to remove functions that are shadowed by other functions which
// match in type but are nearer in scope.
@@ -1833,6 +1889,7 @@ void ClangExpressionDeclMap::AddOneFunction(NameSearchContext &context,
CompilerType function_clang_type;
bool is_indirect_function = false;
+ Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr();
if (function) {
Type *function_type = function->GetType();
@@ -1926,6 +1983,19 @@ void ClangExpressionDeclMap::AddOneFunction(NameSearchContext &context,
return;
}
+
+ // Keep the selected declaration through JIT lookup. Do not pin calls
+ // into shared libraries, which may be overridden by another image.
+ ModuleSP module = function->GetCompileUnit()->GetModule();
+ if (module == target->GetExecutableModule() &&
+ module->GetObjectFile()->GetFileFormat() == llvm::Triple::ELF) {
+ ConstString name =
+ function->GetMangled().GetName(Mangled::ePreferMangled);
+ FunctionCallLabel label{
+ {}, module->GetID(), function->GetID(), name.GetStringRef()};
+ function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
+ *m_ast_context, label.toString()));
+ }
} else {
// We failed to copy the type we found
LLDB_LOG(log,
@@ -1944,8 +2014,6 @@ void ClangExpressionDeclMap::AddOneFunction(NameSearchContext &context,
return;
}
- Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr();
-
lldb::addr_t load_addr =
fun_address.GetCallableLoadAddress(target, is_indirect_function);
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp b/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp
index 794b194ec47d5c..2e05c708febcea 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp
@@ -42,6 +42,8 @@ clang::NamedDecl *NameSearchContext::AddFunDecl(const CompilerType &type,
if (!type.IsValid())
return nullptr;
+ // FIXME: Deduplicating by function type alone assumes all declarations with
+ // the same signature refer to the same function, hiding other definitions.
if (m_function_types.count(type))
return nullptr;
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
index 8adb8af10fe169..8dc6e3d7266c5e 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
@@ -120,6 +120,10 @@ class ObjectFileELF : public lldb_private::ObjectFile {
lldb_private::ArchSpec GetArchitecture() override;
+ llvm::Triple::ObjectFormatType GetFileFormat() const override {
+ return llvm::Triple::ELF;
+ }
+
lldb_private::UUID GetUUID() override;
/// Return the contents of the .gnu_debuglink section, if the object file
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 41e3372584d4f4..f6640812cbb113 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -945,7 +945,12 @@ Function *SymbolFileDWARF::ParseFunction(CompileUnit &comp_unit,
if (ranges.empty())
return nullptr;
- return dwarf_ast->ParseFunctionFromDWARF(comp_unit, die, std::move(ranges));
+ Function *function =
+ dwarf_ast->ParseFunctionFromDWARF(comp_unit, die, std::move(ranges));
+ if (function)
+ function->SetIsExternal(die.GetDIE()->GetAttributeValueAsUnsigned(
+ die.GetCU(), DW_AT_external, 0, /*check_elaborating_dies=*/true));
+ return function;
}
ConstString
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/Makefile b/lldb/test/API/commands/expression/overridden-weak-symbols/Makefile
new file mode 100644
index 00000000000000..ac3216ed92fd1b
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/Makefile
@@ -0,0 +1,10 @@
+C_SOURCES := main.c weak.c
+include Makefile.rules
+
+no-debug-strong.o: CFLAGS += -g0
+symbol-only.o: CFLAGS += -g0
+shared-strong.o: CFLAGS += -g0
+
+localized.o: localized-input.c
+ $(CC) $(CFLAGS) -c $< -o $@
+ $(OBJCOPY) --localize-symbol=action $@
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/TestOverriddenWeakSymbols.py b/lldb/test/API/commands/expression/overridden-weak-symbols/TestOverriddenWeakSymbols.py
new file mode 100644
index 00000000000000..d706ba925300c0
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/TestOverriddenWeakSymbols.py
@@ -0,0 +1,155 @@
+import lldb
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import (
+ requireExpressionEvaluation,
+ skipUnlessPlatform,
+)
+from lldbsuite.test.lldbtest import TestBase, line_number
+
+
+ at skipUnlessPlatform(["linux"])
+ at requireExpressionEvaluation
+class OverriddenWeakSymbolsTest(TestBase):
+ SHARED_BUILD_TESTCASE = False
+
+ def check_functions(self, cxx, reverse, pie, both_weak=False):
+ extension = "cpp" if cxx else "c"
+ prefix = "cxx-" if cxx else ""
+ sources = [f"{prefix}main.{extension}", f"{prefix}weak.{extension}"]
+ if reverse:
+ sources.reverse()
+ cflags = "-fPIE" if pie else "-fno-pie"
+ if both_weak:
+ cflags += " -DBOTH_WEAK"
+ self.build(
+ dictionary={
+ "C_SOURCES": "" if cxx else " ".join(sources),
+ "CXX_SOURCES": " ".join(sources) if cxx else "",
+ "CFLAGS_EXTRAS": cflags,
+ "LD_EXTRAS": "-pie" if pie else "-no-pie",
+ }
+ )
+ target, process, thread, _ = lldbutil.run_to_name_breakpoint(
+ self, "weak_context"
+ )
+ # Check both return value (11 vs 22) and return type (StrongResult vs
+ # WeakResult) to verify that the linked definition's DIE was selected.
+ self.expect_expr("action()", result_type="StrongResult", result_value="11")
+ self.expect_expr("other_action()", result_value="13")
+ self.expect_expr("weak_only()", result_value="7")
+ self.expect_expr("local_only()", result_value="77")
+ self.expect_expr("scoped_action()", result_value="77")
+ self.expect_expr("value", result_value="11")
+
+ pointer_breakpoint = target.BreakpointCreateByLocation(
+ "weak.c", line_number("weak.c", "lookup in pointer scope")
+ )
+ self.assertEqual(pointer_breakpoint.GetNumLocations(), 1)
+ process.Continue()
+ self.expect_expr("action()", result_value="77")
+ self.expect_expr("value", result_value="99")
+ target.BreakpointDelete(pointer_breakpoint.GetID())
+
+ breakpoint = target.BreakpointCreateByName("main_context")
+ self.assertEqual(breakpoint.GetNumLocations(), 1)
+ process.Continue()
+ self.expect_expr("observed_action", result_value="11")
+ self.expect_expr("observed_other", result_value="13")
+ self.expect_expr("action()", result_type="StrongResult", result_value="11")
+ self.expect_expr("other_action()", result_value="13")
+ self.expect_expr("&action == program_action", result_value="true")
+ self.expect_expr("&value == program_value", result_value="true")
+ self.expect_expr("scoped_action(0)", result_value="66")
+ if not cxx:
+ # The overridden body in main.c is still callable via its alias.
+ self.expect_expr("(int)retained_action()", result_value="22")
+ if cxx:
+ self.expect_expr("ns::selected()", result_value="33")
+ self.expect_expr("ns::selected(41)", result_value="42")
+ self.expect_expr("value_ref", result_value="11")
+ self.expect_expr("&value_ref == program_value", result_value="true")
+ self.expect_expr("value_ref = 17", result_value="17")
+ self.expect_expr("*program_value", result_value="17")
+
+ # The retained body remains available to an explicit address query.
+ matches = target.FindFunctions("action", lldb.eFunctionNameTypeBase)
+ for context in matches:
+ function = context.GetFunction()
+ if (
+ function.IsValid()
+ and context.GetCompileUnit().GetFileSpec().GetFilename()
+ == f"{prefix}main.{extension}"
+ and function.GetName() in ("action", "action()")
+ ):
+ address = function.GetStartAddress().GetLoadAddress(target)
+ self.expect_expr(f"((int (*)(void)){address})()", result_value="22")
+ break
+ else:
+ self.fail("Retained weak function is missing")
+
+ def test_c(self):
+ self.check_functions(cxx=False, reverse=False, pie=False)
+
+ def test_c_reverse_pie(self):
+ self.check_functions(cxx=False, reverse=True, pie=True)
+
+ def test_cxx(self):
+ self.check_functions(cxx=True, reverse=False, pie=False)
+
+ def test_cxx_reverse_pie(self):
+ self.check_functions(cxx=True, reverse=True, pie=True)
+
+ def test_two_weak_definitions(self):
+ # The selected ELF definition can itself be weak.
+ self.check_functions(cxx=False, reverse=True, pie=False, both_weak=True)
+
+ def test_selected_function_without_debug_info(self):
+ self.build(dictionary={"C_SOURCES": "no-debug-main.c no-debug-strong.c"})
+ lldbutil.run_to_name_breakpoint(self, "context")
+ # The visible declaration still supplies the type when the linked
+ # definition has no debug info.
+ self.expect_expr("action()", result_type="Result", result_value="11")
+ self.expect_expr("(int)action()", result_value="11")
+ self.expect_expr("program_action()", result_value="11")
+ self.expect_expr("&action == program_action", result_value="true")
+
+ def test_symbol_only_call(self):
+ self.build(dictionary={"C_SOURCES": "symbol-only.c"})
+ lldbutil.run_to_name_breakpoint(self, "context")
+ self.expect_expr("(int)action()", result_value="11")
+
+ def test_shared_library_override(self):
+ self.build(
+ dictionary={
+ "C_SOURCES": "shared-main.c shared-strong.c",
+ "DYLIB_C_SOURCES": "shared-weak.c",
+ "DYLIB_NAME": "weak",
+ "LD_EXTRAS": "-Wl,--export-dynamic",
+ }
+ )
+ lldbutil.run_to_name_breakpoint(
+ self, "main_context", extra_images=["libweak.so"]
+ )
+ # Only the shared library provides a function type, but the executable
+ # overrides that definition. Do not pin calls to the type's module.
+ self.expect_expr("action()", result_value="11")
+ self.expect_expr("observed_action", result_value="11")
+ self.expect_expr("program_action()", result_value="11")
+ self.expect_expr("&action == program_action", result_value="true")
+
+ def test_localized_function(self):
+ self.build(
+ dictionary={
+ "C_SOURCES": "localized-main.c",
+ "OBJECTS": "localized.o localized-main.o",
+ }
+ )
+ lldbutil.run_to_name_breakpoint(self, "local_context")
+ # `objcopy --localize-symbol` turns `action` into a local ELF symbol in
+ # localized.o while its DWARF DIE still has DW_AT_external. Evaluating
+ # `action()` in `local_context` must call the local definition (22), not
+ # the global one (11).
+ self.expect_expr("action()", result_value="22")
+ self.expect_expr("local_action()", result_value="22")
+ self.expect_expr("global_action()", result_value="11")
+ self.expect_expr("&action == local_action", result_value="true")
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/cxx-main.cpp b/lldb/test/API/commands/expression/overridden-weak-symbols/cxx-main.cpp
new file mode 100644
index 00000000000000..0f793e206f401e
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/cxx-main.cpp
@@ -0,0 +1 @@
+#include "main.c"
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/cxx-weak.cpp b/lldb/test/API/commands/expression/overridden-weak-symbols/cxx-weak.cpp
new file mode 100644
index 00000000000000..95317daa6b43ed
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/cxx-weak.cpp
@@ -0,0 +1 @@
+#include "weak.c"
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/localized-input.c b/lldb/test/API/commands/expression/overridden-weak-symbols/localized-input.c
new file mode 100644
index 00000000000000..0e23b10766909a
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/localized-input.c
@@ -0,0 +1,10 @@
+int action(void) { return 22; }
+int (*local_action)(void) = action;
+
+void local_context(void) {}
+
+int local_call(void) {
+ int result = action();
+ local_context();
+ return result;
+}
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/localized-main.c b/lldb/test/API/commands/expression/overridden-weak-symbols/localized-main.c
new file mode 100644
index 00000000000000..013f214d4d1b98
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/localized-main.c
@@ -0,0 +1,5 @@
+int action(void) { return 11; }
+int (*global_action)(void) = action;
+extern int local_call(void);
+
+int main(void) { return local_call() != 22 || action() != 11; }
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/main.c b/lldb/test/API/commands/expression/overridden-weak-symbols/main.c
new file mode 100644
index 00000000000000..925b43293a5954
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/main.c
@@ -0,0 +1,34 @@
+// Paired with weak.c: `action` is weak and `other_action` is strong.
+typedef int WeakResult;
+__attribute__((weak, noinline)) WeakResult action(void) { return 22; }
+#ifndef __cplusplus
+extern __typeof(action) retained_action __attribute__((alias("action")));
+#endif
+__attribute__((noinline)) int other_action(void) { return 13; }
+__attribute__((noinline)) int scoped_action(int n) { return 66 + n; }
+
+int value = 11;
+int (*program_action)(void) = action;
+int *program_value = &value;
+int observed_action, observed_other;
+
+void weak_context(void);
+int weak_only(void);
+__attribute__((noinline)) void main_context(void) {}
+
+#ifdef __cplusplus
+int &value_ref = value;
+
+namespace ns {
+ __attribute__((weak, noinline)) int selected(void) { return 32; }
+ __attribute__((noinline)) int selected(int n) { return n + 1; }
+} // namespace ns
+#endif
+
+int main(void) {
+ observed_action = action();
+ observed_other = other_action();
+ weak_context();
+ main_context();
+ return 0;
+}
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/no-debug-main.c b/lldb/test/API/commands/expression/overridden-weak-symbols/no-debug-main.c
new file mode 100644
index 00000000000000..a7e550266cdff2
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/no-debug-main.c
@@ -0,0 +1,8 @@
+typedef int Result;
+__attribute__((weak, noinline)) Result action(void) { return 22; }
+int (*program_action)(void) = action;
+void context(void) {}
+int main(void) {
+ context();
+ return action() != 11;
+}
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/no-debug-strong.c b/lldb/test/API/commands/expression/overridden-weak-symbols/no-debug-strong.c
new file mode 100644
index 00000000000000..0da608e59e7e8b
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/no-debug-strong.c
@@ -0,0 +1 @@
+int action(void) { return 11; }
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/shared-main.c b/lldb/test/API/commands/expression/overridden-weak-symbols/shared-main.c
new file mode 100644
index 00000000000000..ac89c5a97983c9
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/shared-main.c
@@ -0,0 +1,12 @@
+extern int action(void);
+extern int library_call(void);
+int (*program_action)(void) = action;
+int observed_action;
+
+void main_context(void) {}
+
+int main(void) {
+ observed_action = library_call();
+ main_context();
+ return observed_action != 11;
+}
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/shared-strong.c b/lldb/test/API/commands/expression/overridden-weak-symbols/shared-strong.c
new file mode 100644
index 00000000000000..0da608e59e7e8b
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/shared-strong.c
@@ -0,0 +1 @@
+int action(void) { return 11; }
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/shared-weak.c b/lldb/test/API/commands/expression/overridden-weak-symbols/shared-weak.c
new file mode 100644
index 00000000000000..d9c086067cac5e
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/shared-weak.c
@@ -0,0 +1,2 @@
+__attribute__((weak, noinline)) int action(void) { return 22; }
+int library_call(void) { return action(); }
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/symbol-only.c b/lldb/test/API/commands/expression/overridden-weak-symbols/symbol-only.c
new file mode 100644
index 00000000000000..27139c81ed4580
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/symbol-only.c
@@ -0,0 +1,8 @@
+int action(void) { return 11; }
+
+__attribute__((noinline)) void context(void) {}
+
+int main(void) {
+ context();
+ return action() != 11;
+}
diff --git a/lldb/test/API/commands/expression/overridden-weak-symbols/weak.c b/lldb/test/API/commands/expression/overridden-weak-symbols/weak.c
new file mode 100644
index 00000000000000..7dd74ad9bf0d2a
--- /dev/null
+++ b/lldb/test/API/commands/expression/overridden-weak-symbols/weak.c
@@ -0,0 +1,35 @@
+// Paired with main.c to test weak overrides in both CU orders:
+// `action` is strong (or weak under BOTH_WEAK) and `other_action` is weak.
+typedef int StrongResult;
+#ifdef BOTH_WEAK
+__attribute__((weak))
+#endif
+__attribute__((noinline)) StrongResult action(void) {
+ return 11;
+}
+__attribute__((weak, noinline)) int other_action(void) { return 23; }
+__attribute__((weak, noinline)) int weak_only(void) { return 7; }
+__attribute__((weak)) int value = 22;
+
+static int local_only(void) { return 77; }
+static int scoped_action(void) { return 77; }
+
+__attribute__((noinline)) void pointer_context(void) {
+ int (*action)(void) = local_only;
+ int value = 99;
+ volatile int result = action() + value; // lookup in pointer scope
+ (void)result;
+}
+
+#ifdef __cplusplus
+namespace ns {
+ __attribute__((noinline)) int selected(void) { return 33; }
+} // namespace ns
+#endif
+
+void weak_context(void) {
+ int local_value = local_only();
+ local_value += scoped_action();
+ local_value += 1; // break here
+ pointer_context();
+}
diff --git a/lldb/unittests/Expression/ExpressionTest.cpp b/lldb/unittests/Expression/ExpressionTest.cpp
index 21ac8a31bd82df..5cdff2d57dafaa 100644
--- a/lldb/unittests/Expression/ExpressionTest.cpp
+++ b/lldb/unittests/Expression/ExpressionTest.cpp
@@ -11,11 +11,51 @@
#include "TestingSupport/TestUtilities.h"
#include "lldb/Expression/Expression.h"
+#include "lldb/Symbol/Symbol.h"
#include "lldb/Target/Target.h"
#include "llvm/Testing/Support/Error.h"
using namespace lldb_private;
+TEST(ExpressionTests, UniqueExternalFunctionSymbol) {
+ auto section = std::make_shared<Section>(lldb::ModuleSP(), nullptr, 1,
+ ".text", lldb::eSectionTypeCode,
+ 0x1000, 0x100, 0, 0x100, 0, 0);
+ auto make_symbol = [&](bool external, lldb::addr_t offset) {
+ return Symbol(0, "action", lldb::eSymbolTypeCode, external, false, false,
+ false, section, offset, 1, true, false, 0);
+ };
+ Symbol local = make_symbol(false, 0);
+ Symbol global = make_symbol(true, 0x10);
+ Symbol duplicate = make_symbol(true, 0x10);
+ Symbol other = make_symbol(true, 0x20);
+ Symbol weak = make_symbol(false, 0x30);
+ weak.SetIsWeak(true);
+ Symbol invalid;
+ invalid.SetExternal(true);
+
+ EXPECT_EQ(FindUniqueExternalFunctionSymbol({}), nullptr);
+ EXPECT_EQ(FindUniqueExternalFunctionSymbol({&local, &invalid}), nullptr);
+ EXPECT_EQ(FindUniqueExternalFunctionSymbol({&global, &local}), &global);
+ EXPECT_EQ(FindUniqueExternalFunctionSymbol({&local, &global}), &global);
+ Symbol *selected = FindUniqueExternalFunctionSymbol({&global, &duplicate});
+ ASSERT_NE(selected, nullptr);
+ EXPECT_EQ(selected->GetFileAddress(), global.GetFileAddress());
+ EXPECT_EQ(FindUniqueExternalFunctionSymbol({&global, &other}), nullptr);
+ EXPECT_EQ(FindUniqueExternalFunctionSymbol({&other, &global}), nullptr);
+ EXPECT_EQ(FindUniqueExternalFunctionSymbol({&weak, &local}), &weak);
+ EXPECT_EQ(FindUniqueExternalFunctionSymbol({&weak, &global}), nullptr);
+
+ EXPECT_EQ(FindSymbolAtFileAddress({&global, &local}, 0x1000), &local);
+
+ Symbol *match = FindSymbolAtFileAddress({&global, &duplicate}, 0x1010);
+ ASSERT_NE(match, nullptr);
+ EXPECT_EQ(match->GetFileAddress(), 0x1010u);
+ match = FindSymbolAtFileAddress({&duplicate, &global}, 0x1010);
+ ASSERT_NE(match, nullptr);
+ EXPECT_EQ(match->GetFileAddress(), 0x1010u);
+}
+
struct LabelTestCase {
llvm::StringRef encoded;
FunctionCallLabel label;
More information about the lldb-commits
mailing list