InsightSoftwareConsortium / InsightSoftwareConsortium/ITK

Vendored libraries share a SONAME, so two ITK builds cross-bind on Linux

Open
#6,819 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
1.7k
Forks
748
Avg merge
1d 1h
Merged PRs (30d)
64

Description

On Linux, two independently built ITKs loaded into one process share their vendored libraries, because those libraries live outside `namespace itk` and both builds ship them with **identical SONAMEs**. The dynamic linker keeps one copy, so one ITK's `ITKCommon` binds into the other ITK's VNL.

This is pre-existing and independent of any namespace mechanism, but it is a blocker for the goal in #6786 ("several independently distributed ITK builds coexisting in one process"), so it needs tracking on its own.

Measurement

Two `BUILD_SHARED_LIBS=ON` ITK trees built from the same source, differing only in an inline ABI namespace applied to `namespace itk` (the #6786 mechanism). Both loaded into one process; `LD_DEBUG=bindings` trace inspected.

Ubuntu 24.04, GCC 13.3.0, glibc 2.39, x86_64.

Total cross-library bindings between the two trees: 138, over 80 distinct symbols. **None are `itk::` symbols** — the C++ surface is cleanly separated. The breakdown:

| Caller → definer | count | why |
|---|---|---|
| build B `libITKCommon` → build A `libitksys` | 37 | outside `namespace itk`, same SONAME |
| build B `libITKCommon` → build A `libitkvnl` | 18 | outside `namespace itk`, same SONAME |
| build B `libITKCommon` → build A `libitkdouble-conversion` | 2 | outside `namespace itk`, same SONAME |
| build B `libITKCommon` → build A `libITKCommon` | 3 | header-only third-party statics, see below |

Example bindings: `vnl_vector::set_size`, `vnl_vector::~vnl_vector`, and 37 KWSys entry points resolving from the wrong build.

Both trees emit `libitkvnl-6.0.so.1`, `libitksys-6.0.so.1`, etc. Under glibc's flat symbol namespace the first-loaded definition wins for every subsequent reference.

Why macOS does not show this

macOS uses a two-level namespace: each Mach-O records the defining library for every imported symbol, so build B's `ITKCommon` binds to its own VNL by construction regardless of names. The same two builds on macOS show only 3 cross-bindings, all header-only statics.

So this is genuinely Linux-specific in its *observable* form, though the underlying ambiguity (two libraries, same name, same symbols) exists on any platform that resolves by name alone.

The residual three, which a SONAME change would not fix

Present identically on both platforms:

```
Eigen::internal::manage_caching_sizes(Eigen::internal::Action, long*, long*, long*)::m_cacheSizes

std::_Sp_make_shared_tag::_S_ti()::__tag
```

These are **header-only** function-local statics instantiated into both `libITKCommon` objects. No library-naming scheme reaches them, because the symbol is `Eigen::internal::…` / `std::…` rather than anything ITK controls. Two ITKs would share one Eigen CPU-cache-size cache — harmless in this instance, since it caches immutable hardware properties, but the pattern is worth knowing about: any header-only third-party code vendored outside `itk::` stays shared.

## Possible directions

Not a recommendation, just the options as they appear:

1. **Suffix the vendored library names** when a distinguishing build option is set — the approach VTK takes for its third-party libraries. Fits naturally with `ITK_ABI_NAMESPACE_NAME` if that lands from #6786, but is independently meaningful.
2. **Version the SONAMEs** by something other than the ITK version, so two same-version builds differ.
3. **Link the vendored libraries statically** into the ITK modules that use them, so nothing is exported to collide over.
4. **Accept it and document the limit** — two ITKs share vendored code, and that is safe for stateless libraries but not for any with mutable global state.

Option 4 needs an audit to be defensible: KWSys and VNL are not obviously free of process-global state, and sharing them across two ITKs with different build configurations is a stronger assumption than sharing Eigen's cache-size table.

Contributor guide

Open the contributing guide

Research direction

Start with the two BUILD_SHARED_LIBS=ON builds described in the measurement and reproduce their Linux bindings using LD_DEBUG=bindings. Compare vendored-library name suffixing, SONAME versioning, static linking, and documentation as possible directions, then audit KWSys and VNL for process-global state. Done requires a selected, defensible approach that prevents unsafe cross-binding or documents the verified limit.

Written by the indexing model from the issue text.

Assessment

Tech stack
cmake, cpp, linux
Domain
build-system, operating-systems
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.