[llvm-branch-commits] [lldb] [lldb] Use linkage names for primary JIT symbol lookup (PR #232697)

Karim Alweheshy via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Oct 16 05:00:10 PDT 2026


https://github.com/karim-alweheshy updated https://github.com/llvm/llvm-project/pull/232697

>From 5d83cb4e1674a04a7c44ede5734d425defd214f0 Mon Sep 17 00:00:00 2001
From: Karim Alweheshy <karim.alweheshy at reddit.com>
Date: Fri, 16 Oct 2026 13:00:41 +0200
Subject: [PATCH] [lldb] Use linkage names for primary JIT symbol lookup

Resolve original JIT symbol requests with the exact linkage-name path while retaining source-name lookup for function-call-label and alternate C++ fallbacks. Preserve module order, platform exclusions, weak-symbol handling, and trampoline filtering.

Assisted-by: OpenAI Codex
---
 .../include/lldb/Expression/IRExecutionUnit.h |  3 +-
 lldb/source/Expression/IRExecutionUnit.cpp    | 51 +++++++++----
 .../expression/linkage_lookup/Makefile        | 10 +++
 .../TestIRExecutionUnitLinkageLookup.py       | 75 +++++++++++++++++++
 .../expression/linkage_lookup/decoy.cpp       |  5 ++
 .../expression/linkage_lookup/main.cpp        | 10 +++
 .../expression/linkage_lookup/probe.cpp       | 10 +++
 7 files changed, 150 insertions(+), 14 deletions(-)
 create mode 100644 lldb/test/API/commands/expression/linkage_lookup/Makefile
 create mode 100644 lldb/test/API/commands/expression/linkage_lookup/TestIRExecutionUnitLinkageLookup.py
 create mode 100644 lldb/test/API/commands/expression/linkage_lookup/decoy.cpp
 create mode 100644 lldb/test/API/commands/expression/linkage_lookup/main.cpp
 create mode 100644 lldb/test/API/commands/expression/linkage_lookup/probe.cpp

diff --git a/lldb/include/lldb/Expression/IRExecutionUnit.h b/lldb/include/lldb/Expression/IRExecutionUnit.h
index 6ba0dc52d3db16..f8cf310be1d26c 100644
--- a/lldb/include/lldb/Expression/IRExecutionUnit.h
+++ b/lldb/include/lldb/Expression/IRExecutionUnit.h
@@ -224,7 +224,8 @@ class IRExecutionUnit : public std::enable_shared_from_this<IRExecutionUnit>,
 
   lldb::addr_t FindInSymbols(const std::vector<ConstString> &names,
                              const lldb_private::SymbolContext &sc,
-                             bool &symbol_was_missing_weak);
+                             bool &symbol_was_missing_weak,
+                             bool lookup_by_linkage);
 
   lldb::addr_t FindInRuntimes(const std::vector<ConstString> &names,
                               const lldb_private::SymbolContext &sc);
diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp
index 17e131512ef0f6..95d5df967fb4b1 100644
--- a/lldb/source/Expression/IRExecutionUnit.cpp
+++ b/lldb/source/Expression/IRExecutionUnit.cpp
@@ -716,7 +716,8 @@ ResolveFunctionCallLabel(FunctionCallLabel &label,
 lldb::addr_t
 IRExecutionUnit::FindInSymbols(const std::vector<ConstString> &names,
                                const lldb_private::SymbolContext &sc,
-                               bool &symbol_was_missing_weak) {
+                               bool &symbol_was_missing_weak,
+                               bool lookup_by_linkage) {
   symbol_was_missing_weak = false;
 
   Target *target = sc.target_sp.get();
@@ -745,6 +746,24 @@ IRExecutionUnit::FindInSymbols(const std::vector<ConstString> &names,
   ModuleFunctionSearchOptions function_options;
   function_options.include_symbols = true;
   function_options.include_inlines = false;
+  function_options.match_linkage_names = lookup_by_linkage;
+
+  auto resolve = [&](SymbolContextList &sc_list) {
+    if (lookup_by_linkage) {
+      // General linkage queries retain named trampolines. The ordinary name
+      // indexes exclude them, so retain that exclusion for JIT resolution.
+      for (size_t i = sc_list.GetSize(); i > 0; --i) {
+        const SymbolContext &context = sc_list[i - 1];
+        if (!context.symbol || !context.symbol->IsTrampoline())
+          continue;
+        if (context.function)
+          sc_list.SetSymbolAtIndex(i - 1, nullptr);
+        else
+          sc_list.RemoveContextAtIndex(i - 1);
+      }
+    }
+    return resolver.Resolve(sc_list);
+  };
 
   for (const ConstString &name : names) {
     // The lookup order here is as follows:
@@ -759,7 +778,7 @@ IRExecutionUnit::FindInSymbols(const std::vector<ConstString> &names,
       sc.module_sp->FindFunctions(name, CompilerDeclContext(),
                                   lldb::eFunctionNameTypeFull, function_options,
                                   sc_list);
-      if (auto load_addr = resolver.Resolve(sc_list))
+      if (auto load_addr = resolve(sc_list))
         return *load_addr;
     }
 
@@ -767,7 +786,7 @@ IRExecutionUnit::FindInSymbols(const std::vector<ConstString> &names,
       SymbolContextList sc_list;
       m_preferred_modules.FindFunctions(name, lldb::eFunctionNameTypeFull,
                                         function_options, sc_list);
-      if (auto load_addr = resolver.Resolve(sc_list))
+      if (auto load_addr = resolve(sc_list))
         return *load_addr;
     }
 
@@ -775,31 +794,31 @@ IRExecutionUnit::FindInSymbols(const std::vector<ConstString> &names,
       SymbolContextList sc_list;
       non_local_images.FindFunctions(name, lldb::eFunctionNameTypeFull,
                                      function_options, sc_list);
-      if (auto load_addr = resolver.Resolve(sc_list))
+      if (auto load_addr = resolve(sc_list))
         return *load_addr;
     }
 
     if (sc.module_sp) {
       SymbolContextList sc_list;
       sc.module_sp->FindSymbolsWithNameAndType(name, lldb::eSymbolTypeAny,
-                                               sc_list);
-      if (auto load_addr = resolver.Resolve(sc_list))
+                                               sc_list, lookup_by_linkage);
+      if (auto load_addr = resolve(sc_list))
         return *load_addr;
     }
 
     {
       SymbolContextList sc_list;
-      m_preferred_modules.FindSymbolsWithNameAndType(name, lldb::eSymbolTypeAny,
-                                                     sc_list);
-      if (auto load_addr = resolver.Resolve(sc_list))
+      m_preferred_modules.FindSymbolsWithNameAndType(
+          name, lldb::eSymbolTypeAny, sc_list, lookup_by_linkage);
+      if (auto load_addr = resolve(sc_list))
         return *load_addr;
     }
 
     {
       SymbolContextList sc_list;
       non_local_images.FindSymbolsWithNameAndType(name, lldb::eSymbolTypeAny,
-                                                  sc_list);
-      if (auto load_addr = resolver.Resolve(sc_list))
+                                                  sc_list, lookup_by_linkage);
+      if (auto load_addr = resolve(sc_list))
         return *load_addr;
     }
 
@@ -856,6 +875,9 @@ lldb::addr_t IRExecutionUnit::FindInUserDefinedSymbols(
 
 lldb::addr_t IRExecutionUnit::FindSymbol(lldb_private::ConstString name,
                                          bool &missing_weak) {
+  // Original JIT/interpreter requests use linkage spellings. Source names
+  // recovered from function-call labels retain their compatibility lookup.
+  bool lookup_by_linkage = true;
   if (name.GetStringRef().starts_with(FunctionCallLabelPrefix)) {
     auto label_or_err = FunctionCallLabel::fromString(name);
     if (!label_or_err) {
@@ -878,6 +900,7 @@ lldb::addr_t IRExecutionUnit::FindSymbol(lldb_private::ConstString name,
       // 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);
+      lookup_by_linkage = false;
     }
   }
 
@@ -889,7 +912,8 @@ lldb::addr_t IRExecutionUnit::FindSymbol(lldb_private::ConstString name,
 
   CollectCandidateCNames(candidate_C_names, name);
 
-  lldb::addr_t ret = FindInSymbols(candidate_C_names, m_sym_ctx, missing_weak);
+  lldb::addr_t ret = FindInSymbols(candidate_C_names, m_sym_ctx, missing_weak,
+                                   lookup_by_linkage);
   if (ret != LLDB_INVALID_ADDRESS)
     return ret;
 
@@ -906,7 +930,8 @@ lldb::addr_t IRExecutionUnit::FindSymbol(lldb_private::ConstString name,
 
   CollectCandidateCPlusPlusNames(candidate_CPlusPlus_names, candidate_C_names,
                                  m_sym_ctx);
-  ret = FindInSymbols(candidate_CPlusPlus_names, m_sym_ctx, missing_weak);
+  ret = FindInSymbols(candidate_CPlusPlus_names, m_sym_ctx, missing_weak,
+                      /*lookup_by_linkage=*/false);
   return ret;
 }
 
diff --git a/lldb/test/API/commands/expression/linkage_lookup/Makefile b/lldb/test/API/commands/expression/linkage_lookup/Makefile
new file mode 100644
index 00000000000000..c3058836fd41fe
--- /dev/null
+++ b/lldb/test/API/commands/expression/linkage_lookup/Makefile
@@ -0,0 +1,10 @@
+CXX_SOURCES := main.cpp decoy.cpp
+DYLIB_NAME := LinkageProbe
+DYLIB_CXX_SOURCES := probe.cpp
+LD_EXTRAS := -Wl,-U,_missing_weak
+
+include Makefile.rules
+
+# Keep the decoys in the symbol table without adding DWARF functions. This
+# isolates the JIT's symbol-name lookup from the debug-info function lookup.
+decoy.o: CXXFLAGS += -g0
diff --git a/lldb/test/API/commands/expression/linkage_lookup/TestIRExecutionUnitLinkageLookup.py b/lldb/test/API/commands/expression/linkage_lookup/TestIRExecutionUnitLinkageLookup.py
new file mode 100644
index 00000000000000..1cf80f9e8072bf
--- /dev/null
+++ b/lldb/test/API/commands/expression/linkage_lookup/TestIRExecutionUnitLinkageLookup.py
@@ -0,0 +1,75 @@
+"""Keep primary JIT linkage lookup separate from compatibility aliases."""
+
+import lldb
+from lldbsuite.test.decorators import skipUnlessDarwin
+from lldbsuite.test.lldbtest import TestBase
+from lldbsuite.test import lldbutil
+
+
+class TestIRExecutionUnitLinkageLookup(TestBase):
+    def launch(self):
+        self.build()
+        self.runCmd("settings set target.preload-symbols false")
+        self.runCmd("settings set target.load-script-from-symbol-file false")
+        self.target, _, thread, _ = lldbutil.run_to_source_breakpoint(
+            self, "break here", lldb.SBFileSpec("main.cpp")
+        )
+        self.frame = thread.frames[0]
+
+    def value(self, expression, declarations=""):
+        options = lldb.SBExpressionOptions()
+        options.SetLanguage(lldb.eLanguageTypeC_plus_plus)
+        options.SetPrefix(declarations)
+        result = self.frame.EvaluateExpression(expression, options)
+        self.assertSuccess(result.GetError())
+        return result.GetValueAsSigned()
+
+    @skipUnlessDarwin
+    def test_primary_c_linkage_ignores_symbol_only_cpp_decoy(self):
+        self.launch()
+        self.assertEqual(
+            self.value("linkage_target()", 'extern "C" int linkage_target();'),
+            42,
+        )
+
+    @skipUnlessDarwin
+    def test_global_and_weak_symbols(self):
+        self.launch()
+        self.assertEqual(
+            self.value("linkage_global", 'extern "C" { extern int linkage_global; }'),
+            73,
+        )
+        self.assertEqual(
+            self.value(
+                "missing_weak == nullptr",
+                'extern "C" int missing_weak() __attribute__((weak_import));',
+            ),
+            1,
+        )
+        self.assertEqual(
+            self.value(
+                "present_weak()",
+                'extern "C" int present_weak() __attribute__((weak_import));',
+            ),
+            29,
+        )
+
+    @skipUnlessDarwin
+    def test_cpp_and_function_call_label_fallbacks(self):
+        self.launch()
+        self.assertEqual(
+            self.value("cpp_fallback_target()", "int cpp_fallback_target();"), 17
+        )
+        self.assertEqual(
+            self.value(
+                "labelled_fallback()",
+                'int labelled_fallback() asm("$__lldb_func:generic:'
+                '0xffffffffffffffff:0:fcl_alias_target");',
+            ),
+            19,
+        )
+
+    @skipUnlessDarwin
+    def test_dwarf_callable(self):
+        self.launch()
+        self.assertEqual(self.value("dwarf_callable()"), 31)
diff --git a/lldb/test/API/commands/expression/linkage_lookup/decoy.cpp b/lldb/test/API/commands/expression/linkage_lookup/decoy.cpp
new file mode 100644
index 00000000000000..8f510190e66b9a
--- /dev/null
+++ b/lldb/test/API/commands/expression/linkage_lookup/decoy.cpp
@@ -0,0 +1,5 @@
+// The primary JIT relocation "linkage_target" must not bind to this C++
+// function through the basename alias in the general symbol-name index.
+int linkage_target() { return 11; }
+
+int fcl_alias_target() { return 19; }
diff --git a/lldb/test/API/commands/expression/linkage_lookup/main.cpp b/lldb/test/API/commands/expression/linkage_lookup/main.cpp
new file mode 100644
index 00000000000000..dd964f5d2babd0
--- /dev/null
+++ b/lldb/test/API/commands/expression/linkage_lookup/main.cpp
@@ -0,0 +1,10 @@
+extern "C" int linkage_library_anchor();
+extern "C" int missing_weak() __attribute__((weak_import));
+
+__attribute__((noinline)) static int dwarf_callable() { return 31; }
+
+int main() {
+  int (*volatile missing_address)() = missing_weak;
+  volatile int observed = dwarf_callable() + linkage_library_anchor();
+  return observed + (missing_address != nullptr); // break here
+}
diff --git a/lldb/test/API/commands/expression/linkage_lookup/probe.cpp b/lldb/test/API/commands/expression/linkage_lookup/probe.cpp
new file mode 100644
index 00000000000000..f02de4709565ca
--- /dev/null
+++ b/lldb/test/API/commands/expression/linkage_lookup/probe.cpp
@@ -0,0 +1,10 @@
+extern "C" {
+int linkage_library_anchor() { return 0; }
+int linkage_target() { return 42; }
+int linkage_global = 73;
+int present_weak() { return 29; }
+
+// A C++ expression declaration intentionally omits extern "C". The existing
+// alternate-mangling and basename fallback must still find this C definition.
+int cpp_fallback_target() { return 17; }
+}



More information about the llvm-branch-commits mailing list