[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