Plugin load failures after enabling Smart Linker in Delphi service builds
0 reputation · 16 Apr 2025, 09:46 UTC
0 reputation · 16 Apr 2025, 09:46 UTC
The goal is to reduce the executable size and memory footprint of a low‑traffic Delphi service by enabling the Smart Linker, while still allowing dynamically loaded plugins to access the required RTTI and class information.
However, the Smart Linker may treat units that are only referenced through RTTI as unused and remove them, leading to missing symbols when plugins are loaded at runtime. This creates uncertainty about which units must be preserved and whether the default smart‑linking settings are safe for plugin‑based architectures.
Which linker options or directives can preserve RTTI for dynamically loaded units while still removing truly unused code?
How can we automatically detect which units contain required RTTI before enabling aggressive smart linking?
Is there a way to selectively disable smart linking for specific units or packages without disabling it globally?
32275 reputation · 16 Apr 2025, 13:57 UTC
Smart linking in a Delphi EXE removes any unit whose symbols are not reachable from the application entry point. If a plugin discovers a class only through RTTI at runtime, the linker treats that class as dead code and strips it. The fix is to force a reference from live code; there is no supported per-unit directive to disable smart linking for a single unit in a classic EXE.
Likely explanation: your plugin-facing units appear in the uses clause, but no reachable symbol references them. The linker prunes them, and the plugin later fails with EClassNotFound or a nil TRttiType.
Confirmed mechanics: smart linking is reachability-based. {$M+} and {$METHODINFO} control RTTI generation, not retention. A unit that is dropped never runs its initialization section, so RegisterClass calls inside a pruned unit are lost.
{$M+} for published-member RTTI. If the plugin uses extended RTTI via TRttiContext, ensure {$METHODINFO} is active (on by default in modern Delphi) and that the class has fields or methods to attach RTTI to.uses clause and reference one of its symbols from live code. Inside that unit, call RegisterClass(TFoo) for each plugin-facing class. Because the unit is now referenced, the linker keeps it and its RTTI.GetClass('TName') is non-nil and that the type appears in TRttiContext.GetTypes. Log any missing type.RegisterClass call and rebuild.{$WEAKPACKAGEUNIT} affects package requires resolution, not EXE dead-code elimination. There is no supported per-unit smart-link switch for a classic Delphi EXE; the granularity is the project-level option.
Which Delphi version are you building with? The default state of Smart Linked (commonly on in Release) and the exact compiler directive list vary by version, so the project-level options and any version-specific workaround should be confirmed in Project Options > Delphi Compiler > Linking for your exact toolchain.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.