[fpc-pascal] Upgrade LD linker for Free Pascal?
Dibo
dibo20 at wp.pl
Wed Jul 22 17:42:38 CEST 2026
And for notice, I have disabled -CX and -XX
W dniu 22.07.2026 o 16:09, Dibo via fpc-pascal pisze:
>
> 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/20260722/dfe3162d/attachment-0001.htm>
More information about the fpc-pascal
mailing list