Investigating Library Dependencies with ELF Dissector
With the upcoming KF6 transition we have the chance again to further untangle and clean up dependencies between KDE Frameworks. There’s a number of tools that help us with analyzing the current state of dependencies, one that we didn’t have when KF5 was started is ELF Dissector.
Library-level Dependencies
When looking at dependencies we usually start at the module or library level, with something as simple and widely
available like ldd
. For nicer visualization, script like KDE’s draw_lib_dependencies
exist to visualize the result. CMake also provides a similar option to produce a built-time dependency graph.

While that is good enough in many cases, sometimes we need to dig deeper, and that’s where ELF Dissector can help. After opening the library or executable to analyze and switching to the dependency view, you’ll find three different ways to look at dependencies, described below.
Symbol-level Dependencies
The first view is closest to a tree view of what you’d get from ldd
as well. However, it has one major addition,
it shows which symbols (and how many) are used from a library. This helps with determining why a dependency exists
in the first place, and to asses how strongly coupled two components are.

This is done by looking at the imported symbols of a component, which means this cannot distinguish how often a specific symbol is used. So even a dependency on just a single symbol can mean a very strong coupling in practice. Nevertheless this is often a good indicator.
Inverse Dependencies
For answering the question why an application or library pulls in a specific dependency the symbol-level view can get hard to navigate when there’s many components involved. That’s where the inverse dependency view comes in. There you can select a library and see everything that depends on it, again down to the symbol level.

Unused Dependencies
The third view is the list of unused dependencies. What you can see in there heavily depends
on whether the --as-needed
linker flag has been used or not. Either way, the most common case for a dependency
showing up in here is a transitive dependency that ended up in the public link interface of a library that is
actually used.
This isn’t a problem as such, but it can be useful to review if everything that’s in the public link interface is really needed there, or if we are leaking implementation details to the outside. Fixing this has no immediate impact on the overall dependency situation, but it does enable future changes without breaking the public interface.

It’s also worth noting that “unused” in this context merely means that no symbol from a library is used by the
component linking to it. That doesn’t however mean that the library can safely be removed, there are a few special
cases like ICU’s data library that are used by other means (and that’s also why we can’t just use --as-needed
unconditionally
everywhere).
Outlook
There’s of course more that could be done here, for example combining the graph visualization with edge weights based on the symbol counts.
I also should finally get ELF Dissector into the KDE release service to make it easier available via distribution packages. Getting it built as standalone app bundle is unfortunately tricky due to its dependency on internal binutils API.