[llvm-branch-commits] [lldb] [LLDB] Support Re-export Symbol for FunctionCall (PR #208998)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Jul 17 03:52:22 PDT 2026
https://github.com/aokblast updated https://github.com/llvm/llvm-project/pull/208998
>From 0b85e1e9f569820064e6cbf999a9853e7bdb3f8b Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Sat, 11 Jul 2026 19:11:57 +0800
Subject: [PATCH 1/6] Support Re-export Symbol for FunctionCall
A symbol can be a stub that is re-exported to another library. For
example, ELF can specify an auxiliary library and emit a zero-sized
symbol in the symbol table. This can cause expression evaluation to
resolve the stub instead of the actual function. Fix this by resolving
re-exported symbols to their actual addressed.
---
.../Process/Utility/InferiorCallPOSIX.cpp | 34 +++++++++++++++----
1 file changed, 28 insertions(+), 6 deletions(-)
diff --git a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
index 5226b9bbe7c78..f190b589ad704 100644
--- a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
+++ b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
@@ -33,6 +33,30 @@
using namespace lldb;
using namespace lldb_private;
+/// Pick a call target from \p sc_list. Re-exported symbols have no code of
+/// their own and are resolved through the library that actually defines
+/// them, mirroring what the dynamic linker does.
+static Address GetCallableAddress(Target &target,
+ const SymbolContextList &sc_list) {
+ const uint32_t count = sc_list.GetSize();
+ for (uint32_t i = 0; i < count; ++i) {
+ SymbolContext sc;
+ if (!sc_list.GetContextAtIndex(i, sc))
+ continue;
+ if (sc.symbol && sc.symbol->GetType() == eSymbolTypeReExported) {
+ // Pass the containing module so all of its re-exported libraries
+ // (e.g. an ELF filter library's filtees) are searched in order.
+ if (Symbol *actual =
+ sc.symbol->ResolveReExportedSymbol(target, sc.module_sp))
+ return actual->GetAddress();
+ // Unresolvable re-export: not callable.
+ continue;
+ }
+ return sc.GetFunctionOrSymbolAddress();
+ }
+ return Address();
+}
+
bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr,
addr_t addr, addr_t length, unsigned prot,
unsigned flags, addr_t fd, addr_t offset) {
@@ -50,8 +74,7 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr,
ConstString("mmap"), eFunctionNameTypeFull, function_options, sc_list);
const uint32_t count = sc_list.GetSize();
if (count > 0) {
- SymbolContext sc;
- if (sc_list.GetContextAtIndex(0, sc)) {
+ {
EvaluateExpressionOptions options;
options.SetStopOthers(true);
options.SetUnwindOnError(true);
@@ -74,7 +97,7 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr,
prot_arg |= PROT_WRITE;
}
- Address mmap_addr = sc.GetFunctionOrSymbolAddress();
+ Address mmap_addr = GetCallableAddress(process->GetTarget(), sc_list);
if (mmap_addr.IsValid()) {
auto type_system_or_err =
process->GetTarget().GetScratchTypeSystemForLanguage(
@@ -142,8 +165,7 @@ bool lldb_private::InferiorCallMunmap(Process *process, addr_t addr,
ConstString("munmap"), eFunctionNameTypeFull, function_options, sc_list);
const uint32_t count = sc_list.GetSize();
if (count > 0) {
- SymbolContext sc;
- if (sc_list.GetContextAtIndex(0, sc)) {
+ {
EvaluateExpressionOptions options;
options.SetStopOthers(true);
options.SetUnwindOnError(true);
@@ -153,7 +175,7 @@ bool lldb_private::InferiorCallMunmap(Process *process, addr_t addr,
options.SetTimeout(process->GetUtilityExpressionTimeout());
options.SetTrapExceptions(false);
- Address munmap_addr = sc.GetFunctionOrSymbolAddress();
+ Address munmap_addr = GetCallableAddress(process->GetTarget(), sc_list);
if (munmap_addr.IsValid()) {
lldb::addr_t args[] = {addr, length};
lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallFunction(
>From cff87246629c8aeef17459c99d76b1f88c4a3486 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Tue, 14 Jul 2026 21:38:36 +0800
Subject: [PATCH 2/6] fixup! Support Re-export Symbol for FunctionCall
---
.../Plugins/Process/Utility/InferiorCallPOSIX.cpp | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
index f190b589ad704..dbc53ce5fcee6 100644
--- a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
+++ b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
@@ -33,9 +33,10 @@
using namespace lldb;
using namespace lldb_private;
-/// Pick a call target from \p sc_list. Re-exported symbols have no code of
-/// their own and are resolved through the library that actually defines
-/// them, mirroring what the dynamic linker does.
+/// Pick a call target from \p sc_list. A symbol from an ELF filter/auxiliary
+/// library is resolved through its filtee(s) first, falling back to its own
+/// definition only if none of them provide it, mirroring what the dynamic
+/// linker does.
static Address GetCallableAddress(Target &target,
const SymbolContextList &sc_list) {
const uint32_t count = sc_list.GetSize();
@@ -43,14 +44,16 @@ static Address GetCallableAddress(Target &target,
SymbolContext sc;
if (!sc_list.GetContextAtIndex(i, sc))
continue;
- if (sc.symbol && sc.symbol->GetType() == eSymbolTypeReExported) {
+ if (sc.symbol) {
// Pass the containing module so all of its re-exported libraries
// (e.g. an ELF filter library's filtees) are searched in order.
if (Symbol *actual =
sc.symbol->ResolveReExportedSymbol(target, sc.module_sp))
return actual->GetAddress();
- // Unresolvable re-export: not callable.
- continue;
+ // A pure re-export placeholder with no filtee providing a definition
+ // has no code of its own and isn't callable.
+ if (sc.symbol->GetType() == eSymbolTypeReExported)
+ continue;
}
return sc.GetFunctionOrSymbolAddress();
}
>From 66dc68d21562acbe70b4ae035e46e121603f125c Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Tue, 14 Jul 2026 22:31:57 +0800
Subject: [PATCH 3/6] fixup! Support Re-export Symbol for FunctionCall
---
.../Process/Utility/InferiorCallPOSIX.cpp | 123 +++++++++---------
1 file changed, 60 insertions(+), 63 deletions(-)
diff --git a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
index dbc53ce5fcee6..f11594f2327d1 100644
--- a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
+++ b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
@@ -77,72 +77,69 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr,
ConstString("mmap"), eFunctionNameTypeFull, function_options, sc_list);
const uint32_t count = sc_list.GetSize();
if (count > 0) {
- {
- EvaluateExpressionOptions options;
- options.SetStopOthers(true);
- options.SetUnwindOnError(true);
- options.SetIgnoreBreakpoints(true);
- options.SetTryAllThreads(true);
- options.SetDebug(false);
- options.SetTimeout(process->GetUtilityExpressionTimeout());
- options.SetTrapExceptions(false);
+ EvaluateExpressionOptions options;
+ options.SetStopOthers(true);
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTryAllThreads(true);
+ options.SetDebug(false);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetTrapExceptions(false);
+
+ addr_t prot_arg;
+ if (prot == eMmapProtNone)
+ prot_arg = PROT_NONE;
+ else {
+ prot_arg = 0;
+ if (prot & eMmapProtExec)
+ prot_arg |= PROT_EXEC;
+ if (prot & eMmapProtRead)
+ prot_arg |= PROT_READ;
+ if (prot & eMmapProtWrite)
+ prot_arg |= PROT_WRITE;
+ }
- addr_t prot_arg;
- if (prot == eMmapProtNone)
- prot_arg = PROT_NONE;
- else {
- prot_arg = 0;
- if (prot & eMmapProtExec)
- prot_arg |= PROT_EXEC;
- if (prot & eMmapProtRead)
- prot_arg |= PROT_READ;
- if (prot & eMmapProtWrite)
- prot_arg |= PROT_WRITE;
+ Address mmap_addr = GetCallableAddress(process->GetTarget(), sc_list);
+ if (mmap_addr.IsValid()) {
+ auto type_system_or_err =
+ process->GetTarget().GetScratchTypeSystemForLanguage(eLanguageTypeC);
+ if (!type_system_or_err) {
+ llvm::consumeError(type_system_or_err.takeError());
+ return false;
}
-
- Address mmap_addr = GetCallableAddress(process->GetTarget(), sc_list);
- if (mmap_addr.IsValid()) {
- auto type_system_or_err =
- process->GetTarget().GetScratchTypeSystemForLanguage(
- eLanguageTypeC);
- if (!type_system_or_err) {
- llvm::consumeError(type_system_or_err.takeError());
- return false;
- }
- auto ts = *type_system_or_err;
- if (!ts)
- return false;
- CompilerType void_ptr_type =
- ts->GetBasicTypeFromAST(eBasicTypeVoid).GetPointerType();
- const ArchSpec arch = process->GetTarget().GetArchitecture();
- MmapArgList args =
- process->GetTarget().GetPlatform()->GetMmapArgumentList(
- arch, addr, length, prot_arg, flags, fd, offset);
- lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallFunction(
- *thread, mmap_addr, void_ptr_type, args, options));
- if (call_plan_sp) {
- DiagnosticManager diagnostics;
-
- StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
- if (frame) {
- ExecutionContext exe_ctx;
- frame->CalculateExecutionContext(exe_ctx);
- ExpressionResults result = process->RunThreadPlan(
- exe_ctx, call_plan_sp, options, diagnostics);
- if (result == eExpressionCompleted) {
-
- allocated_addr =
- call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(
- LLDB_INVALID_ADDRESS);
- if (process->GetAddressByteSize() == 4) {
- if (allocated_addr == UINT32_MAX)
- return false;
- } else if (process->GetAddressByteSize() == 8) {
- if (allocated_addr == UINT64_MAX)
- return false;
- }
- return true;
+ auto ts = *type_system_or_err;
+ if (!ts)
+ return false;
+ CompilerType void_ptr_type =
+ ts->GetBasicTypeFromAST(eBasicTypeVoid).GetPointerType();
+ const ArchSpec arch = process->GetTarget().GetArchitecture();
+ MmapArgList args =
+ process->GetTarget().GetPlatform()->GetMmapArgumentList(
+ arch, addr, length, prot_arg, flags, fd, offset);
+ lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallFunction(
+ *thread, mmap_addr, void_ptr_type, args, options));
+ if (call_plan_sp) {
+ DiagnosticManager diagnostics;
+
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+ if (frame) {
+ ExecutionContext exe_ctx;
+ frame->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result = process->RunThreadPlan(
+ exe_ctx, call_plan_sp, options, diagnostics);
+ if (result == eExpressionCompleted) {
+
+ allocated_addr =
+ call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(
+ LLDB_INVALID_ADDRESS);
+ if (process->GetAddressByteSize() == 4) {
+ if (allocated_addr == UINT32_MAX)
+ return false;
+ } else if (process->GetAddressByteSize() == 8) {
+ if (allocated_addr == UINT64_MAX)
+ return false;
}
+ return true;
}
}
}
>From 25c3eefe80778940d141af28fd9455eb0baee2ba Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Thu, 16 Jul 2026 22:42:37 +0800
Subject: [PATCH 4/6] fixup! Support Re-export Symbol for FunctionCall
---
.../Process/Utility/InferiorCallPOSIX.cpp | 52 +++++++++----------
1 file changed, 25 insertions(+), 27 deletions(-)
diff --git a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
index f11594f2327d1..d4d79e712a83d 100644
--- a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
+++ b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
@@ -165,33 +165,31 @@ bool lldb_private::InferiorCallMunmap(Process *process, addr_t addr,
ConstString("munmap"), eFunctionNameTypeFull, function_options, sc_list);
const uint32_t count = sc_list.GetSize();
if (count > 0) {
- {
- EvaluateExpressionOptions options;
- options.SetStopOthers(true);
- options.SetUnwindOnError(true);
- options.SetIgnoreBreakpoints(true);
- options.SetTryAllThreads(true);
- options.SetDebug(false);
- options.SetTimeout(process->GetUtilityExpressionTimeout());
- options.SetTrapExceptions(false);
-
- Address munmap_addr = GetCallableAddress(process->GetTarget(), sc_list);
- if (munmap_addr.IsValid()) {
- lldb::addr_t args[] = {addr, length};
- lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallFunction(
- *thread, munmap_addr, CompilerType(), args, options));
- if (call_plan_sp) {
- DiagnosticManager diagnostics;
-
- StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
- if (frame) {
- ExecutionContext exe_ctx;
- frame->CalculateExecutionContext(exe_ctx);
- ExpressionResults result = process->RunThreadPlan(
- exe_ctx, call_plan_sp, options, diagnostics);
- if (result == eExpressionCompleted) {
- return true;
- }
+ EvaluateExpressionOptions options;
+ options.SetStopOthers(true);
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTryAllThreads(true);
+ options.SetDebug(false);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetTrapExceptions(false);
+
+ Address munmap_addr = GetCallableAddress(process->GetTarget(), sc_list);
+ if (munmap_addr.IsValid()) {
+ lldb::addr_t args[] = {addr, length};
+ lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallFunction(
+ *thread, munmap_addr, CompilerType(), args, options));
+ if (call_plan_sp) {
+ DiagnosticManager diagnostics;
+
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+ if (frame) {
+ ExecutionContext exe_ctx;
+ frame->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result = process->RunThreadPlan(
+ exe_ctx, call_plan_sp, options, diagnostics);
+ if (result == eExpressionCompleted) {
+ return true;
}
}
}
>From 6fe6d4d8c8cf7b5255d62b8600f72d8e83d90adb Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Fri, 17 Jul 2026 16:55:13 +0800
Subject: [PATCH 5/6] fixup! [LLDB] Support Auxiliary library in ELF format
---
.../Plugins/ObjectFile/ELF/ObjectFileELF.cpp | 15 ---------------
lldb/source/Symbol/Symbol.cpp | 7 ++++++-
2 files changed, 6 insertions(+), 16 deletions(-)
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index 14092fc494cf7..dbcda730c1a2e 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -2352,11 +2352,6 @@ ObjectFileELF::ParseSymbols(Symtab *symtab, user_id_t start_id,
SectionList *module_section_list =
module_sp ? module_sp->GetSectionList() : nullptr;
- // This is modeled after the DT_AUXILIARY and DT_FILTER mechanisms in ELF,
- // where the actual symbols are defined in another library, while the compiler
- // generates stubs in the original library.
- const FileSpecList filtees = GetReExportedLibraries();
-
// We might have debug information in a separate object, in which case
// we need to map the sections from that object to the sections in the
// main object during symbol lookup. If we had to compare the sections
@@ -2683,16 +2678,6 @@ ObjectFileELF::ParseSymbols(Symtab *symtab, user_id_t start_id,
flags); // Symbol flags.
if (symbol.getBinding() == STB_WEAK)
dc_symbol.SetIsWeak(true);
- // Zero-sized exported function in a filter library: a placeholder the
- // dynamic linker resolves through the filtees (see
- // GetReExportedLibraries()). The re-exported name must not carry the
- // @VERSION suffix, since it is looked up in the filtee by name.
- if (filtees.GetSize() > 0 && symbol.getType() == STT_FUNC &&
- symbol.st_size == 0 && symbol.getBinding() != STB_LOCAL &&
- symbol_section_sp) {
- dc_symbol.SetReExportedSymbolName(ConstString(symbol_bare));
- dc_symbol.SetReExportedSymbolSharedLibrary(filtees.GetFileSpecAtIndex(0));
- }
symtab->AddSymbol(dc_symbol);
}
diff --git a/lldb/source/Symbol/Symbol.cpp b/lldb/source/Symbol/Symbol.cpp
index dea61c1c3eb58..fbb144d1c3f8e 100644
--- a/lldb/source/Symbol/Symbol.cpp
+++ b/lldb/source/Symbol/Symbol.cpp
@@ -456,7 +456,7 @@ Symbol *Symbol::ResolveReExportedSymbolInModuleSpec(
module_sp->FindSymbolsWithNameAndType(reexport_name, eSymbolTypeAny,
sc_list);
for (const SymbolContext &sc : sc_list) {
- if (!sc.symbol->IsExternal())
+ if (!sc.symbol->IsExternal() && !sc.symbol->IsWeak())
continue;
// Don't return a symbol that itself only re-exports the definition
// (e.g. an ELF filter library's placeholder): the real definition is
@@ -514,6 +514,11 @@ Symbol *Symbol::ResolveReExportedSymbol(
!object_file->ReExportedLibrariesShadowLocalDefinitions())
return nullptr;
+ // Only exported (global or weak) definitions take part in dynamic
+ // linking, so local symbols are never shadowed by a filtee.
+ if (!IsExternal() && !IsWeak())
+ return nullptr;
+
// Use this symbol's own (version-suffix-stripped) name so the filtees
// below are searched for it.
reexport_name = GetName();
>From ddf626b83017e76da1a680c081e67a6f59f8580b Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Fri, 17 Jul 2026 16:55:13 +0800
Subject: [PATCH 6/6] fixup! Add unit tests for ELF filter-library symbol
handling.
---
.../ObjectFile/ELF/TestObjectFileELF.cpp | 49 +++----
.../unittests/Target/ReExportedSymbolTest.cpp | 137 ++++++++++++++----
2 files changed, 129 insertions(+), 57 deletions(-)
diff --git a/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp b/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp
index 90ae974337090..4126fbbbab156 100644
--- a/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp
+++ b/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp
@@ -563,8 +563,12 @@ TEST_F(ObjectFileELFTest, SkipsLocalMappingAndDotLSymbols) {
// are placeholders whose addresses are meaningless (e.g. FreeBSD >= 15's
// libc.so.7 exports a zero-sized "mmap" whose address points into an
// unrelated function; the real definition lives in the libsys.so.7 filtee).
-// Check that the filtees are reported in dynamic-section order and that
-// placeholders are turned into re-exported symbols.
+// Check that the filtees are reported in dynamic-section order and that the
+// symbol table itself is left untouched: marking a symbol as re-exported
+// reuses its address range as string-pointer storage (see
+// Symbol::SetReExportedSymbolName), which would corrupt the file-address
+// indexes for symbols that have a section. Filtee shadowing happens at
+// resolution time instead (see ReExportedSymbolTest.cpp).
TEST_F(ObjectFileELFTest, FilterLibraryPlaceholderSymbols) {
auto ExpectedFile = TestFile::fromYaml(R"(
--- !ELF
@@ -639,41 +643,28 @@ TEST_F(ObjectFileELFTest, FilterLibraryPlaceholderSymbols) {
EXPECT_EQ(ConstString("libaux2.so"),
filtees.GetFileSpecAtIndex(1).GetFilename());
- // A zero-sized exported function becomes a re-export recording the first
- // filtee.
- const Symbol *placeholder = module_sp->FindFirstSymbolWithNameAndType(
- ConstString("mmap"), eSymbolTypeReExported);
- ASSERT_NE(nullptr, placeholder);
- EXPECT_EQ(ConstString("mmap"), placeholder->GetReExportedSymbolName());
- EXPECT_EQ(ConstString("libaux1.so"),
- ConstString(
- placeholder->GetReExportedSymbolSharedLibrary().GetFilename()));
-
- // The re-exported name must not carry the @VERSION suffix. Find the
- // symbol by iterating rather than by name: whether a versioned symbol can
- // be looked up by its stripped base name is a property of the symtab name
- // indexes, not of the re-export marking under test here.
+ // Filtee shadowing is a resolution-time concern: no symbol may be marked
+ // eSymbolTypeReExported, which would clobber its address range.
Symtab *symtab = module_sp->GetSymtab();
ASSERT_NE(nullptr, symtab);
- const Symbol *versioned = nullptr;
for (size_t i = 0, e = symtab->GetNumSymbols(); i != e; ++i) {
- Symbol *candidate = symtab->SymbolAtIndex(i);
- if (candidate->GetType() == eSymbolTypeReExported &&
- candidate->GetName().GetStringRef().starts_with("open"))
- versioned = candidate;
+ const Symbol *symbol = symtab->SymbolAtIndex(i);
+ EXPECT_NE(eSymbolTypeReExported, symbol->GetType())
+ << "symbol: " << symbol->GetName().GetStringRef().str();
}
- ASSERT_NE(nullptr, versioned);
- EXPECT_EQ(ConstString("open"), versioned->GetReExportedSymbolName());
+
+ // The zero-sized placeholder stays a plain code symbol whose address (and
+ // thus the file-address indexes built from it) is intact.
+ const Symbol *placeholder = module_sp->FindFirstSymbolWithNameAndType(
+ ConstString("mmap"), eSymbolTypeCode);
+ ASSERT_NE(nullptr, placeholder);
+ EXPECT_TRUE(placeholder->ValueIsAddress());
+ EXPECT_EQ(0x1000U, placeholder->GetAddress().GetFileAddress());
+ EXPECT_TRUE(placeholder->GetReExportedSymbolName().IsEmpty());
// A function the filter genuinely defines (nonzero size) keeps its
// definition, matching the dynamic linker's DT_AUXILIARY fallback.
const Symbol *real = module_sp->FindFirstSymbolWithNameAndType(
ConstString("printf"), eSymbolTypeCode);
ASSERT_NE(nullptr, real);
-
- // Local zero-sized functions are not exported and must not be marked.
- const Symbol *local = module_sp->FindFirstSymbolWithNameAndType(
- ConstString("local_helper"), eSymbolTypeAny);
- ASSERT_NE(nullptr, local);
- EXPECT_NE(eSymbolTypeReExported, local->GetType());
}
diff --git a/lldb/unittests/Target/ReExportedSymbolTest.cpp b/lldb/unittests/Target/ReExportedSymbolTest.cpp
index bd5512b2d0e34..5d62ac4818142 100644
--- a/lldb/unittests/Target/ReExportedSymbolTest.cpp
+++ b/lldb/unittests/Target/ReExportedSymbolTest.cpp
@@ -37,10 +37,14 @@ class ReExportedSymbolTest : public testing::Test {
} // namespace
// An ELF filter library naming two auxiliary filtees. Its zero-sized
-// exported functions "foo" and "bar" are placeholders that ObjectFileELF
-// models as re-exported symbols (the dynamic linker resolves them through
-// the filtees). "bar" is defined by the first filtee, "foo" only by the
-// second, so resolving "foo" exercises the ordered search across filtees.
+// exported functions "foo", "bar" and "open@@FBSD_1.0" are placeholders the
+// dynamic linker resolves through the filtees. ObjectFileELF leaves them as
+// plain code symbols (re-export marking would clobber their address ranges);
+// Symbol::ResolveReExportedSymbol shadows them at resolution time. "bar" is
+// defined by the first filtee, "foo" only by the second, so resolving "foo"
+// exercises the ordered search across filtees. "open@@FBSD_1.0" exercises
+// version-suffix stripping, and the local "hidden_helper" must not be
+// shadowed even though the first filtee exports that name.
//
// .dynstr content: "\0libaux1.so\0libaux2.so\0"
// offset 0x1: "libaux1.so"
@@ -58,7 +62,7 @@ static const char *k_filter_yaml = R"(
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x1000
AddressAlign: 0x10
- Content: C3C3C3C3
+ Content: C3C3C3C3C3C3C3C3
- Name: .dynstr
Type: SHT_STRTAB
Flags: [ SHF_ALLOC ]
@@ -79,6 +83,11 @@ static const char *k_filter_yaml = R"(
- Tag: DT_NULL
Value: 0x0
Symbols:
+ - Name: hidden_helper
+ Type: STT_FUNC
+ Section: .text
+ Value: 0x1003
+ Binding: STB_LOCAL
- Name: foo
Type: STT_FUNC
Section: .text
@@ -89,6 +98,11 @@ static const char *k_filter_yaml = R"(
Section: .text
Value: 0x1001
Binding: STB_GLOBAL
+ - Name: "open@@FBSD_1.0"
+ Type: STT_FUNC
+ Section: .text
+ Value: 0x1001
+ Binding: STB_WEAK
# A real (nonzero-sized) function the filter defines itself. Besides being
# realistic -- a filter library like libc.so.7 is full of real functions --
# it gives the object file a code symbol so that SymbolFileSymtab claims the
@@ -100,6 +114,16 @@ static const char *k_filter_yaml = R"(
Value: 0x1002
Size: 0x2
Binding: STB_GLOBAL
+ # A genuine (nonzero-sized) implementation that the first filtee *also*
+ # defines. The dynamic linker always searches the filtees before the
+ # filter object's own definition, so the filtee must win even though the
+ # filter's definition is real code and not a placeholder.
+ - Name: dup_impl
+ Type: STT_FUNC
+ Section: .text
+ Value: 0x1004
+ Size: 0x2
+ Binding: STB_GLOBAL
...
)";
@@ -116,17 +140,32 @@ static const char *k_aux1_yaml = R"(
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x1000
AddressAlign: 0x10
- Content: C3C3C3C3
+ Content: C3C3C3C3C3C3C3C3
Symbols:
- Name: bar
Type: STT_FUNC
Section: .text
Value: 0x1000
- Size: 0x4
+ Size: 0x2
+ Binding: STB_GLOBAL
+ - Name: hidden_helper
+ Type: STT_FUNC
+ Section: .text
+ Value: 0x1002
+ Size: 0x2
+ Binding: STB_GLOBAL
+ - Name: dup_impl
+ Type: STT_FUNC
+ Section: .text
+ Value: 0x1004
+ Size: 0x2
Binding: STB_GLOBAL
...
)";
+// The second filtee's definitions are weak: that is how FreeBSD's
+// libsys.so.7 exports its syscall stubs, and weak definitions take part in
+// dynamic linking just like global ones.
static const char *k_aux2_yaml = R"(
--- !ELF
FileHeader:
@@ -146,8 +185,14 @@ static const char *k_aux2_yaml = R"(
Type: STT_FUNC
Section: .text
Value: 0x1000
- Size: 0x4
- Binding: STB_GLOBAL
+ Size: 0x2
+ Binding: STB_WEAK
+ - Name: open
+ Type: STT_FUNC
+ Section: .text
+ Value: 0x1002
+ Size: 0x2
+ Binding: STB_WEAK
...
)";
@@ -159,9 +204,8 @@ static ModuleSP AddModule(Target &target, TestFile &file, const char *name) {
// The module is built from a buffer with no backing file. Parse everything
// that depends on that buffer *now*, while the module still has no file:
- // - GetReExportedLibraries() populates the dynamic-symbol cache, so that
- // when the symbol table is parsed below the filter placeholders are seen
- // and marked as re-exports.
+ // - GetReExportedLibraries() parses and caches the dynamic section, which
+ // re-export resolution reads later.
// - GetSymtab() parses and caches the symbol table (and its symbol file).
// Renaming afterwards would otherwise make these look for the symbols in the
// now-nonexistent file. The name is required so re-export resolution can
@@ -179,20 +223,19 @@ static ModuleSP AddModule(Target &target, TestFile &file, const char *name) {
return module_sp;
}
-// Find the re-exported placeholder symbol named \p name in \p module_sp by
-// iterating the symbol table. Looking a re-exported symbol up by name and
-// type through the module goes through the symtab name indexes, whose
-// handling of versioned/placeholder names is orthogonal to what is tested
-// here.
-static const Symbol *FindReExport(const ModuleSP &module_sp,
- llvm::StringRef name) {
+// Find the symbol whose name is \p name, ignoring any @VERSION suffix, in
+// \p module_sp by iterating the symbol table. Iterating keeps the lookup
+// independent of how the symtab name indexes treat versioned names, which is
+// orthogonal to what is tested here.
+static const Symbol *FindFilterSymbol(const ModuleSP &module_sp,
+ llvm::StringRef name) {
Symtab *symtab = module_sp->GetSymtab();
if (!symtab)
return nullptr;
for (size_t i = 0, e = symtab->GetNumSymbols(); i != e; ++i) {
const Symbol *symbol = symtab->SymbolAtIndex(i);
- if (symbol->GetType() == eSymbolTypeReExported &&
- symbol->GetName().GetStringRef() == name)
+ llvm::StringRef symbol_name = symbol->GetName().GetStringRef();
+ if (symbol_name.substr(0, symbol_name.find('@')) == name)
return symbol;
}
return nullptr;
@@ -242,10 +285,14 @@ TEST_F(ReExportedSymbolTest, ResolveThroughFilteesInOrder) {
ASSERT_NE(nullptr, filter_symtab);
ASSERT_GT(filter_symtab->GetNumSymbols(), 0u);
- // "bar" resolves through the library recorded on the symbol itself (the
- // first filtee).
- const Symbol *bar = FindReExport(filter_sp, "bar");
+ // The placeholders stay plain code symbols: marking them as re-exported
+ // would reuse their address ranges as string-pointer storage and corrupt
+ // the file-address indexes.
+ const Symbol *bar = FindFilterSymbol(filter_sp, "bar");
ASSERT_NE(nullptr, bar);
+ EXPECT_EQ(eSymbolTypeCode, bar->GetType());
+
+ // "bar" is found in the first filtee.
Symbol *bar_def = bar->ResolveReExportedSymbol(*target_sp, filter_sp);
ASSERT_NE(nullptr, bar_def);
EXPECT_EQ(ConstString("bar"), bar_def->GetName());
@@ -254,7 +301,7 @@ TEST_F(ReExportedSymbolTest, ResolveThroughFilteesInOrder) {
// "foo" is not defined by the first filtee: resolution must continue with
// the remaining filtees in declaration order and find it in the second.
- const Symbol *foo = FindReExport(filter_sp, "foo");
+ const Symbol *foo = FindFilterSymbol(filter_sp, "foo");
ASSERT_NE(nullptr, foo);
Symbol *foo_def = foo->ResolveReExportedSymbol(*target_sp, filter_sp);
ASSERT_NE(nullptr, foo_def);
@@ -262,8 +309,42 @@ TEST_F(ReExportedSymbolTest, ResolveThroughFilteesInOrder) {
EXPECT_EQ(eSymbolTypeCode, foo_def->GetType());
EXPECT_EQ(aux2_sp, foo_def->GetAddress().GetModule());
- // Without the containing module only the library recorded on the symbol
- // is searched, so "foo" cannot be resolved. This is why callers that
- // have the module should pass it.
+ // "open@@FBSD_1.0": the @VERSION suffix is stripped before the filtees are
+ // searched, since the filtee exports the plain name.
+ const Symbol *open_sym = FindFilterSymbol(filter_sp, "open");
+ ASSERT_NE(nullptr, open_sym);
+ Symbol *open_def = open_sym->ResolveReExportedSymbol(*target_sp, filter_sp);
+ ASSERT_NE(nullptr, open_def);
+ EXPECT_EQ(ConstString("open"), open_def->GetName());
+ EXPECT_EQ(aux2_sp, open_def->GetAddress().GetModule());
+
+ // A local symbol is invisible to the dynamic linker and must not be
+ // shadowed, even though the first filtee exports the same name.
+ const Symbol *hidden = FindFilterSymbol(filter_sp, "hidden_helper");
+ ASSERT_NE(nullptr, hidden);
+ EXPECT_EQ(nullptr, hidden->ResolveReExportedSymbol(*target_sp, filter_sp));
+
+ // A name no filtee provides resolves to nothing; callers then fall back
+ // to the filter's own (intact) definition.
+ const Symbol *filter_local = FindFilterSymbol(filter_sp, "filter_local");
+ ASSERT_NE(nullptr, filter_local);
+ EXPECT_EQ(nullptr,
+ filter_local->ResolveReExportedSymbol(*target_sp, filter_sp));
+
+ // Both the filter and the first filtee genuinely define "dup_impl"
+ // (nonzero-sized code on both sides). The dynamic linker searches the
+ // filtees before the filter object's own definition, so the filtee's
+ // definition must win.
+ const Symbol *dup = FindFilterSymbol(filter_sp, "dup_impl");
+ ASSERT_NE(nullptr, dup);
+ EXPECT_EQ(eSymbolTypeCode, dup->GetType());
+ Symbol *dup_def = dup->ResolveReExportedSymbol(*target_sp, filter_sp);
+ ASSERT_NE(nullptr, dup_def);
+ EXPECT_EQ(ConstString("dup_impl"), dup_def->GetName());
+ EXPECT_EQ(aux1_sp, dup_def->GetAddress().GetModule());
+
+ // Without the containing module nothing connects the symbol to the
+ // filtees, so "foo" cannot be resolved. This is why callers that have
+ // the module should pass it.
EXPECT_EQ(nullptr, foo->ResolveReExportedSymbol(*target_sp));
}
More information about the llvm-branch-commits
mailing list