[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