Skip to content

fix: deduplicate DT_NEEDED entries by SONAME#1022

Open
deepakshirkem wants to merge 2 commits intoqualcomm:mainfrom
deepakshirkem:fix/dt-needed-soname
Open

fix: deduplicate DT_NEEDED entries by SONAME#1022
deepakshirkem wants to merge 2 commits intoqualcomm:mainfrom
deepakshirkem:fix/dt-needed-soname

Conversation

@deepakshirkem
Copy link
Copy Markdown
Contributor

Problem

Fixes #50

When multiple shared libraries with the same SONAME are passed to the linker, ELD emits duplicate DT_NEEDED entries for the same SONAME.

Fix

Deduplicate by SONAME string instead of file pointer using llvm::StringSet<>. This matches the behavior of ld.lld and ld.bfd.

Testing

Verified manually using the reproducer from issue #50.

When multiple shared libraries with the same SONAME are passed to the
linker, ELD was emitting duplicate DT_NEEDED entries for the same SONAME.
Copy link
Copy Markdown
Contributor

@parth-07 parth-07 left a comment

Choose a reason for hiding this comment

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

We need tests with this patch.

Additionally, we should warn if there are multiple different shared library with the same SO name.

const ELFDynObjectFile *dynObjFile = llvm::cast<ELFDynObjectFile>(lib);
if (addedLibs.count(dynObjFile->getInput()->getMemArea()))
std::string SOName = dynObjFile->getSOName();
if (!addedSONAMEs.insert(SOName).second)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We are changing the if-condition without preserving the behavior of the old if-condition. Would it break anything?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hi @parth-07,

I tested this locally and the behavior is preserved. The old code deduplicated by MemoryArea pointer to prevent the same file from being added twice. The new code deduplicates by SONAME string,
which is strictly stronger:

So the new check covers everything the old one did, plus fixes the bug.

Screenshot With reproduce step at #50
Screenshot from 2026-04-06 19-27-28

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The old code deduplicated by MemoryArea pointer to prevent the same file from being added twice. The new code deduplicates by SONAME string,
which is strictly stronger:

What happens for libraries without any SONAME? Deduplication using SONAME wouldn't work for such libraries.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hi @parth-07, I investigated this concern and the fix handles this case correctly. Every SONAME library always has a SONAME set before our code runs

if (!hasSOName)
dynObjFile->setSOName(dynObjFile->getFallbackSOName());

getFallbackSOName() returns the resolved file path, which is unique per file. By the time our code runs in sizeDynamic(), every library already has a SONAME set either real or fallback. We never see an empty SONAME.

I am still open to your feedback!

@deepakshirkem
Copy link
Copy Markdown
Contributor Author

Hi @parth-07,

I will add a test in this patch. I also think emitting a warning for duplicate shared libraries with the same SONAME would make more sense.

Thank You ((::

@deepakshirkem
Copy link
Copy Markdown
Contributor Author

Hi @parth-07,
I have addressed all the requested changes. Please take a look and let me know if any further changes are needed.

Thank You ::))

@deepakshirkem deepakshirkem requested a review from parth-07 April 6, 2026 19:48
if (llvm::dyn_cast<ELFFileBase>(lib)->isELFNeeded()) {
const ELFDynObjectFile *dynObjFile = llvm::cast<ELFDynObjectFile>(lib);
if (addedLibs.count(dynObjFile->getInput()->getMemArea()))
std::string SOName = dynObjFile->getSOName();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

llvm::StringRef ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hi @quic-seaswara, getSOName() returns std::string by value. Using llvm::StringRef here would result in a dangling reference since the temporary std::string would be destroyed at the end of the full expression.
I did try using llvm::StringRef initially but it resulted in a dangling reference error during compilation.

const ELFDynObjectFile *dynObjFile = llvm::cast<ELFDynObjectFile>(lib);
if (addedLibs.count(dynObjFile->getInput()->getMemArea()))
std::string SOName = dynObjFile->getSOName();
std::string fileName = dynObjFile->getInput()->getFileName();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can you move filenanme to 990 and use StringRef

llvm::StringSet<> addedSONAMEs;
llvm::StringMap<std::string> sonameToFile;
for (auto &lib : m_Module.getDynLibraryList()) {
if (llvm::dyn_cast<ELFFileBase>(lib)->isELFNeeded()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

if (!isELFNeeded())
continue;

RUN: %clang %clangopts -fPIC -c %p/Inputs/2.c -o %t1.2.o
RUN: %link %linkopts %t1.1.o -o %t1.lib1.so -shared -soname=foo
RUN: %link %linkopts %t1.2.o -o %t1.lib2.so -shared -soname=foo
RUN: %link %linkopts /dev/null %t1.lib1.so %t1.lib2.so -o %t1.out 2>&1 | %filecheck %s --check-prefix=WARN
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why /dev/null ? is it portable to windows ?

RUN: %link %linkopts %t1.2.o -o %t1.lib2.so -shared -soname=foo
RUN: %link %linkopts /dev/null %t1.lib1.so %t1.lib2.so -o %t1.out 2>&1 | %filecheck %s --check-prefix=WARN
RUN: %readelf --dynamic %t1.out | %filecheck %s --check-prefix=NEEDED
#WARN: Warning: Multiple shared libraries with the same SONAME "foo" detected
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does bfd warn ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

bfd and lld does not warn here, they simply emit the first shared library seen when there are multiple libraries with the same SONAME. I requested @deepakshirkem to emit the warning diagnostic here.

"Empty archive file: '%0'")
DIAG(sframe_read_error, DiagnosticEngine::Error,
"SFrame Read Error : %0 from file %1") No newline at end of file
"SFrame Read Error : %0 from file %1")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

remove new line

"SFrame Read Error : %0 from file %1") No newline at end of file
"SFrame Read Error : %0 from file %1")
DIAG(warn_duplicate_soname, DiagnosticEngine::Warning,
"Multiple shared libraries with the same SONAME \"%0\" detected: %1 and %2") No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can you use the same format of message used by Gnu Ld or lld ?

@@ -0,0 +1 @@
int bar() { return 3; } No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

you can have the same input file to generate two shared libraries ?

When multiple shared libraries with the same SONAME are passed to the
linker, ELD was emitting duplicate DT_NEEDED entries.

Signed-off-by: deepakshirkem <deepakshirke509@gmail.com>
@deepakshirkem deepakshirkem force-pushed the fix/dt-needed-soname branch from 1019252 to 5bd591c Compare April 7, 2026 14:26
@deepakshirkem
Copy link
Copy Markdown
Contributor Author

Hi @quic-seaswara, I have addressed all the requested changes. Please take a look and let me know if any further changes are needed.

Thank You ::))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Shared libraries are not identified by the SO name when emitting DT_NEEDED entries

3 participants