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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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)); } _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
