Recent

Author Topic: Full MSVC/LLVM/MinGW COFF Linker Support - Testers needed  (Read 1572 times)

Coldzer0

  • Jr. Member
  • **
  • Posts: 55
  • Discord : Coldzer0
    • ChatWithMe
Full MSVC/LLVM/MinGW COFF Linker Support - Testers needed
« on: March 24, 2026, 11:33:00 pm »
Hello everyone,

We've been working on a merge request that adds full MSVC/LLVM/MinGW static library support to FPC's internal COFF linker.

https://gitlab.com/freepascal.org/fpc/source/-/merge_requests/1385

This allows linking .a and .lib files produced by Clang, GCC, and MSVC (containing C++ template instantiations, COMDAT sections, short import objects, weak externals, CRT initializers) without falling back to an external linker.

We'd appreciate testing from anyone who links C/C++ static libraries into their FPC projects on Windows.

What's new

  • COMDAT section support, including deduplication, associative sections, and all 6 selection modes. Required for any C++ library with templates or inline functions.

  • LLVM/Clang archive compatibility, covering second linker member handling, NUL-terminated long filename entries, alignment padding fixes, and reloc allocation fix.

  • MSVC short import object parsing that reads DLL import definitions (0x0000 0xFFFF signature) from .lib archives and generates proper IAT entries. Also fixed __imp_ prefixed symbol matching in GenerateLibraryImports to correctly generate direct IAT pointers instead of jmp thunks, which was causing runtime crashes with MSVC .lib import libraries.

  • Duplicate symbol fix for same-name COMDAT sections. LLVM objects can have 1000+ .rdata sections with identical names, and each now gets its own TObjSymbol so relocations resolve correctly.

  • Weak externals and /alternatename:X=Y directive, used extensively by MSVC CRT objects for fallback symbol definitions. Added placeholder symbol registration so the archive scanner pulls in members that define /alternatename targets.

  • /include:SYMBOL directive support. UCRT objects use this to force-load CRT initializer objects (stdio, heap, locale, etc.).

  • /DEFAULTLIB:, /NODEFAULTLIB:, /DISALLOWLIB: directives for automatic loading of dependent libraries from .drectve sections. With these directives supported, adding the Visual Studio library paths to the FPC search paths is all that's needed for the linker to pull in everything automatically. For example:

Code: [Select]
-Fl"C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.50.35717\lib\x64"
-Fl"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\ucrt\x64"
-Fl"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.26100.0\um\x64"
    Tested with the latest update of Visual Studio. No need to manually specify each library, only the main ones needed by the project. The linker resolves dependencies from the MSVC objects automatically if defined by /DEFAULTLIB.  :D

  • .CRT$ section handling so MSVC CRT initializer function pointer tables survive section GC and get sorted correctly via $ ordering.

  • .lib extension recognized as static archive, meaning {$linklib foo.lib} now works on Windows instead of misrouting to the DLL import path. Also fixed AddStaticCLibrary mangling .lib files to libfoo.lib.a.

  • Archive rescan loop with member dedup to handle circular dependencies between libraries (Now you can linklib in any order)  8) like libucrt.a and libmsvcrt.a.

  • __imp_ prefix fallback so the archive scanner finds MSVC short import objects indexed under __imp_ names.

  • MSVC section patterns in PE linker script for _RDATA*, .00cfg*, .rtc$*, .fptable*, .gfids$*, .giats$*. Without these, MSVC sections get no ExeSection assignment and all RIP-relative references break.

  • RELOC_SECREL32 fix that always subtracts ExeSection.MemPos for section-relative offsets, fixing TLS variable crashes.

  • BigObj COFF format support for COFF objects with more than 65535 sections, as produced by large C++ template libraries.

  • LTCG anonymous object detection fix. Tightened detection to require mach=0 AND nsects=0 AND syms=0, so machine-independent COFF objects are no longer falsely skipped.

  • RELOC_SECTION handler with IMAGE_REL_AMD64_SECTION support for 16-bit section index relocations.

  • Relocation handling for discarded COMDATs. Zeroes out relocations to discarded COMDAT sections instead of triggering internalerror.

  • symansistr enabled for x86_64 because MSVC C++ mangled symbol names can exceed 500 characters. Enabled ansistring-based symbol storage to prevent silent truncation at 255 chars.

  • i386 support with IMAGE_REL_I386_ABSOLUTE and IMAGE_REL_I386_SECTION relocation types including read/write mappings. Added ___ImageBase symbol for the i386 underscore prefix convention. Fixed GetSection erroring on COFF special section indices (-1 absolute, -2 debug).

  • FixupSymbols absolute symbol when linking i386 MSVC objects. COFF special section indices: -1 (IMAGE_SYM_ABSOLUTE) for symbols with fixed values not in any section (e.g. __tls_array at TEB offset 0x2C on i386), -2 (IMAGE_SYM_DEBUG) for debug symbols. These are still valid special values.
Important: CRT initialization for C/C++ static libraries

If you are statically linking C/C++ libraries (MSVC or MinGW), the C runtime needs to be initialized before any C code runs. Add this unit to your project and make sure it is in your uses clause:

Code: Pascal  [Select][+][-]
  1. unit CPPInit;
  2.  
  3. {$mode ObjFPC}{$H+}
  4. {$Assertions ON}
  5.  
  6. interface
  7.  
  8. implementation
  9.   {$if defined(WINDOWS) and not defined(MinGW)}
  10.     uses windows;
  11.   {$EndIf}
  12.  
  13.   {$If defined(MinGW)}
  14.     procedure __main(); cdecl; external;
  15.   {$ElseIf defined(WINDOWS)} // MSVC
  16.     {$IfDef CPU64}
  17.       function CRT_INIT(hinstDLL: HINST; fdwReason: DWORD; lpReserved: PtrInt): LongBool; stdcall;
  18.         external name '_CRT_INIT';
  19.     {$else}
  20.       function CRT_INIT(hinstDLL: HINST; fdwReason: DWORD; lpReserved: PtrInt): LongBool; stdcall;
  21.         external name '__CRT_INIT@12';
  22.     {$EndIf}
  23.   {$EndIf}
  24.  
  25. initialization
  26.   {$If defined(MinGW)}
  27.   __main();
  28.   {$ElseIf defined(WINDOWS)} // MSVC
  29.   Assert(CRT_INIT(HINSTANCE, DLL_PROCESS_ATTACH, dllparam), 'CRT_INIT FAILED');
  30.   {$EndIf}
  31.  
  32. {$if defined(WINDOWS) and not defined(MinGW)}
  33. finalization
  34.   CRT_INIT(HINSTANCE, DLL_PROCESS_DETACH, dllparam);
  35. {$EndIf}
  36. end.
  37.  

Without this, you may get crashes in C/C++ library functions that depend on CRT heap, stdio, or locale initialization. Define MinGW if linking MinGW-compiled libraries, otherwise MSVC mode is used by default.

We are investigating adding this initialization automatically, either through the linker itself or through system.pas, so that users won't need this unit in the future. However, this may require weak external support on Windows targets so the CRT init symbols are only resolved when C libraries are actually linked, without causing errors in pure Pascal projects. For now, the manual unit approach above is the reliable way to handle it.


What needs testing

If you can test any of the following scenarios, it would be really helpful:

  • Basic regression test. Build any existing FPC project that does NOT use C libraries. Make sure the internal linker still works as before, on both x86_64 and i386 on Windows.

  • Clang/LLVM static libraries. If you have any .a files produced by clang/llvm-ar, try linking them with the internal linker.

  • MSVC static libraries. If you have .lib files from MSVC (cl.exe), try {$linklib something.lib} and see if it links.

  • GCC/MinGW static libraries. Try .a files from gcc/g++ via the MinGW toolchain.

  • Mixed toolchain. Linking libraries from different compilers in the same project MinGW/MSVC/Clang.

  • DLL building. If you build DLLs that link against C/C++ static libraries, verify exports and imports are correct.

  • Debug info. Verify that DWARF/stabs debug info still works when linking C objects.

  • Cross-compilation. If you cross-compile for Win64 from Linux or other hosts.
How to test

1. Apply the merge request on top of current main branch
2. Build the compiler (the RTL needs to be rebuilt as well)
3. Build your test projects using the new compiler with the internal linker
4. Report any errors, crashes, or unexpected behavior

If you hit "Unresolved external symbol" errors, please include the full symbol name as it helps identify which feature might need adjustment.

Thanks for any testing you can do!
« Last Edit: March 26, 2026, 05:42:38 am by Coldzer0 »

Jorg3000

  • Jr. Member
  • **
  • Posts: 84
Re: Testers needed - Full MSVC/LLVM/MinGW COFF Linker Support
« Reply #1 on: March 25, 2026, 03:29:07 am »
Hi!
Thanks for your work!

I’m not very familiar with linkers. But I assume that with the new MSVC support it might be possible in the future to statically link .lib files - is that correct?

I would like to see, for example, that the WebView4Delphi project could link the .lib file "WebView2LoaderStatic.lib" directly into the .exe, so that there is no need to distribute the external DLL "WebView2Loader.dll" anymore.

Coldzer0

  • Jr. Member
  • **
  • Posts: 55
  • Discord : Coldzer0
    • ChatWithMe
Re: Testers needed - Full MSVC/LLVM/MinGW COFF Linker Support
« Reply #2 on: March 25, 2026, 04:48:53 am »
Hi!
Thanks for your work!

I’m not very familiar with linkers. But I assume that with the new MSVC support it might be possible in the future to statically link .lib files - is that correct?

I would like to see, for example, that the WebView4Delphi project could link the .lib file "WebView2LoaderStatic.lib" directly into the .exe, so that there is no need to distribute the external DLL "WebView2Loader.dll" anymore.



Yes, that's exactly what this enables, You can now statically link MSVC .lib files directly into your executable using {$linklib WebView2LoaderStatic.lib} with the internal linker, no external DLL needed.

Take a look at the screenshot, everything works as expected.( I just had to bypass some checks the project do to check if the dll exists, And for sure makes the APIs as external )

https://img.lightshot.app/W8kGbAqZSSeAeDUhwM6KFQ.png

Just keep in mind you'll need the CPPInit unit (posted above) if the library depends on the MSVC runtime being initialized.

valdir.marcos

  • Hero Member
  • *****
  • Posts: 1285
Re: Testers needed - Full MSVC/LLVM/MinGW COFF Linker Support
« Reply #3 on: March 26, 2026, 04:03:46 am »
Hello everyone,

We've been working on a merge request that adds full MSVC/LLVM/MinGW static library support to FPC's internal COFF linker.

https://gitlab.com/freepascal.org/fpc/source/-/merge_requests/1385

This allows linking .a and .lib files produced by Clang, GCC, and MSVC (containing C++ template instantiations, COMDAT sections, short import objects, weak externals, CRT initializers) without falling back to an external linker.

We'd appreciate testing from anyone who links C/C++ static libraries into their FPC projects on Windows.

What's new

  • COMDAT section support, including deduplication, associative sections, and all 6 selection modes. Required for any C++ library with templates or inline functions.

  • LLVM/Clang archive compatibility, covering second linker member handling, NUL-terminated long filename entries, alignment padding fixes, and reloc allocation fix.

  • MSVC short import object parsing that reads DLL import definitions (0x0000 0xFFFF signature) from .lib archives and generates proper IAT entries. Also fixed __imp_ prefixed symbol matching in GenerateLibraryImports to correctly generate direct IAT pointers instead of jmp thunks, which was causing runtime crashes with MSVC .lib import libraries.

  • Duplicate symbol fix for same-name COMDAT sections. LLVM objects can have 1000+ .rdata sections with identical names, and each now gets its own TObjSymbol so relocations resolve correctly.

  • Weak externals and /alternatename:X=Y directive, used extensively by MSVC CRT objects for fallback symbol definitions. Added placeholder symbol registration so the archive scanner pulls in members that define /alternatename targets.

  • /include:SYMBOL directive support. UCRT objects use this to force-load CRT initializer objects (stdio, heap, locale, etc.).

  • /DEFAULTLIB:, /NODEFAULTLIB:, /DISALLOWLIB: directives for automatic loading of dependent libraries from .drectve sections. With these directives supported, adding the Visual Studio library paths to the FPC search paths is all that's needed for the linker to pull in everything automatically. For example:

Code: [Select]
-Fl"C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.50.35717\lib\x64"
-Fl"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\ucrt\x64"
-Fl"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.26100.0\um\x64"
    Tested with the latest update of Visual Studio. No need to manually specify each library, only the main ones needed by the project. The linker resolves dependencies from the MSVC objects automatically if defined by /DEFAULTLIB.  :D

  • .CRT$ section handling so MSVC CRT initializer function pointer tables survive section GC and get sorted correctly via $ ordering.

  • .lib extension recognized as static archive, meaning {$linklib foo.lib} now works on Windows instead of misrouting to the DLL import path. Also fixed AddStaticCLibrary mangling .lib files to libfoo.lib.a.

  • Archive rescan loop with member dedup to handle circular dependencies between libraries (Now you can linklib in any order)  8) like libucrt.a and libmsvcrt.a.

  • __imp_ prefix fallback so the archive scanner finds MSVC short import objects indexed under __imp_ names.

  • MSVC section patterns in PE linker script for _RDATA*, .00cfg*, .rtc$*, .fptable*, .gfids$*, .giats$*. Without these, MSVC sections get no ExeSection assignment and all RIP-relative references break.

  • RELOC_SECREL32 fix that always subtracts ExeSection.MemPos for section-relative offsets, fixing TLS variable crashes.

  • BigObj COFF format support for COFF objects with more than 65535 sections, as produced by large C++ template libraries.

  • LTCG anonymous object detection fix. Tightened detection to require mach=0 AND nsects=0 AND syms=0, so machine-independent COFF objects are no longer falsely skipped.

  • RELOC_SECTION handler with IMAGE_REL_AMD64_SECTION support for 16-bit section index relocations.

  • Relocation handling for discarded COMDATs. Zeroes out relocations to discarded COMDAT sections instead of triggering internalerror.

  • symansistr enabled for x86_64 because MSVC C++ mangled symbol names can exceed 500 characters. Enabled ansistring-based symbol storage to prevent silent truncation at 255 chars.

  • i386 support with IMAGE_REL_I386_ABSOLUTE and IMAGE_REL_I386_SECTION relocation types including read/write mappings. Added ___ImageBase symbol for the i386 underscore prefix convention. Fixed GetSection erroring on COFF special section indices (-1 absolute, -2 debug).

  • FixupSymbols absolute symbol when linking i386 MSVC objects. COFF special section indices: -1 (IMAGE_SYM_ABSOLUTE) for symbols with fixed values not in any section (e.g. __tls_array at TEB offset 0x2C on i386), -2 (IMAGE_SYM_DEBUG) for debug symbols. These are still valid special values.
Important: CRT initialization for C/C++ static libraries

If you are statically linking C/C++ libraries (MSVC or MinGW), the C runtime needs to be initialized before any C code runs. Add this unit to your project and make sure it is in your uses clause:

Code: Pascal  [Select][+][-]
  1. unit CPPInit;
  2.  
  3. {$mode ObjFPC}{$H+}
  4. {$Assertions ON}
  5.  
  6. interface
  7.  
  8. implementation
  9.   {$if defined(WINDOWS) and not defined(MinGW)}
  10.     uses windows;
  11.   {$EndIf}
  12.  
  13.   {$If defined(MinGW)}
  14.     procedure __main(); cdecl; external;
  15.   {$ElseIf defined(WINDOWS)} // MSVC
  16.     {$IfDef CPU64}
  17.       function CRT_INIT(hinstDLL: HINST; fdwReason: DWORD; lpReserved: PtrInt): LongBool; stdcall;
  18.         external name '_CRT_INIT';
  19.     {$else}
  20.       function CRT_INIT(hinstDLL: HINST; fdwReason: DWORD; lpReserved: PtrInt): LongBool; stdcall;
  21.         external name '__CRT_INIT@12';
  22.     {$EndIf}
  23.   {$EndIf}
  24.  
  25. initialization
  26.   {$If defined(MinGW)}
  27.   __main();
  28.   {$ElseIf defined(WINDOWS)} // MSVC
  29.   Assert(CRT_INIT(HINSTANCE, DLL_PROCESS_ATTACH, dllparam), 'CRT_INIT FAILED');
  30.   {$EndIf}
  31.  
  32. {$if defined(WINDOWS) and not defined(MinGW)}
  33. finalization
  34.   CRT_INIT(HINSTANCE, DLL_PROCESS_DETACH, dllparam);
  35. {$EndIf}
  36. end.
  37.  

Without this, you may get crashes in C/C++ library functions that depend on CRT heap, stdio, or locale initialization. Define MinGW if linking MinGW-compiled libraries, otherwise MSVC mode is used by default.

We are investigating adding this initialization automatically, either through the linker itself or through system.pas, so that users won't need this unit in the future. However, this may require weak external support on Windows targets so the CRT init symbols are only resolved when C libraries are actually linked, without causing errors in pure Pascal projects. For now, the manual unit approach above is the reliable way to handle it.


What needs testing

If you can test any of the following scenarios, it would be really helpful:

  • Basic regression test. Build any existing FPC project that does NOT use C libraries. Make sure the internal linker still works as before, on both x86_64 and i386 on Windows.

  • Clang/LLVM static libraries. If you have any .a files produced by clang/llvm-ar, try linking them with the internal linker.

  • MSVC static libraries. If you have .lib files from MSVC (cl.exe), try {$linklib something.lib} and see if it links.

  • GCC/MinGW static libraries. Try .a files from gcc/g++ via the MinGW toolchain.

  • Mixed toolchain. Linking libraries from different compilers in the same project MinGW/MSVC/Clang.

  • DLL building. If you build DLLs that link against C/C++ static libraries, verify exports and imports are correct.

  • Debug info. Verify that DWARF/stabs debug info still works when linking C objects.

  • Cross-compilation. If you cross-compile for Win64 from Linux or other hosts.
How to test

1. Apply the merge request on top of current main branch
2. Build the compiler (the RTL needs to be rebuilt as well)
3. Build your test projects using the new compiler with the internal linker
4. Report any errors, crashes, or unexpected behavior

If you hit "Unresolved external symbol" errors, please include the full symbol name as it helps identify which feature might need adjustment.

Thanks for any testing you can do!

Interesting. Thanks.



Is your solution [even remotely] related to the issue below?
 
Can arbitrary C++ compilers use each other's classes?
https://forum.lazarus.freepascal.org/index.php/topic,73766.0.html

Coldzer0

  • Jr. Member
  • **
  • Posts: 55
  • Discord : Coldzer0
    • ChatWithMe
Re: Testers needed - Full MSVC/LLVM/MinGW COFF Linker Support
« Reply #4 on: March 26, 2026, 05:01:23 am »

Interesting. Thanks.



Is your solution [even remotely] related to the issue below?
 
Can arbitrary C++ compilers use each other's classes?
https://forum.lazarus.freepascal.org/index.php/topic,73766.0.html

Quote
Can arbitrary C++ compilers use each other's classes?
I don't think so.

This Merge-Request is purely linker-level work, it has nothing to do with interfacing with C++ classes or VMT layouts. What we've done is teach FPC's internal linker to understand the COFF object file format as produced by MSVC, Clang, and GCC, so you can statically link their .a/.lib files without needing an external linker.

That said, using C++ classes from Pascal is definitely possible. I've done something similar with a C++ library before, but it requires re-creating the C++ VMT layout in Pascal as a record with function pointers in the correct order. It's manual work and depends on the specific compiler's ABI, but it works.

A small snippet from the project
Code: Pascal  [Select][+][-]
  1.  
  2. Type
  3.   NPlatformParameters = Pointer;
  4.  
  5.   { NClientParameters }
  6.  
  7.   NClientParameters = Record
  8.     serverKey, host: string;
  9.     port: Int32;
  10.     ssl:  Boolean;
  11.     platformParams: Pointer;
  12.   End;
  13.  
  14.   Vtable = Record
  15.     CPP_Destructor:   Pointer;
  16.     SetErrorCallback: Procedure(this: Pointer; NError: Pointer); Cdecl;
  17.     setUserData: Procedure(this: Pointer; userData: Pointer); Cdecl;
  18.     getUserData: Function(this: Pointer): Pointer; Cdecl;
  19.   End;
  20.   PVtable  = ^Vtable;
  21.   PPVtable = ^PVtable;
  22.  
  23.   NClientInterface = Record
  24.     Vtable: PPVtable;
  25.   End;
  26.   NClientPtr = ^NClientInterface;
  27.  
  28. Function CreateClientInterface(this, Parameters: Pointer): Pointer;
  29.   Cdecl; External 'nakama-sdk' Name '??0NClientInterface@Nakama@@QEAA@AEBV01@@Z';
  30.  
  31. Function createDefaultClient(this, Parameters: Pointer): NClientPtr;
  32.   Cdecl; External 'nakama-sdk' Name
  33.   '?createDefaultClient@Nakama@@YA?AV?$shared_ptr@VNClientInterface@Nakama@@@std@@AEBUNClientParameters@1@@Z';
  34.  
  35. Function getNakamaSdkVersion(): PChar; Cdecl; External 'nakama-sdk' Name '?getNakamaSdkVersion@Nakama@@YAPEBDXZ';
  36.  
  37. Function CreateNClientParameters(SERVER, IP: String; PORT: Int32): NClientParameters;
  38.  
  39. Implementation
  40.  
  41. { NClientParameters }
  42.  
  43. Function CreateNClientParameters(SERVER, IP: String; PORT: Int32): NClientParameters;
  44. Begin
  45.   FillByte(Result, SizeOf(NClientParameters), 0);
  46.   Result.host      := IP;
  47.   Result.serverKey := SERVER;
  48.   Result.port      := PORT;
  49. End;
  50.  
  51.  

And we call it with
Code: Pascal  [Select][+][-]
  1. Var
  2.   ClientParams: NClientParameters;
  3.   Client: NClientPtr;
  4. Begin
  5.  
  6.   WriteLn('Nakama Loaded');
  7.   WriteLn('Nakama Version = ', getNakamaSdkVersion());
  8.  
  9.   ClientParams := CreateNClientParameters('SERVER_KEY', '127.0.0.1', 8080);
  10.   Client := AllocMem(SizeOf(NClientInterface));
  11.   Client := createDefaultClient(Client, @ClientParams);
  12.   If Client <> nil Then
  13.   Begin
  14.     WriteLn('SetUserData = ', HexStr($1337C0DE, 16));
  15.     Client.Vtable^.SetUserData(Client.Vtable, Pointer($1337C0DE));
  16.     WriteLn('UserDater   = ', HexStr(Client.Vtable^.getUserData(Client.Vtable)));
  17.   End;
  18.  
  19.   ReadLn;
  20. End.
  21.  

As for the GTK2/GTK3 question in that thread, the Lazarus team already working on GTK3 widgetset in trunk, and Qt5/Qt6 widgetsets are available as well. So Debian dropping GTK2 shouldn't be a concern for Lazarus users going forward.
« Last Edit: March 26, 2026, 05:14:28 am by Coldzer0 »

valdir.marcos

  • Hero Member
  • *****
  • Posts: 1285
Re: Testers needed - Full MSVC/LLVM/MinGW COFF Linker Support
« Reply #5 on: March 26, 2026, 06:09:19 am »

Interesting. Thanks.



Is your solution [even remotely] related to the issue below?
 
Can arbitrary C++ compilers use each other's classes?
https://forum.lazarus.freepascal.org/index.php/topic,73766.0.html

Quote
Can arbitrary C++ compilers use each other's classes?
I don't think so.

This Merge-Request is purely linker-level work, it has nothing to do with interfacing with C++ classes or VMT layouts. What we've done is teach FPC's internal linker to understand the COFF object file format as produced by MSVC, Clang, and GCC, so you can statically link their .a/.lib files without needing an external linker.

That said, using C++ classes from Pascal is definitely possible. I've done something similar with a C++ library before, but it requires re-creating the C++ VMT layout in Pascal as a record with function pointers in the correct order. It's manual work and depends on the specific compiler's ABI, but it works.

A small snippet from the project
Code: Pascal  [Select][+][-]
  1.  
  2. Type
  3.   NPlatformParameters = Pointer;
  4.  
  5.   { NClientParameters }
  6.  
  7.   NClientParameters = Record
  8.     serverKey, host: string;
  9.     port: Int32;
  10.     ssl:  Boolean;
  11.     platformParams: Pointer;
  12.   End;
  13.  
  14.   Vtable = Record
  15.     CPP_Destructor:   Pointer;
  16.     SetErrorCallback: Procedure(this: Pointer; NError: Pointer); Cdecl;
  17.     setUserData: Procedure(this: Pointer; userData: Pointer); Cdecl;
  18.     getUserData: Function(this: Pointer): Pointer; Cdecl;
  19.   End;
  20.   PVtable  = ^Vtable;
  21.   PPVtable = ^PVtable;
  22.  
  23.   NClientInterface = Record
  24.     Vtable: PPVtable;
  25.   End;
  26.   NClientPtr = ^NClientInterface;
  27.  
  28. Function CreateClientInterface(this, Parameters: Pointer): Pointer;
  29.   Cdecl; External 'nakama-sdk' Name '??0NClientInterface@Nakama@@QEAA@AEBV01@@Z';
  30.  
  31. Function createDefaultClient(this, Parameters: Pointer): NClientPtr;
  32.   Cdecl; External 'nakama-sdk' Name
  33.   '?createDefaultClient@Nakama@@YA?AV?$shared_ptr@VNClientInterface@Nakama@@@std@@AEBUNClientParameters@1@@Z';
  34.  
  35. Function getNakamaSdkVersion(): PChar; Cdecl; External 'nakama-sdk' Name '?getNakamaSdkVersion@Nakama@@YAPEBDXZ';
  36.  
  37. Function CreateNClientParameters(SERVER, IP: String; PORT: Int32): NClientParameters;
  38.  
  39. Implementation
  40.  
  41. { NClientParameters }
  42.  
  43. Function CreateNClientParameters(SERVER, IP: String; PORT: Int32): NClientParameters;
  44. Begin
  45.   FillByte(Result, SizeOf(NClientParameters), 0);
  46.   Result.host      := IP;
  47.   Result.serverKey := SERVER;
  48.   Result.port      := PORT;
  49. End;
  50.  
  51.  

And we call it with
Code: Pascal  [Select][+][-]
  1. Var
  2.   ClientParams: NClientParameters;
  3.   Client: NClientPtr;
  4. Begin
  5.  
  6.   WriteLn('Nakama Loaded');
  7.   WriteLn('Nakama Version = ', getNakamaSdkVersion());
  8.  
  9.   ClientParams := CreateNClientParameters('SERVER_KEY', '127.0.0.1', 8080);
  10.   Client := AllocMem(SizeOf(NClientInterface));
  11.   Client := createDefaultClient(Client, @ClientParams);
  12.   If Client <> nil Then
  13.   Begin
  14.     WriteLn('SetUserData = ', HexStr($1337C0DE, 16));
  15.     Client.Vtable^.SetUserData(Client.Vtable, Pointer($1337C0DE));
  16.     WriteLn('UserDater   = ', HexStr(Client.Vtable^.getUserData(Client.Vtable)));
  17.   End;
  18.  
  19.   ReadLn;
  20. End.
  21.  

As for the GTK2/GTK3 question in that thread, the Lazarus team already working on GTK3 widgetset in trunk, and Qt5/Qt6 widgetsets are available as well. So Debian dropping GTK2 shouldn't be a concern for Lazarus users going forward.




Interesting. Thanks.

Is your solution [even remotely] related to the issue below?
 
Can arbitrary C++ compilers use each other's classes?
https://forum.lazarus.freepascal.org/index.php/topic,73766.0.html

Quote
Can arbitrary C++ compilers use each other's classes?
I don't think so.

This Merge-Request is purely linker-level work, it has nothing to do with interfacing with C++ classes or VMT layouts. What we've done is teach FPC's internal linker to understand the COFF object file format as produced by MSVC, Clang, and GCC, so you can statically link their .a/.lib files without needing an external linker.

That said, using C++ classes from Pascal is definitely possible. I've done something similar with a C++ library before, but it requires re-creating the C++ VMT layout in Pascal as a record with function pointers in the correct order.

It's manual work and depends on the specific compiler's ABI, but it works.

Thank you!

tooknox

  • New Member
  • *
  • Posts: 40
Re: Full MSVC/LLVM/MinGW COFF Linker Support - Testers needed
« Reply #6 on: May 11, 2026, 08:27:49 pm »
Came here to say thank you to @ColdZer0, his recent commit allows linking Microsoft Detours library to work seamlessly with FPC. (at least in my testing) Hope his work gets merged soon.

 

TinyPortal © 2005-2018