Skip to content

COMP: Mangle libpng symbols added since the last vendored update - #6821

Merged
dzenanz merged 1 commit into
InsightSoftwareConsortium:mainfrom
hjmjohnson:fix/png-mangle-stale-symbols
Aug 31, 2026
Merged

dzenanz merged 1 commit into
InsightSoftwareConsortium:mainfrom
hjmjohnson:fix/png-mangle-stale-symbols

Conversation

@hjmjohnson

@hjmjohnson hjmjohnson commented Aug 31, 2026 •

Copy link
Copy Markdown
Member

Problem

Three static Windows nightlies fail with 10 build errors each — Windows11-VS2022-Static-Release,
Windows11-VS2022-Static-DebugTBB, Windows11-VS2019-Static-ReleaseTBB — all from one root cause:

vtkpng-9.7d.lib(png.obj)     : error LNK2005: png_fixed_ITU already defined in itkpng…
vtkpng-9.7d.lib(pngwutil.obj): error LNK2005: png_write_cICP already defined in itkpng…
vtkpng-9.7d.lib(pngrutil.obj): error LNK2005: png_handle_chunk already defined in itkpng…
…
ITKVtkGlueTestDriver.exe : fatal error LNK1169: one or more multiply defined symbols found

Root cause

Not a VTK problem, and not new upstream behavior. itkpng is supposed to rename every exported symbol
through itk_png_mangle.h.in, so that a program may link it alongside another libpng. That header is
regenerated by hand from nm output; the recipe is documented in its own comment block. It was not re-run
when UpdateFromUpstream.sh moved the pin to v1.6.54.

libpng 1.6.54 exports nine functions the header predates:

png_fixed_ITU            png_write_cICP
png_handle_chunk         png_write_cLLI_fixed
png_resolve_file_gamma   png_write_mDCV_fixed
png_set_rgb_coefficients png_XYZ_from_xy
png_xy_from_XYZ

These are exactly the nine LNK2005 names. Shared builds resolve the duplicate at the DLL boundary, which
is why only the static Windows configurations fail — the leak itself is present on every platform.

A second, independent defect surfaced while measuring the first. Seven ARM NEON entries read:

#define _png_read_filter_row_up_neon @MANGLE_PREFIX@___png_read_filter_row_up_neon

The leading underscore is Mach-O symbol decoration, not part of the C identifier — pngpriv.h:1529
declares png_read_filter_row_up_neon. The recipe was last run on macOS without stripping the decoration,
so these defines name a token that appears nowhere in the sources and have never applied. All seven
functions export unmangled from every ARM build.

Fix

Add the nine missing defines and replace the seven decorated NEON entries with the identifiers as spelled
in pngpriv.h. No source file changes; the fix is entirely in the rename table.

png_fixed_ITU is also a function-like macro at pngpriv.h:818, guarded by
PNG_FIXED_POINT_MACRO_SUPPORTED. That macro is not enabled in ITK's pnglibconf.h, and png_fixed — the
same dual macro/function shape — has been mangled all along, so the object-like define is safe here.

Verification

Built the itkpng target and inspected the static archive, on macOS/arm64, Apple clang, Release,
against main at 2bdb783:

$ nm lib/libitkpng-6.0.a | awk '$2 ~ /^[TDRSB]$/ {print $3}' | sed 's/^_//' | sort -u | grep -v '^itk_png'
Tree Unmangled png_* symbols exported
main 16
this branch 0

The 16 are the 9 new libpng symbols plus the 7 NEON functions. pre-commit run --all-files passes.

Scope of verification: macOS/arm64 exercises the NEON half directly and proves the rename table itself.
The LNK2005 symptom needs a static Windows build linking both itkpng and vtkpng, which was not run
here; the Windows static dashboards are the appropriate regression check before merging.

AI assistance
  • Tool: Claude Code (claude-opus-5)
  • Role: dashboard triage, root-cause analysis, symbol measurement
  • Contribution: identified the stale rename table as the cause of the
    LNK2005 failures, and separately found the seven decorated ARM NEON
    entries by diffing nm output against the mangle header
  • All changes were built and verified locally before committing

@github-actions github-actions Bot added type:Compiler Compiler support or related warnings area:ThirdParty Issues affecting the ThirdParty module type:Data Changes to testing data labels Aug 31, 2026
@hjmjohnson
hjmjohnson marked this pull request as ready for review August 31, 2026 12:46
@greptile-apps

greptile-apps Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This change updates the vendored libpng mangling template for symbols added in libpng 1.6.54 and corrects the ARM NEON filter-row macro names. The configured header was checked against vendored declarations and definitions, and the ordinary libpng implementations were compiled and inspected: all updated mappings use the itk_png_* prefix and the previously raw ordinary png_* linkage names are absent.

Confidence Score: 5/5

The symbol-mangling update appears safe to merge: the tested mappings produce the intended prefixed linker symbols without exposing the previously unmangled ordinary libpng names.

The focused verification checked every changed macro expansion against the vendored source tokens and exercised compilation plus archive symbol inspection for all nine ordinary symbols. No defect was found.

Files Needing Attention: No additional files need attention. Native ARM object compilation was not performed on the x86_64 host, but the seven ARM NEON source-level names and configured macro expansions were directly checked.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex compiled the parent header and captured the raw linkage names for the nine ordinary symbols in the compiled archive.
  • T-Rex ran the focused PNG mangle validator against the updated template and vendored declarations, and verified that the after state expands to itk_png_* names with the original nine symbols renamed and no longer present as raw names.
  • T-Rex preprocessed the updated header and performed archive inspections of the ordinary implementations to confirm the correct linkage names, including that the obsolete leading-underscore Mach-O spelling is not retained as a macro key.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "COMP: Mangle libpng symbols added since ..." | Re-trigger Greptile

@hjmjohnson
hjmjohnson force-pushed the fix/png-mangle-stale-symbols branch from 92db8a8 to eb73406 Compare August 31, 2026 13:31
itk_png_mangle.h.in predates libpng 1.6.54, so nine exported symbols
keep their upstream names: png_fixed_ITU, png_handle_chunk,
png_resolve_file_gamma, png_set_rgb_coefficients, png_write_cICP,
png_write_cLLI_fixed, png_write_mDCV_fixed, png_XYZ_from_xy and
png_xy_from_XYZ.  A static build linking both itkpng and VTK's vtkpng
then fails with LNK2005; a shared build hides the duplicate behind the
DLL boundary.

The seven ARM NEON filter-row entries carry the leading underscore of
the Mach-O symbol, so they define a token that never appears in the
sources.  pngpriv.h spells them without it.
@hjmjohnson
hjmjohnson force-pushed the fix/png-mangle-stale-symbols branch from eb73406 to 03d3c79 Compare August 31, 2026 13:38
@hjmjohnson

Copy link
Copy Markdown
Member Author

Reproduced the LNK2005 on Windows/MSVC and validated this fix against it. Details below, including two findings that refine the reproduction conditions.

Environment: MSVC 19.38.33145 (VS 2022, toolset 14.38), Ninja, Release x64, static (BUILD_SHARED_LIBS=OFF) for both ITK and VTK. ITK main at 2bdb783.

Symbol-level before/after

Built the itkpng target and dumped the static archive's public symbols (dumpbin /linkermember:1):

before (main) after (this PR)
total public symbols 807 807
unprefixed png_* 9 0
itk_png_* 372 381

A byte-order diff of the full symbol sets shows exactly 9 removed and exactly 9 added — a clean 1:1 rename to itk_png_*, with no other symbol changed and no net change in count. The nine are exactly the ones named in the commit message.

LNK2005 reproduction

The VTK version matters, and 9.3.1 cannot reproduce this. VTK 9.3.1 vendors libpng 1.6.37, which does not contain these functions at all, and its vtkpng exports zero unprefixed png_* symbols. Against that VTK there is no duplicate definition and no possible collision.

VTK master (707a36f4) vendors libpng 1.6.58, and its vtk_png_mangle.h does not mangle these same nine names — so vtkpng leaks them too. That is the actual collision: both archives define the same nine unprefixed symbols.

Forcing both archives into one link:

link /WHOLEARCHIVE:itkpng-6.0.lib /WHOLEARCHIVE:vtkpng-9.7.lib ...

Before (ITK main) — exit 1169, 9 × LNK2005:

vtkpng-9.7.lib(pngwutil.c.obj) : error LNK2005: png_write_cICP already defined in itkpng-6.0.lib(pngwutil.c.obj)
vtkpng-9.7.lib(pngwutil.c.obj) : error LNK2005: png_write_cLLI_fixed already defined in itkpng-6.0.lib(pngwutil.c.obj)
vtkpng-9.7.lib(pngwutil.c.obj) : error LNK2005: png_write_mDCV_fixed already defined in itkpng-6.0.lib(pngwutil.c.obj)
vtkpng-9.7.lib(pngrutil.c.obj) : error LNK2005: png_handle_chunk already defined in itkpng-6.0.lib(pngrutil.c.obj)
vtkpng-9.7.lib(pngrtran.c.obj) : error LNK2005: png_resolve_file_gamma already defined in itkpng-6.0.lib(pngrtran.c.obj)
vtkpng-9.7.lib(png.c.obj)      : error LNK2005: png_XYZ_from_xy already defined in itkpng-6.0.lib(png.c.obj)
vtkpng-9.7.lib(png.c.obj)      : error LNK2005: png_fixed_ITU already defined in itkpng-6.0.lib(png.c.obj)
vtkpng-9.7.lib(png.c.obj)      : error LNK2005: png_set_rgb_coefficients already defined in itkpng-6.0.lib(png.c.obj)
vtkpng-9.7.lib(png.c.obj)      : error LNK2005: png_xy_from_XYZ already defined in itkpng-6.0.lib(png.c.obj)

After (this PR) — exit 0, 0 × LNK2005, executable produced. Identical link command; only itkpng differs.

Caveat on the ITKVtkGlueTestDriver path

I could not reproduce the failure through ITKVtkGlueTestDriver itself in the configuration I built. With VTK_RENDERING_BACKEND=None, that target links cleanly (exit 0, zero LNK2005) even against a leaking itkpng and a leaking VTK master vtkpng, with both archives on the link line.

The reason is that QuickView.cxx is the only VtkGlue source that touches VTK's PNG classes, and it is excluded when rendering is disabled. Without it, nothing references VTK's PNG code, so the linker never pulls vtkpng's objects and the duplicate definitions never meet. Reproducing through the test driver therefore requires a rendering-enabled VTK, which is presumably what the failing dashboard uses. The /WHOLEARCHIVE link above isolates the same defect deterministically without needing that configuration.

This does not weaken the fix — it just means the trigger is narrower than "any static build linking both".

Unrelated observation, VTK side

VTK master's vtkpng leaks ten further symbols that ITK does not:

png_get_cICP  png_get_cLLI  png_get_cLLI_fixed  png_get_mDCV  png_get_mDCV_fixed
png_set_cICP  png_set_cLLI  png_set_cLLI_fixed  png_set_mDCV  png_set_mDCV_fixed

These do not collide today only because ITK's mangle header happens to cover them. That is a gap on the Kitware side and is out of scope here, but it is worth reporting upstream to VTK — otherwise the same class of failure can reappear from the other direction after a future libpng bump.

Not covered

The seven ARM NEON entries in this change could not be exercised on x64. The correction looks right by inspection — the previous entries carried a leading underscore (the Mach-O nm form, e.g. _png_read_filter_row_avg3_neon), so they defined a token that never appears in the sources and mangled nothing — but confirming that empirically needs an ARM build.

@hjmjohnson

Copy link
Copy Markdown
Member Author

Follow-up: I have now reproduced the failure through ITKVtkGlueTestDriver itself, which my earlier comment could not. My caveat about the rendering-disabled configuration was correct, and the missing ingredient was exactly QuickView.cxx.

Configuration: static ITK + static VTK master (707a36f4, libpng 1.6.58), MSVC 19.38.33145, Ninja, Release x64, VTK_RENDERING_BACKEND=OpenGL2, Module_ITKVtkGlue=ON.

Before — ITK main (2bdb783)

ITKVtkGlueTestDriver fails to link:

vtkpng-9.7.lib(png.c.obj)      : error LNK2005: png_XYZ_from_xy already defined in itkpng-6.0.lib(png.c.obj)
vtkpng-9.7.lib(png.c.obj)      : error LNK2005: png_fixed_ITU already defined in itkpng-6.0.lib(png.c.obj)
vtkpng-9.7.lib(png.c.obj)      : error LNK2005: png_set_rgb_coefficients already defined in itkpng-6.0.lib(png.c.obj)
vtkpng-9.7.lib(png.c.obj)      : error LNK2005: png_xy_from_XYZ already defined in itkpng-6.0.lib(png.c.obj)
vtkpng-9.7.lib(pngwutil.c.obj) : error LNK2005: png_write_cICP already defined in itkpng-6.0.lib(pngwutil.c.obj)
vtkpng-9.7.lib(pngwutil.c.obj) : error LNK2005: png_write_cLLI_fixed already defined in itkpng-6.0.lib(pngwutil.c.obj)
vtkpng-9.7.lib(pngwutil.c.obj) : error LNK2005: png_write_mDCV_fixed already defined in itkpng-6.0.lib(pngwutil.c.obj)
vtkpng-9.7.lib(pngrutil.c.obj) : error LNK2005: png_handle_chunk already defined in itkpng-6.0.lib(pngrutil.c.obj)
vtkpng-9.7.lib(pngrtran.c.obj) : error LNK2005: png_resolve_file_gamma already defined in itkpng-6.0.lib(pngrtran.c.obj)
bin\ITKVtkGlueTestDriver.exe : fatal error LNK1169: one or more multiply defined symbols found

9 × LNK2005 + LNK1169, on exactly the nine symbols this PR mangles.

After — this PR

Same build tree, same VTK, only the mangle header changed:

[21/24] Linking CXX executable bin\ITKVtkGlueTestDriver.exe
LINK EXIT: 0    LNK2005: 0    LNK1169: 0
ITKVtkGlueTestDriver.exe -> 20.5 MB

Why the rendering backend decides it

With VTK_RENDERING_BACKEND=None the same link succeeds even with both leaking archives on the command line. QuickView.cxx is the only VtkGlue source that references VTK's PNG classes, and it is excluded when rendering is off. Nothing then pulls vtkpng's objects out of the archive, so the duplicate definitions never meet the linker.

Enabling rendering compiles QuickView.cxx, which drags in vtkpng's png.c.obj, pngwutil.c.obj, pngrutil.c.obj and pngrtran.c.obj alongside the ones ITK's PNG IO already pulled from itkpng — and those are precisely the four objects named in the errors above.

So the trigger is narrower than "any static build linking both": it needs a rendering-enabled VTK. That matches a normal dashboard configuration, but it is worth knowing when trying to reproduce.

Also, as noted before: VTK 9.3.1 cannot reproduce this at all — its libpng 1.6.37 does not contain these functions. VTK master is required.

Verified end to end: reproduced, fixed, and the resulting driver links clean.

@dzenanz dzenanz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM.

@dzenanz
dzenanz merged commit 6fe98c6 into InsightSoftwareConsortium:main Aug 31, 2026
22 of 23 checks passed
hjmjohnson added a commit that referenced this pull request Sep 17, 2026
COMP: Mangle libpng ARM NEON filter-row symbols (backport #6821 to release-5.4)
@hjmjohnson
hjmjohnson deleted the fix/png-mangle-stale-symbols branch October 29, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ThirdParty Issues affecting the ThirdParty module type:Compiler Compiler support or related warnings type:Data Changes to testing data

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants