[fpc-pascal] Upgrade LD linker for Free Pascal?

Dibo dibo20 at wp.pl
Thu Jul 23 07:13:52 CEST 2026


Thanks for hint. I have created ticket:

https://gitlab.com/freepascal.org/fpc/source/-/work_items/41826

W dniu 22.07.2026 o 20:02, n7800 pisze:
> Perhaps it would be better to post this on the FPC bug tracker 
> <https://gitlab.com/freepascal.org/fpc/source/-/work_items>?
> Or on the "fpc-devel" mailing list.
>
>     Hi,
>
>     I suspect that my first message was blocked because I attached ODT
>     document. Please ignore duplicate if it show up.
>
>     I mentioned about this issue some time ago in the topic about
>     PasFLTK bindings which I made. I decided to make dedicated thread
>     because it seems to be interesting.
>
>     So. I'm developing PasFLTK bindings for CFLTK which is flatten C
>     binding for major FLTK UI framework written in C++. PasFLTK has
>     two modes, shared library which work perfectly fine and
>     experimental static linking.
>
>     Static linking has issues which I'm consistently fixing. Major
>     problem is that C++ classes are not initialized in proper way.
>     Problem is not a matter of "Static Initialization Order Fiasco"
>     because CFLTK/FLTK is designed to be static linking for default
>     (and that is how other language bindings are using it). Problem is
>     how FPC LD linker prepare "startup" routines. For example:
>
>     Class Fl_File_Chooser2.cxx has this public variable:
>
>     const char *Fl_File_Chooser::filesystems_label =
>     Fl::system_driver()->filesystems_label();
>
>     System_driver is not initialized yet so filesystems_label become
>     null which result with access violations. Since I'm linux user I
>     started from this platform, working on this with Claude AI (which
>     amazed me how deeply he can analyze binaries on low level). On
>     linux, fix was small and easy (for Claude, not for me of course).
>     LD linker prepare wrong init structure and doesn't call the
>     __init_array_start in linux ELF standard. So patch looks like that:
>
>     type
>       TCtorProc = procedure(); cdecl;
>       PCtorProc = ^TCtorProc;
>
>     var
>       __init_array_start: Byte; external name '__init_array_start';
>       __init_array_end: Byte; external name '__init_array_end';
>
>     procedure RunCppGlobalConstructorsLinux;
>     var
>       p, pend: PCtorProc;
>     begin
>       p := PCtorProc(@__init_array_start);
>       pend := PCtorProc(@__init_array_end);
>       while p < pend do
>       begin
>         if Assigned(p^) then
>           p^();
>         Inc(p);
>       end;
>     end;
>
>     That small piece of code fixed everything and static linking work
>     like a charm on linux. I have project (which I publish soon)
>     totally written as static linking and didn't notice any new issues
>     so far.
>
>     Now windows version. Here complication start. First, build-in FPC
>     linker is useless and throw error:
>
>     Error: Failed reading coff file, invalid section index while
>     reading
>     C:\Users\vboxuser\Documents\CFLTKlibs\libcfltk.a(cfl_window.cpp.obj)
>
>     I had to use -Xe flag which switch to external ld.exe linker. But
>     then I had DWARF errors because LD support only up to 4 version
>     and my MinGW toolchain (MSYS2 on windows) is using ver. 5. I had
>     to call `strip --strip-debug` on all *.a files (including some
>     MinGW - e.g libstd++.a).
>
>     Now, when binding are compiling fine, I fall to the same problem
>     with initialization as on linux. I spent few evenings with Claude
>     on this. We compared objdump -h and extracted objects on exactly
>     the same demos written line by line one using C (CFLTK) and second
>     Free Pascal. Since linux use ELF we get __init_array_start, on
>     windows PE - it looks harder. LD.exe delivered with FPC is quite
>     old and doesn't use modern standard. Demo written in C and linked
>     by LD from toolchain has correct .ctors and __CTOR_LIST__ which
>     FPC ld.exe is missing (in fact, filling it incorrectly). Tried
>     using -FD"C:\msys64\mingw64\bin" which load ld.exe from MinGW
>     toolchain but this result with dozens errors:
>
>     C:\msys64\mingw64\bin\ld.exe:
>     C:\programowanie\lazarus\fpc\3.2.2\units\x86_64-win64\rtl\sysutils.o:
>     illegal relocation type 0 at address 0
>     C:\msys64\mingw64\bin\ld.exe:
>     C:\programowanie\lazarus\fpc\3.2.2\units\x86_64-win64\rtl\math.o:
>     illegal relocation type 0 at address 0
>
>     Claude also compared rust bindings for CFLTK because it also use
>     static linking. They had also problems and are stick to the
>     specific toolchain. But there problem was easier to fix because
>     Rust linker correctly linking crt2.o and has own guardians
>     rsbegin.o and rsend.o (equivalent of crtbegin.o and crtend.o)
>     which are missing in FPC ld linker.
>
>     Finally I get it working with Claude but this require reorganize
>     *.a and C++ objects with --globalize-symbol and this solution is
>     not acceptable for me because it require engagement everytime when
>     something changed in CFLT/FLTK source or even in MinGW.
>
>     I'll be not writting anymore and just attach summary of our work
>     which I asked Claude to write in short. It has more technical
>     details. To sum up. Is there any plans to upgrade LD linker or
>     exists any alpha / beta of upcoming release? The difference
>     between FPC ld and GCC ld is almost 10 years. I read somewhere
>     that FPC is not designed to link C++ (only C) but as we can see,
>     it could be just small step to make it possible and that open huge
>     door.
>
>     Below is Claude's summary.
>
>     Regards, Dibo.
>
>     Environment:
>
>         Free Pascal Compiler 3.2.2, Windows target x86_64-win64
>
>         Bundled linker:
>     C:\programowanie\lazarus\fpc\3.2.2\bin\x86_64-win64\ld.exe — GNU
>     ld (GNU Binutils) 2.28
>
>         Comparison linker: ld.exe from MSYS2 mingw-w64-x86_64-binutils
>     package, located at C:\msys64\mingw64\bin\ld.exe — GNU ld (GNU
>     Binutils) 2.46.1
>
>         GCC/G++ used to build FLTK, CFLTK, and the comparison C++
>     demo: 16.1.0 (MSYS2 mingw-w64-x86_64-gcc, reported as "GCC 16.1.0"
>
>         FLTK version: 1.4.5, CFLTK version: 1.5.23
>
>     Root cause identified: FPC 3.2.2's bundled ld.exe fails to
>     correctly merge .ctors input sections (containing C++ global
>     constructor pointers) from object files compiled by a modern GCC
>     (16.1.0, MinGW-w64/MSYS2) into the final __CTOR_LIST__ array —
>     even though the linker script (identical KEEP(*(.ctors)) rule,
>     confirmed via ld.exe --verbose on both linkers) is present in both
>     the old bundled ld.exe and a current MSYS2 ld.exe.
>
>     Evidence:
>
>         A raw hex dump of __CTOR_LIST__ in an FPC-linked executable
>     (with 10 real global constructors from a statically-linked
>     FLTK/CFLTK library present and confirmed via nm) shows: sentinel
>     (-1) immediately followed by terminator (0) — zero constructor
>     pointers, despite the constructor functions (_GLOBAL__sub_I_*)
>     physically existing in the binary.
>
>         The same source compiled and linked natively with
>     g++.exe/current MSYS2 ld.exe produces a fully populated
>     __CTOR_LIST__ with all 10 pointers correctly resolved to the
>     matching _GLOBAL__sub_I_* addresses.
>
>         Attempting to relink the FPC-built binary with the current
>     MSYS2 ld.exe (via -FD<path>) fails with illegal relocation type 0
>     / invalid section index errors on FPC's own RTL object files
>     (sysutils.o, math.o) — indicating FPC's internal object emitter
>     and modern binutils have diverged in mutually incompatible ways.
>     rtl\sysutils.o: illegal relocation type 0 at address 0 etc
>
>         The input .o files from the affected FLTK library use COMDAT
>     sections (LINK_ONCE_DISCARD) extensively (confirmed via objdump
>     -h), which may be related to how the old ld.exe mishandles their
>     .ctors content during merging — this is hypothesis
>
>         Consequently, statically linking modern C++ libraries
>     (FLTK/CFLTK, built with current MinGW-w64/GCC) into an FPC/Lazarus
>     Windows executable silently drops all global C++ constructors,
>     causing use of uninitialized static objects downstream (observed
>     as intermittent access violations depending on which code paths
>     touch affected statics).
>
>     Workaround currently used: manually enumerating _GLOBAL__sub_I_*
>     symbols via nm on the linked (even if crashing) executable,
>     globalizing their linkage scope via objcopy --globalize-symbol
>     (they are emitted as local/t symbols), and generating a small
>     Pascal unit that calls them all explicitly at program startup.
>     This resolves some but not all downstream issues, suggesting
>     missing constructor invocation is necessary but possibly not
>     sufficient — full mainCRTStartup context
>     (TLS/locale/guard-variable state) may also matter for some
>     lazily-initialized C++ singletons. - this is last hypothesis
>
>     Question for the list: Is there a newer/beta ld.exe (or plan to
>     update the bundled binutils) for the Windows FPC toolchain that
>     correctly handles .ctors/COMDAT sections as emitted by current
>     GCC, without breaking compatibility with FPC's own object file
>     emitter?
>
>     _______________________________________________
>     fpc-pascal maillist - fpc-pascal at lists.freepascal.org
>     https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freepascal.org/pipermail/fpc-pascal/attachments/20260723/994fd21e/attachment-0001.htm>


More information about the fpc-pascal mailing list