Recent

Author Topic: obj file linking: function alias  (Read 2794 times)

DevilDevil

  • New Member
  • *
  • Posts: 49
obj file linking: function alias
« on: November 22, 2020, 12:45:34 pm »
Hello everyone!

In my low-level library Tiny.Library, a lot of code is written in C, which is compiled into object files and linked to Delphi/FPC projects. In particular, it contains several functions that simplify and speed up working with standard types (for example, interfaces, strings, dynamic arrays). Interestingly, the internal structures of RTL are different for different versions of the compiler. Therefore, the code contains several variants for implementing the function, and only one is indicated in the header of the * .pas file. For instance:

Code: Pascal  [Select][+][-]
  1. // header:
  2. function AStrInit(var S{: AnsiString}; const AChars: PAnsiChar; const ALength: NativeUInt; const ACodePage: Word): PAnsiChar;
Code: Pascal  [Select][+][-]
  1. // implementation:
  2. function AStrInit; external {$ifdef EXTERNALLINKER}LIB_TINY_TYPES{$endif}
  3.   {$ifdef OBJLINKNAME}name
  4.     {$if not Defined(ANSISTRSUPPORT)}
  5.       'AStrInit_nextgen'
  6.     {$elseif not Defined(FPC)}
  7.       'AStrInit_new'
  8.     {$else .FPC}
  9.       'AStrInit_fpc'
  10.     {$ifend}
  11.   {$endif};

Now the moment has come when I need to call the final function from the C code. On the C side, I simply declare it in * .h file and try to call it at the right time:
Code: C++  [Select][+][-]
  1. REGISTER_DECL void* AStrInit(void* value, char8_t* chars, native_uint length, uint16_t codepage);
Code: C++  [Select][+][-]
  1. REGISTER_DECL void* InternalTest(void* value)
  2. {
  3.     return AStrInit(value, (char8_t*)"Test", 4, 1251);
  4. }

In Delphi, this construction is easily picked up - it simply sees the AStrInit function declared in Delphi and refers to it. But in FPC, in order for the object file to see the function from * .pas, the function must be marked as public, otherwise an error is generated:
Quote
Error: Undefined symbol: AStrInit

I'm trying to mark a function as public, but that doesn't help:
Code: Pascal  [Select][+][-]
  1. function AStrInit(var S{: AnsiString}; const AChars: PAnsiChar; const ALength: NativeUInt; const ACodePage: Word): PAnsiChar; {$ifdef FPC}public name 'AStrInit';{$endif}

How to fix it?



PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: obj file linking: function alias
« Reply #1 on: November 22, 2020, 01:21:26 pm »
Just so that I understand it correctly:

You have functions AStrInit_nextgen, AStrInit_new and AStrInit_fpc that are provided as Pascal code by either Delphi or FPC.
The function AStrInit should be an alias for the correct function above.
This AStrInit should be called from C-code.

Assuming the above is correct: you need to declare the original function (e.g. AStrInit_fpc decorated with public alias 'AStrInit') as FPC will only use the name you used in the external clause otherwise (on the assembly level the name AStrInit won't exist, only AStrInit_fpc will).

By the way: now that FPC has a Rtti unit with 3.2.0 as well it would be interesting if you benchmark your library against that, too (please note that FPC has a native Invoke implementation only for the Win64 ABI as well as the register calling convention on i386, for all other cases it relies on libffi for now).

DevilDevil

  • New Member
  • *
  • Posts: 49
Re: obj file linking: function alias
« Reply #2 on: November 22, 2020, 01:42:37 pm »
PascalDragon,

You got it right. Thank you very much.
Could you show me how to declare a function with an alias?

P.S. I am very glad that you are also interested in comparing performance with the standard Invoke routine. I am sure that when I more closely support the new FPC RTTI capabilities in the library, it will be possible to add FPC to the benchmark too.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: obj file linking: function alias
« Reply #3 on: November 22, 2020, 01:58:13 pm »
You got it right. Thank you very much.
Could you show me how to declare a function with an alias?

The following leads to the correct generation of a AStrInit symbol. You'll need to experiment in how to get it into your existing code structure:

Code: Pascal  [Select][+][-]
  1. unit uexport;
  2.  
  3. {$mode delphi}
  4.  
  5. interface
  6.  
  7. function AStrInit(var S{: AnsiString}; const AChars: PAnsiChar; const ALength: NativeUInt; const ACodePage: Word): PAnsiChar;
  8.  
  9. implementation
  10.  
  11. function AStrInit; external name 'AStrInit';
  12.  
  13. function AStrInit_fpc(var S{: AnsiString}; const AChars: PAnsiChar; const ALength: NativeUInt; const ACodePage: Word): PAnsiChar; public alias:'AStrInit';
  14. begin
  15.  
  16. end;
  17.  
  18. end.

P.S. I am very glad that you are also interested in comparing performance with the standard Invoke routine. I am sure that when I more closely support the new FPC RTTI capabilities in the library, it will be possible to add FPC to the benchmark too.

Well, I'd like to know where FPC stands in comparison to Delphi (I did not yet develop any benchmarks for that myself) and in comparison to other libraries (you mentioned the various alternatives to TValue in your benchmarks). And I'd really like to try to improve the performance of the Rtti unit without sacrificing the Delphi compatibility.

That's also the second point: there shouldn't be much need to adjust much, cause FPC's Rtti unit is compatible to Delphi's though it does not yet support everything. But that what exists is supposed to be compatible.

DevilDevil

  • New Member
  • *
  • Posts: 49
Re: obj file linking: function alias
« Reply #4 on: November 22, 2020, 05:15:30 pm »
PascalDragon,

This design works:
Code: Pascal  [Select][+][-]
  1. function AStrInit; external name 'AStrInit';
  2.  
  3. function AStrInit_fpc(var S{: AnsiString}; const AChars: PAnsiChar; const ALength: NativeUInt; const ACodePage: Word): PAnsiChar; public alias:'AStrInit';
  4. begin
  5.   Result := nil;
  6. end;  

But the AStrInit_fpc implementation is not in * .pas, but in the object file. Accordingly, I need something like this:
Code: Pascal  [Select][+][-]
  1. function AStrInit; external name 'AStrInit';
  2.  
  3. function AStrInit_fpc(var S{: AnsiString}; const AChars: PAnsiChar; const ALength: NativeUInt; const ACodePage: Word): PAnsiChar;
  4.   external name 'AStrClear_fpc'; public alias: 'AStrInit';

How to implement this?

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: obj file linking: function alias
« Reply #5 on: November 22, 2020, 05:42:27 pm »
Most C libraries do not use uppercase in the exported names. Are you sure about the Astr etc isn't it simply astr etc?
The imported names must match exactly. Of course the alias may contain pascal like uppercase on the pascal side, but the import names are likely all lowercase Can you check that? Also, most C libraries are cdecl, which differs from Pascal default, so you probably need to add that too.
Delphi interfaces to C code behave identical as the above, btw.
It is of course different if the C code is not exposed at all. Pascal libraries are often camel cased and that should work provided the calling convention is correct. But even in that case the imported names must match exactly and again, same as in Delphi.
« Last Edit: November 22, 2020, 05:57:26 pm by Thaddy »
Specialize a type, not a var.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: obj file linking: function alias
« Reply #6 on: November 22, 2020, 06:03:03 pm »
But the AStrInit_fpc implementation is not in * .pas, but in the object file. Accordingly, I need something like this:

You are aware that I had explicitely asked whether the former is the case?

So again:
- you have an object file (compiled with a C or C++ compiler) that contains the symbol AStrInit_fpc
- you have a Pascal unit that imports this function as AStrInit
- you want to access it from C or C++ code as AStrInit

Is this correct now?

Okay, in that case: you can't. At least not without writing code (e.g. a stub that calls AStrInit_fpc while you export AStrInit with a public alias) or doing some other manual stuff.

Most C libraries do not use uppercase in the exported names. Are you sure about the Astr etc isn't it simply astr etc?

DevilDevil stated that the symbol that is missing is the one provided by the Pascal unit and consumed by the C code.

DevilDevil

  • New Member
  • *
  • Posts: 49
Re: obj file linking: function alias
« Reply #7 on: November 22, 2020, 06:44:22 pm »
Thaddy,

Function names are the same.
Repeating the Pascal calling convention is achieved through this trick:

Code: C++  [Select][+][-]
  1. #if defined (CPUX86)
  2.   #define REGISTER_DECL __attribute__((stdcall)) __attribute__((regparm(3)))
  3. #else
  4.   #define REGISTER_DECL
  5. #endif

DevilDevil

  • New Member
  • *
  • Posts: 49
Re: obj file linking: function alias
« Reply #8 on: November 22, 2020, 07:00:45 pm »
PascalDragon,

Quote
Is this correct now?

Yep
Here is the implementation of the function:
https://github.com/d-mozulyov/Tiny.Library/blob/master/c/tiny.types.c#L1091

This is how the function looks to a Delphi/FPC developer:
https://github.com/d-mozulyov/Tiny.Library/blob/master/Tiny.Types.pas#L1472

This is how the function is linked to the final function:
https://github.com/d-mozulyov/Tiny.Library/blob/master/Tiny.Types.pas#L3061

Now I put in a generic function description in the header:
Code: C++  [Select][+][-]
  1. REGISTER_DECL void* AStrInit(void* value, char8_t* chars, native_uint length, uint16_t codepage);

It is necessary that when this function is called on the C side, the final function is called. In Delphi, this is easy enough. Not in FPC yet.

DevilDevil

  • New Member
  • *
  • Posts: 49
Re: obj file linking: function alias
« Reply #9 on: November 23, 2020, 05:04:28 am »
So far I did it this way. If you have ideas on how to improve - please speak )

Code: Pascal  [Select][+][-]
  1. {$if Defined(FPC) and Defined(CPUX64)}
  2. procedure __AStrClear; assembler; nostackframe; public name 'AStrClear';
  3. asm
  4.   jmp AStrClear
  5. end;
  6. procedure __AStrInit; assembler; nostackframe; public name 'AStrInit';
  7. asm
  8.   jmp AStrInit
  9. end;
  10. procedure __AStrReserve; assembler; nostackframe; public name 'AStrReserve';
  11. asm
  12.   jmp AStrReserve
  13. end;
  14. procedure __AStrSetLength; assembler; nostackframe; public name 'AStrSetLength';
  15. asm
  16.   jmp AStrSetLength
  17. end;
  18. procedure __UStrClear; assembler; nostackframe; public name 'UStrClear';
  19. asm
  20.   jmp UStrClear
  21. end;
  22. procedure __UStrInit; assembler; nostackframe; public name 'UStrInit';
  23. asm
  24.   jmp UStrInit
  25. end;
  26. procedure __UStrReserve; assembler; nostackframe; public name 'UStrReserve';
  27. asm
  28.   jmp UStrReserve
  29. end;
  30. procedure __UStrSetLength; assembler; nostackframe; public name 'UStrSetLength';
  31. asm
  32.   jmp UStrSetLength
  33. end;
  34. procedure __CStrClear; assembler; nostackframe; public name 'CStrClear';
  35. asm
  36.   jmp CStrClear
  37. end;
  38. procedure __CStrInit; assembler; nostackframe; public name 'CStrInit';
  39. asm
  40.   jmp CStrInit
  41. end;
  42. procedure __CStrReserve; assembler; nostackframe; public name 'CStrReserve';
  43. asm
  44.   jmp CStrReserve
  45. end;
  46. procedure __CStrSetLength; assembler; nostackframe; public name 'CStrSetLength';
  47. asm
  48.   jmp CStrSetLength
  49. end;
  50. {$elseif Defined(FPC)}
  51.   {$MESSAGE ERROR 'Unsupported platform'}
  52. {$ifend}

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: obj file linking: function alias
« Reply #10 on: November 23, 2020, 01:37:14 pm »
So far I did it this way. If you have ideas on how to improve - please speak )

That's probably the best you can do so far.

What you could additionally do is to put these into an include file and have that file generated by some utility that either uses a list of these functions or parses the unit where these functions are declared. This way there'll be less trouble once you support more platforms with FPC and then add an additional function.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11382
  • FPC developer.
Re: obj file linking: function alias
« Reply #11 on: November 23, 2020, 02:30:08 pm »
So basically this exploits symbols that Delphi adds to the RTL for BDB's benefit ?

DevilDevil

  • New Member
  • *
  • Posts: 49
Re: obj file linking: function alias
« Reply #12 on: November 23, 2020, 03:37:40 pm »
PascalDragon,

I can't believe that in FPС it is impossible to make a "public" function, whose implementation is in the object file :)

marcov,

To be honest, I didn't quite understand your question. Could you clarify it?

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: obj file linking: function alias
« Reply #13 on: November 23, 2020, 04:32:19 pm »
PascalDragon,

I can't believe that in FPС it is impossible to make a "public" function, whose implementation is in the object file :)

It's not FPC's task to do that. It's a Pascal compiler, not a "make assembly symbol of object file X available with a different name for object file Y"-utility.

marcov,

To be honest, I didn't quite understand your question. Could you clarify it?

You're essentially using that BCB and Delphi have a rather tight relationship and thus BCB can easily access Delphi symbols. The same is not true for FPC and any C compiler.

DevilDevil

  • New Member
  • *
  • Posts: 49
Re: obj file linking: function alias
« Reply #14 on: November 23, 2020, 04:58:00 pm »
PascalDragon,

I use Clang, rather than C++Builder :)

 

TinyPortal © 2005-2018