Browse documentation

Win32 exception metadata

Executive Summary

  • Every FPC Win32 executable gets a checked SafeSEH table after linking.
  • The table lists the handlers from reviewed, pinned toolchain inputs.
  • Unsupported images or changed audited inputs fail the build.
  • Windows CI compares exception behavior before and after the metadata change.

Build behavior

Every FPC i386-win32 executable built by build.pas or the full CI compiler driver receives a SafeSEH table before the build succeeds. Windows can then validate exception handlers against the table instead of querying virtual memory for each frame. The post-link step is Pascal and adds no Python requirement to local builds. Other targets, including x86_64-win64, keep their existing output.

The build requests FPC's linker map, verifies the audited toolchain and handler manifest, and appends a read-only load-configuration section containing sorted handler RVAs. Original code and data sections remain unchanged. Unsupported images fail the build: signed or relocatable images, existing load configuration, unknown handlers, changed audited objects, and malformed or unsupported PE layouts are never silently accepted. Sign an executable only after this step.

For cross-compilation, FPC_TARGET_CPU=i386 FPC_TARGET_OS=win32 ./build.pas --prod selects the target; install that target's FPC units first. CI uses the same post-link implementation and checks every staged .exe before upload.

Each unpinned input must be a compiler output in the build's designated unit directory and match a unique repository source. Finalization records source and object hashes in <map>.safeseh-inputs, bound to the map, image and compiler version. Keep this receipt beside the map for staged checks after compiler scratch files are deleted. Receipts are local build evidence, not signatures; the sources and pinned toolchain objects must remain available for checking.

Reviewed registrations

The manifest records the exact -iW version (3.2.4-rc2+1) and complete object SHA-256 values from the 3.2.4-rc2-b1 toolchain release. It covers supported linked units that register handlers as well as the objects defining them. The map supplies their final addresses; it is not the completeness proof.

EntryRegistration or replacement
__FPC_finally_handlerCompiler-generated try/finally frames
__FPC_except_handlerCompiler-generated untyped try/except frames
__FPC_on_handlerTyped on clauses
__FPC_except_safecallsafecall exception frames
__FPC_DEFAULT_HANDLERStartup frame and hardware exception translation
NullHandlerExisting-frame replacement during raise and cleanup
NestedHandlerNested unwind protection through a frame-pointer store
Signals_exception_handlersignals.o replaces the existing system frame's handler

CommonHandler is called directly, and API_signals_exception_handler is an unhandled-exception filter callback; neither is a separate SEH frame entry. Their ordinary-callback classifications are explicit and object-hash-bound. Chain-restoration operations reuse saved frames instead of adding new entries. An unlinked optional unit needs no table entry. A linked handler can be absent only when the linker explicitly records its section as removed.

Re-auditing a toolchain update

The reviewed inputs are recorded in config/win32-safeseh/fpc-3.2.4.manifest. A toolchain bump requires a new audit, not merely replacing hashes. Inspect the new RTL object code and relocations for frame installation, handler replacement, and restoration, including registrations through ordinary pointer stores. Reconcile every handler symbol with the live linker maps of all executables, update the reviewed manifest's version and object SHA-256 values, and rerun the PE refusal tests plus full Windows CI. CI compares Pascal exceptions, hardware faults, nested exceptions, and the Proxy recursion case with a temporary copy whose SafeSEH directory reference is removed. Both variants must preserve the same outcomes. See the decision.

Ordinary project helper names that resemble handlers have explicit source-nonseh classifications bound to their source file hashes. After editing one of those files, review the named helper again before refreshing its hash; do not turn an unknown-name refusal into a broad exclusion.

Retain the new compiler and unit archive, query the actual compiler with fpc -Pi386 -Twin32 -iW, and inspect object disassembly with relocations (objdump -dr -Mintel). Check both direct and register-indexed FS writes, ordinary stores to existing frame fields, and callback replacement/restoration. Review new linked units explicitly. Confirm symbol aliases and defining-object ownership against production and development maps before approving new hashes. CI runs the paired exception probes for both production and development images, including images containing retained debug metadata.

The binary layout follows Microsoft's SafeSEH documentation and load-configuration definition.