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

n7800 n7800 at inbox.ru
Wed Jul 22 20:02:34 CEST 2026


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/20260722/f118d4d2/attachment-0001.htm>


More information about the fpc-pascal mailing list