Document the user thread safety model for elfutils libraries when built with --enable-thread-safety.
Signed-off-by: Aaron Merey <[email protected]> --- v2 changes: Added THREAD-SAFETY to Makefile.am and config/elfutils.spec.in. Mentioned _ELFUTILS_THREAD_SAFE. Distinguished library interfaces (elf_*, dwarf_*, etc.) from libdw.so and libelf.so. Clarified that debuginfod_client handles are not tied to the creating thread. Added example use cases for a phased approach that avoids caller-side locking as well as what to do if caller side locking is needed. Clarified that ancestor handles do not need to be re-derived when modifying derived handles. > On Thu, 2026-07-02 at 18:32 -0400, Aaron Merey wrote: > > +The following libelf and libdw public library functions are > > caller-serialized. > > +All other functions in these libraries are thread-safe. > > + > > + libelf elf32_checksum, elf32_newehdr, elf32_newphdr, elf32_xlatetof, > > + elf32_xlatetom, elf64_checksum, elf64_newehdr, elf64_newphdr, > > + elf64_xlatetof, elf64_xlatetom, elf_begin (non-NULL ref), > > + elf_cntl, elf_compress, elf_compress_gnu, elf_end, elf_fill, > > + elf_flagdata, elf_flagehdr, elf_flagelf, elf_flagphdr, > > + elf_flagscn, elf_flagshdr, elf_newdata, elf_newscn, elf_next, > > + elf_rand, elf_update > > + > > + gelf gelf_checksum, gelf_newehdr, gelf_newphdr, gelf_update_*, > > + gelf_xlatetof, gelf_xlatetom > > + > > + libdw dwarf_begin_elf, dwarf_cfi_end, dwarf_end, > > dwarf_new_oom_handler, > > + dwarf_setalt > > This looks right, but I don't really know how to verify. I plan on using AI-generated tests to try and validate all this. The tests will not be included in elfutils patches/contributions but they will be made publicly available. > Have to think hard what it means that Elf and Dwarf are cyclic, but > apart from that this hierarchy makes sense. I don't believe the Elf/Dwarf relationship is cyclic in a way that threatens this model. dwarf_getelf returns an Elf handle from a Dwarf but the Elf handle is identical to the one used to create the Dwarf. It does not create a brand new handle. Makefile.am | 2 +- THREAD-SAFETY | 127 ++++++++++++++++++++++++++++++++++++++++ config/elfutils.spec.in | 2 +- 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 THREAD-SAFETY diff --git a/Makefile.am b/Makefile.am index fa6fda34..98e49e76 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,7 +32,7 @@ SUBDIRS = config lib libelf libcpu backends libebl libdwelf libdwfl \ libdwfl_stacktrace libdw libasm debuginfod src po doc tests EXTRA_DIST = elfutils.spec GPG-KEY NOTES CONTRIBUTING SECURITY \ - COPYING COPYING-GPLV2 COPYING-LGPLV3 CONDUCT + COPYING COPYING-GPLV2 COPYING-LGPLV3 CONDUCT THREAD-SAFETY # Make sure the test install uses lib64 when $LIB will yield lib64. # Make sure the test build uses the same compiler, which on e.g. ppc64 diff --git a/THREAD-SAFETY b/THREAD-SAFETY new file mode 100644 index 00000000..a68ae0da --- /dev/null +++ b/THREAD-SAFETY @@ -0,0 +1,127 @@ +Elfutils Thread Safety + +Status: under development and not officially supported. Enable with configure +option `--enable-thread-safety`. If enabled, <elfutils/version.h> will +define macro _ELFUTILS_THREAD_SAFE to 1, otherwise this macro is not defined. + +Experimental support is available for the elf_* and gelf_* interfaces from +libelf.so as well as the dwarf_* interface from libdw.so. Thread safety +support for the dwfl_* interface is under development. The libasm, +libdwfl_stacktrace and libdwelf interfaces are not currently supported. + +libdebuginfod: A debuginfod_client handle can be used by at most one thread +at a time. Distinct client handles may be used concurrently in separate +threads. + +Elfutils library functions called on distinct library handles which share no +state may be called concurrently in multiple threads. Handles share state +if one is derived from another (see "Derived handles" below) either directly +or transitively. + +For cases where a library handle is shared across threads, library functions +are divided into two thread safety types: Thread-safe and caller-serialized. + +Thread-safe functions may run concurrently across threads even if the handles +are shared. + +A caller-serialized function must not run concurrently with any other library +function call (whether thread-safe or caller-serialized) on the same handle, +on any handle derived from it (see below), or on any handle it is derived from. +The caller is responsible for serializing these function calls. Ancestor +handles are not invalidated by caller-serialized functions being called +on derived handles. + +Typical use cases involve a caller-serialized phase where library handles +are initialized, followed by a concurrent phase where thread-safe library +functions are freely called across multiple threads. Once a concurrent +phase is finished handles can be modified or destroyed in a caller-serialized +manner. This phased approach helps avoid the need for locking on the +caller side. If caller-side locking is required one can associate a lock +with the highest handle in the derivation chain (typically an Elf or Dwarf) +being accessed concurrently. + + +Caller-serialized functions + +Currently thread safety is supported for the elf_*, gelf_* and dwarf_* +interfaces. The following functions from these interfaces are +caller-serialized. All other functions from these interfaces are thread-safe. + + elf_* elf32_checksum, elf32_newehdr, elf32_newphdr, elf32_xlatetof, + elf32_xlatetom, elf64_checksum, elf64_newehdr, elf64_newphdr, + elf64_xlatetof, elf64_xlatetom, elf_begin (non-NULL ref), + elf_cntl, elf_compress, elf_compress_gnu, elf_end, elf_fill, + elf_flagdata, elf_flagehdr, elf_flagelf, elf_flagphdr, + elf_flagscn, elf_flagshdr, elf_newdata, elf_newscn, elf_next, + elf_rand, elf_update + + gelf_* gelf_checksum, gelf_newehdr, gelf_newphdr, gelf_update_*, + gelf_xlatetof, gelf_xlatetom + + dwarf_* dwarf_begin_elf, dwarf_cfi_end, dwarf_end, dwarf_new_oom_handler, + dwarf_setalt + + dwfl_* TBD + + +Derived handles + +A handle is derived from another when it is obtained from it and shares or +copies its state. Concurrency limitations of caller-serialized functions apply +to both derived handles and ancestor handles. + +For example, an Elf_Data is derived from an Elf_Scn and the Elf_Scn is derived +from an Elf. In this case the caller must ensure that elf_flagdata is not +called on the Elf_Data concurrently with either elf_newdata called on its +associated Elf_Scn or elf_update called on its associated Elf. + +After a caller-serialized function runs, handles previously derived from any +handles used as arguments may be invalid and should be re-derived before +further use. Ancestor handles do not need to be re-derived. + +Indentation below shows derivation. ElfXX refers to both Elf32 and Elf64. + +libelf: + Elf + Elf_Scn + Elf_Data + ElfXX_* except ElfXX_Ehdr, ElfXX_Phdr and ElfXX_Shdr + GElf_* except GElf_Ehdr, GElf_Phdr and GElf_Shdr + ElfXX_Shdr + GElf_Shdr + ElfXX_Ehdr + ElfXX_Phdr + GElf_Ehdr + GElf_Phdr + Elf_Arsym + Elf archive member, elf_clone + Elf_Arhdr + +libdw: + Dwarf derived from the Elf it was opened with + Dwarf_CU + Dwarf_Die + Dwarf_Attribute + Dwarf_Op + Dwarf_Block + Dwarf_Lines + Dwarf_Line + Dwarf_Files + Dwarf_Abbrev + Dwarf_Aranges + Dwarf_Arange + Dwarf_Macro + Dwarf_Attribute + Dwarf_Files + Dwarf_Global + Dwarf_CFI + Dwarf_Frame + Dwarf_Op + Dwarf alternate, split, .dwp + Dwarf_CFI derived from Elf + Dwarf_Frame + Dwarf_Op + Dwarf_CFI_Entry derived from Elf_Data + +libdwfl: + TBD diff --git a/config/elfutils.spec.in b/config/elfutils.spec.in index cbbc5433..6b609cb4 100644 --- a/config/elfutils.spec.in +++ b/config/elfutils.spec.in @@ -261,7 +261,7 @@ fi %files %license COPYING COPYING-GPLV2 COPYING-LGPLV3 doc/COPYING-GFDL -%doc README TODO CONTRIBUTING SECURITY CONDUCT +%doc README TODO CONTRIBUTING SECURITY CONDUCT THREAD-SAFETY %{_bindir}/eu-addr2line %{_bindir}/eu-ar %{_bindir}/eu-elfclassify -- 2.55.0
