4

How can I link a FPC .o from a library to a Delphi executable. When I try to link the following code I get a bunch of unsatisfied forward or external declarations.

library project1;

{$mode objfpc}{$H+}

uses
  Classes
  { you can add units after this };

function Test: Integer;
begin
  Result := -1;
end;

begin
end.


[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'INIT$_$SYSTEM'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'FINALIZE$_$OBJPAS'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'INIT$_$LNFODWRF'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'FINALIZE$_$LNFODWRF'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'INIT$_$FPINTRES'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'FINALIZE$_$WINDIRS'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'SYSUTILS$_$TENCODING_$__$$_create'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'SYSUTILS$_$TENCODING_$__$$_destroy'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'INIT$_$SYSUTILS'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'FINALIZE$_$SYSUTILS'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'INIT$_$TYPINFO'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'FINALIZE$_$TYPINFO'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'INIT$_$CLASSES'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'FINALIZE$_$CLASSES'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'THREADVARLIST_$SYSTEM'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'THREADVARLIST_$CLASSES'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'RESSTR_$RTLCONSTS_$$_START'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'RESSTR_$RTLCONSTS_$$_END'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'RESSTR_$SYSCONST_$$_START'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'RESSTR_$SYSCONST_$$_END'
[dcc64 Error] Project2.dpr(170): E2065 Unsatisfied forward or external declaration: 'FPC_LIBINITIALIZEUNITS' 
user3060326
  • 187
  • 2
  • 16

1 Answers1

8

It is highly unlikely that you will be able to make this work, at least as written. The unsatisfied declarations are from the FPC runtime. You'd need to link that too, or re-implement it in Delphi. Neither option is terribly viable.

Of course, if you removed the reference to the Classes unit, and put this simple function in separate code unit rather than a library unit then it is plausible that there might be no unsatisfied declarations. That said, surely you are exploring this because you want to use FPC code that actually does something. And as soon as you do that, then you will be right back to square one.

The way out of this problem is to link to the FPC code dynamically. Compile the FPC code into a library and link to that library dynamically.


Just for fun I tried to link an FPC object to a Delphi program. The FPC unit:

unit unit1;

interface

implementation

function Test(i: Integer): Integer; cdecl;
begin
  Test := i*42;
end;

end.

I compiled this with:

fpc unit1.pp

Then I wrote the following Delphi program to link it:

{$APPTYPE CONSOLE}

{$L 'unit1.o'}

function Test(i: Integer): Integer; cdecl; 
  external name 'UNIT1_TEST$SMALLINT$$SMALLINT';

begin
  Writeln(Test(666));
end.

The output:

27972

Note that the function name is decorated. In order to find the name I used objdump:

>objdump -d unit1.o

unit1.o:     file format pe-i386


Disassembly of section .text.n_unit1_test$smallint$$smallint:

00000000 :
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 04                sub    $0x4,%esp
   6:   0f bf 45 08             movswl 0x8(%ebp),%eax
   a:   6b c0 2a                imul   $0x2a,%eax,%eax
   d:   66 89 45 fc             mov    %ax,-0x4(%ebp)
  11:   66 8b 45 fc             mov    -0x4(%ebp),%ax
  15:   c9                      leave
  16:   c3                      ret
        ...

I did this work with x86 versions of the compiler. I expect that it's viable under x64 too.

So you can indeed link FPC object files, provided that they are simple enough. However, should you need any of the FPC runtime and standard units, then I expect it will become too hard.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • You could get around the RTL issue by using Delphi RTL. But I am not sure if that would compile. – user3060326 Feb 26 '14 at 14:13
  • You can't just substitute any old RTL. It has to be the right one. FPC and Delphi have different RTLs. Again, just because they have the same name (RTL), it does not follow that they are interchangeable. – David Heffernan Feb 26 '14 at 14:15
  • Bigger problems would arise when you try to use more complex types. Or even to use them as parameters/return values for your exported functions. Types other than the standard C interop types (integral types, floating point types, pointers) cannot be used across interop boundaries. I know that Remy and I told you that at your previous question, and you did not seem to believe us. But it is true. – David Heffernan Feb 26 '14 at 14:17
  • What about `System.pas`? You mean the Delphi system unit? That's no use to any FPC code. – David Heffernan Feb 26 '14 at 14:18
  • One can set an unmangled name for the symbol by appending [public,alias:'namethatyoulike']; after the calling convention. Oh, and the FPC system unit is "system.pp", but of course you are correct. – Marco van de Voort Feb 27 '14 at 22:15
  • @Marco Can you do that for an overloaded function, or are they unexportable? See the more recent question that I answered. Quite possibly incorrectly. Also, what's the FPC codegen like? Does it spit out efficient code? – David Heffernan Feb 27 '14 at 22:16
  • Yes, afaik you could have two overloaded functions with different linker level identifiers. But the export statement afaik takes pascal level, not linker level identifiers. – Marco van de Voort Feb 27 '14 at 22:20
  • @Marco Which means that my answer here (http://stackoverflow.com/questions/22068653/exporting-overloaded-functions-with-fpc) is accurate? – David Heffernan Feb 27 '14 at 22:24
  • I tested, and as far as I can see, that syntax doesn't exist yet, not even in trunk. Which delphi version was that introduced btw? – Marco van de Voort Feb 27 '14 at 22:33
  • @Marco Came in with overloads IIRC. D3 perhaps. Certainly it is old. – David Heffernan Feb 28 '14 at 03:42
  • @MarcovandeVoort More important question is why can't we link the missing functions with Delphi? Where are these defined? Which `.o`? – user3060326 Feb 28 '14 at 07:16
  • You aren't going to be able to link them. Marco agreed with me in his first comment. – David Heffernan Feb 28 '14 at 07:42
  • The simple reason is that FPC code needs FPC runtime, and delphi code needs the delphi runtime. This is normal. MSVC++ can't use GCC's precompiled C++ stdlib either. – Marco van de Voort Feb 28 '14 at 08:39
  • @MarcovandeVoort True. But atleast we can link `C` object's while we can't link `FPC`. A true disapointment. – user3060326 Feb 28 '14 at 08:45
  • @user You can link FPC objects. My answer gives a demonstration of that. – David Heffernan Feb 28 '14 at 08:58
  • As soon as you start using the runtime, you might not with C either. As you can see in David's example. In C this doesn't limit you that much, since there is barely a language or a runtime. On a Delphi dialect level it is just more noticable. – Marco van de Voort Feb 28 '14 at 10:16
  • Yes, @Marco is quite right. As soon as you start using OOP features you'll find that you need a whole boat load of runtime support. So, sticking to very simple C style code will be fine. – David Heffernan Feb 28 '14 at 10:18
  • Well, C afaik has int64 too now. Use it, compile with GCC, and it will start nagging you for libgcc. (and on plain Pascal level also stuff like sets > 32 items, stings and dynamic arrays have helpers) – Marco van de Voort Feb 28 '14 at 12:16
  • @MarcovandeVoort Thats all fine and dandy. But GCC or ICC would generate better code. – user3060326 Feb 28 '14 at 12:17
  • @user Better than what? Delphi, or FPC? – David Heffernan Feb 28 '14 at 12:20
  • @DavidHeffernan Both. – user3060326 Feb 28 '14 at 12:22
  • @user In that case why are you trying to use FPC rather than gcc or icc? And what is the application, out of interest? – David Heffernan Feb 28 '14 at 12:27
  • user3060326: I fail to see how the performance of FPC relative to icc or not has anything to do with this discussion, which is about linking and linking compatibility. – Marco van de Voort Mar 01 '14 at 16:27
  • @user I see that you've unaccepted the answer. Why is that? Do you think I'm wrong? – David Heffernan Mar 01 '14 at 16:42