Skip to content

Add FREEIMAGE_WITH_EXTERNAL_PLUGINS option - #99

Open
jojastahl wants to merge 1 commit into
agruzdev:masterfrom
demig-de:pr/no-external-plugins
Open

jojastahl wants to merge 1 commit into
agruzdev:masterfrom
demig-de:pr/no-external-plugins

Conversation

@jojastahl

Copy link
Copy Markdown

Adds the CMake option FREEIMAGE_WITH_EXTERNAL_PLUGINS (default ON) which controls whether the library loads external *.fip plugins when it is loaded.

Motivation

On Windows, DllMain calls FreeImage_Initialise(FALSE), which calls LoadLibrary for every *.fip file in the directory of the executable and in its plugins\ subdirectory. This happens during DLL_PROCESS_ATTACH, before any application code runs, so an application cannot opt out. Consequences:

  • A DLL placed next to the executable (renamed to *.fip) is loaded into the process. Applications that restrict which code they load cannot use the library without this exception.
  • The function changes the current directory with SetCurrentDirectoryW and loads libraries while the loader lock is held, which is discouraged.
  • On x64 and ARM64 the feature does not work: the entry point is looked up by the name _Init@8, which is the x86 stdcall decoration. So each *.fip gets loaded, its DllMain runs, no plugin is registered, and the library is unloaded again. The risk stays, and there is no benefit.

See the linked issue for the full background.

Changes

  • New option FREEIMAGE_WITH_EXTERNAL_PLUGINS, default ON. The default behaviour is unchanged. The option defines FREEIMAGE_WITH_EXTERNAL_PLUGINS as 1 or 0 publicly. If the macro is not defined at all (a build outside CMake), the behaviour is the same as ON.
  • DllMain (and the shared-object constructor on other platforms) calls FreeImage_Initialise(!FREEIMAGE_WITH_EXTERNAL_PLUGINS). The parameter load_local_plugins_only skips the loading of external plugins, so it is TRUE if the option is OFF.
  • If the option is OFF, the loading code in FreeImage_Initialise is not compiled at all. An explicit call FreeImage_Initialise(FALSE) by an application then also loads no external plugins. The FreeImage_Initialise signature is unchanged.

Scope — what this PR does not change

This PR only makes the external-plugin loader optional. It does not fix the loader itself:

  • With the option ON (the default), the loader behaves exactly as before, including the broken _Init@8 entry-point lookup on x64/ARM64. Nothing in the default build changes.
  • The x64/ARM64 brokenness is described under Motivation only to explain why making the loader optional is low-risk, not as something this PR repairs.
  • Fixing _Init@8 would mean an arch-aware lookup (the undecorated Init on x64/ARM64 vs. the stdcall-decorated _Init@8 on x86), which changes the external-plugin entry-point contract. I have deliberately left that out of this PR: I build and ship no *.fip plugins, so I cannot test such a fix. I'm happy to open it as a separate PR, or to leave it to you — see the linked issue.

Testing

Built with Visual Studio 2026 (v145, x64, RelWithDebInfo) with FREEIMAGE_BUILD_TESTS=ON, libraries limited to LibJPEG and LibPNG, option OFF. The resulting DLL does not contain the *.fip search pattern. The default build (option ON) still contains the loader.

Part of #97.

On Windows the library loads every *.fip file found in the directory of
the executable or in its "plugins" subdirectory while it is being loaded
(DllMain -> FreeImage_Initialise(FALSE)). This can be neither avoided nor
controlled by the application, because it happens before any of the
application code runs. It means that a DLL placed next to the executable
gets loaded into the process, which is unwanted for applications that
restrict the code they load. Besides that, the current directory is
changed with SetCurrentDirectoryW() while the loader lock is held.

On the x64 and ARM64 targets the feature does not even work: the init
function is looked up by the name "_Init@8", which exists only in the
32-bit stdcall decoration. So each *.fip is loaded, its DllMain is run,
and it is unloaded again without registering anything.

Add the CMake option FREEIMAGE_WITH_EXTERNAL_PLUGINS, default ON, which
keeps the behaviour as it is. If it is OFF, then DllMain passes
load_local_plugins_only = TRUE, and the loading code is not compiled into
the library.

This does not fix the loader itself. With the option ON the behaviour is
unchanged, including the broken _Init@8 lookup on x64/ARM64 described
above. Fixing that would require an architecture-dependent entry-point
name (undecorated "Init" on x64/ARM64 vs. the stdcall-decorated "_Init@8"
on x86), which changes the external-plugin entry-point contract. It is
left out here on purpose: I build and ship no *.fip plugins and cannot
test such a change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant