Skip to content

Stop exporting the built-in filter class structs - #6764

Open
brtnfld wants to merge 6 commits into
HDFGroup:developfrom
brtnfld:fix/h5z-static-filter-classes
Open

brtnfld wants to merge 6 commits into
HDFGroup:developfrom
brtnfld:fix/h5z-static-filter-classes

Conversation

@brtnfld

@brtnfld brtnfld commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Describe your changes

libhdf5 exported the class structs of its built-in filters (H5Z_DEFLATE, H5Z_SHUFFLE, H5Z_FLETCHER32, H5Z_NBIT, H5Z_SCALEOFFSET, H5Z_SZIP). They are declared in the private H5Zpkg.h, but H5_DLLVAR and default symbol visibility put them in the shared library's exported symbols.

Filter plugins built from a copy of HDF5's filter source define globals with the same names; netCDF-C's deflate, shuffle and fletcher32 plugins do. On Linux, when such a plugin is loaded into a process that already has libhdf5, the plugin's reference to its own struct binds to libhdf5's copy, so H5PLget_plugin_info() returns libhdf5's struct and filter function. This has been silent because the structs matched. #6710 changes the built-in structs to H5Z_class3_t with version 3; netCDF-C's NCZarr loader then rejects the plugins (libnczarr/zplugins.c:413) and the netCDF CI job fails.

This makes the six structs static, the same way the VFD and native VOL class structs already are. Each filter file now registers its own filter through H5Z__<filter>_register() (like H5FD__sec2_register()), so no pointer to the struct leaves its file; the szip encoder check moved into H5Z__szip_register(). Internal linkage keeps them out of the export table with any compiler, without visibility attributes.

  • test/dsets.c: new test_filter_class_symbols() checks with dlsym(RTLD_DEFAULT, ...) that none of the six names is exported. It fails on develop and passes with this change, and is skipped where RTLD_DEFAULT is not available.
  • With Implement thread-safe filters #6710's changes plus this fix, netCDF-C's nczarr_test passes 34/34 locally (23 fail without it).

Once #6710 merges, this needs a rebase onto the H5Z_class3_t structs; the change itself is the same.

Issue ticket number (GitHub or JIRA)

Related to #6710 (netCDF CI failure).

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Review Checklist

This PR touches the following areas. Each needs a sign-off
from its listed owners before merging.

✅ All areas have been signed off.

@brtnfld
brtnfld requested review from fortnern and removed request for mattjala October 21, 2026 13:49
Comment thread src/H5Zscaleoffset.c Outdated
*-------------------------------------------------------------------------
*/
const H5Z_class2_t *
H5Z__scaleoffset_class(void)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just as a design suggestion, these functions could alternatively self-register the filter so that they don't return a pointer to internal details from a function that is exposed

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.

I agree, I was going to make the same suggestion, create H5Z__deflate_register(), etc. analogous to H5FD__sec2_register(). These functions would then call H5Z_register with their private class structs. While the way it is now works, it is slightly strange (if legal) to pass the address of a static variable outside the scope of that variable. I think it would be more straightforward to use e.g. H5Z__deflate_register(), and it would be more in line with the rest of the library.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done in c621bc5. Each filter file now has H5Z__<filter>_register(), like H5FD__sec2_register(), and the szip encoder check moved into H5Z__szip_register(). The structs never leave their files now; test/dsets.c gets the szip class through H5Z_find().

@brtnfld
brtnfld requested a review from jhendersonHDF October 21, 2026 16:44
@github-actions
github-actions Bot removed the request for review from fortnern October 21, 2026 16:45
fortnern
fortnern previously approved these changes Oct 21, 2026
fortnern
fortnern previously approved these changes Oct 21, 2026
The class structs of the built-in filters (H5Z_DEFLATE, H5Z_SHUFFLE,
H5Z_FLETCHER32, H5Z_NBIT, H5Z_SCALEOFFSET, H5Z_SZIP) were exported
from libhdf5. Filter plugins built from a copy of HDF5's filter source,
such as netCDF-C's deflate, shuffle and fletcher32 plugins, define
globals with the same names, and on ELF platforms the plugin's
references to its own struct bound to libhdf5's copy instead.

Make the structs static, as the VFD and native VOL class structs
already are, and reach them through package accessors. Add a test that
checks none of the names is exported.
Replace the H5Z__<filter>_class() accessors with H5Z__<filter>_register()
functions that call H5Z_register() on the file's static class struct, as
H5FD__sec2_register() and the other VFDs do, so no pointer to the
internal struct leaves its file. The szip encoder check moves into
H5Z__szip_register(), and test/dsets.c looks the szip class up with
H5Z_find().
@github-actions
github-actions Bot removed the request for review from jhendersonHDF October 21, 2026 20:32
Comment thread test/dsets.c Outdated

#if defined(H5_HAVE_DLFCN_H) && defined(RTLD_DEFAULT)
for (u = 0; u < NELMTS(names); u++)
if (dlsym(RTLD_DEFAULT, names[u]) != NULL) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is maybe fine as a quick hack but consider that projects already appear to be exporting these symbols publicly as well, so if HDF5 is being built as a sub-project and this test is run, it has the potential to fail for reasons unrelated to what this is testing for.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good point, fixed in 4b7e5a6. The test now looks the names up in libhdf5's own shared object (found with dladdr() on H5open, opened with RTLD_NOLOAD) and only fails if the symbol found is inside libhdf5, so a definition elsewhere in the process doesn't count. It's skipped for static builds.

I checked it by preloading a library that exports its own H5Z_DEFLATE: the test passes against this PR's library and still fails when libhdf5 exports the structs. Incidentally, with that preload the unfixed libhdf5 itself breaks: its own references to H5Z_DEFLATE bind to the foreign definition, and dsets segfaults in the filter tests. This PR fixes that too.

@brtnfld brtnfld Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Do you think we need a test for this, in general, or should I remove it?

@jhendersonHDF jhendersonHDF Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Generally speaking I would avoid testing for specific symbol names at all since it can be hard to predict what is going to be exported at any given time, especially when dlopen() is involved (let's say the library dlopens a filter like blosc which may in turn dlopen its own copy of an H5Z_SZIP).

hyoklee added a commit to hyoklee/hdf5 that referenced this pull request Oct 21, 2026
…lass structs)

# Conflicts:
#	release_docs/CHANGELOG.md
dlsym(RTLD_DEFAULT, ...) searches the whole process, so the test failed
when another object, such as a project that builds HDF5 as a
sub-project, defines one of these names. Look the names up in the
library's own shared object instead and only count a symbol found
inside it. Skip the test when the library is not a shared object.
hyoklee
hyoklee previously approved these changes Oct 21, 2026
Comment thread release_docs/CHANGELOG.md Outdated

The class structs of the built-in filters (H5Z_DEFLATE, H5Z_SHUFFLE, H5Z_FLETCHER32,
H5Z_NBIT, H5Z_SCALEOFFSET and H5Z_SZIP) were exported from the shared library. Filter
plugins built from a copy of HDF5's filter source, such as netCDF-C's deflate, shuffle

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.

shuffle -> shuffle,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Applied in 02a95af, thanks.

Comment thread release_docs/CHANGELOG.md
The class structs of the built-in filters (H5Z_DEFLATE, H5Z_SHUFFLE, H5Z_FLETCHER32,
H5Z_NBIT, H5Z_SCALEOFFSET and H5Z_SZIP) were exported from the shared library. Filter
plugins built from a copy of HDF5's filter source, such as netCDF-C's deflate, shuffle
and fletcher32 plugins, define globals with the same names, and on ELF platforms the

@hyoklee hyoklee Oct 21, 2026 •

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.

ELF platforms the -> ELF platforms where the

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch: "bound" was meant as the verb, and it is easy to misread. "Where" would leave the clause without a verb, though, so I reworded it instead: "...on ELF platforms the plugin's references to its own struct were resolved to the library's copy instead." (02a95af). Does that read clearly now?

Comment thread release_docs/CHANGELOG.md Outdated
### Stopped exporting the built-in filter class structs

The class structs of the built-in filters (H5Z_DEFLATE, H5Z_SHUFFLE, H5Z_FLETCHER32,
H5Z_NBIT, H5Z_SCALEOFFSET and H5Z_SZIP) were exported from the shared library. Filter

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.

H5Z_SCALEOFFSET and H5Z_SZIP -> `H5Z_SCALEOFFSET, and H5Z_SZIP

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks, applied in 02a95af. The CHANGELOG uses the serial comma elsewhere, so this is more consistent.

Address review: add the serial commas the rest of the CHANGELOG uses, and
replace "references ... bound to the library's copy", which read as a noun
phrase, with "were resolved to the library's copy".
@brtnfld

brtnfld commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the careful read, @hyoklee. All three are addressed in 02a95af.

One small request for future wording nits: could you post them as suggestion blocks (the "Insert a suggestion" button in the review comment box)? Then I can apply them straight from the PR in one batch commit instead of editing each one by hand. Thanks!

hyoklee
hyoklee previously approved these changes Oct 22, 2026

@hyoklee hyoklee 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.

Reviewed documentation only

Testing for specific exported symbol names is fragile: what a process
exports depends on what else is loaded, including filter plugins that
dlopen their own copies of these names.
@brtnfld

brtnfld commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Removed. Whether a given name is exported depends on what else is loaded, so the test was checking the environment as much as the fix.

@github-actions

Copy link
Copy Markdown
Contributor

🎉 All checklist items are signed off — @brtnfld, this PR is ready to merge.

@github-actions github-actions Bot added the checklist-complete All review checklist areas are signed off label Oct 22, 2026

This branch has not been deployed

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

Labels

checklist-complete All review checklist areas are signed off

Projects

Status: To be triaged

Development

Successfully merging this pull request may close these issues.

4 participants