Recent

Author Topic: Building static libraries (.a)  (Read 832 times)

Fibonacci

  • Full Member
  • ***
  • Posts: 219
  • #PDK
Building static libraries (.a)
« on: October 03, 2023, 10:49:04 am »
Is it possible to build a static library for static linking in other projects and other languages in FPC?

What I did so far..

Create some library (does it matter? I just need the .o file)

Code: Pascal  [Select][+][-]
  1. library nothing;
  2.  
  3. procedure donothing; stdcall; [public, alias: 'pubdonothing'];
  4. begin
  5. end;
  6.  
  7. end.

Compile it to get the .o file

Quote
fpc nothing.pas

Build .a file with index

Quote
ar rcs libnothing.a nothing.o

Index looks like this

Quote
nothing.o    P$NOTHING_$$_DONOTHING
nothing.o    pubdonothing
nothing.o    _P$NOTHING_$$_main
nothing.o    PASCALMAIN
nothing.o    INITFINAL
nothing.o    FPC_THREADVARTABLES
nothing.o    FPC_RESOURCESTRINGTABLES
nothing.o    FPC_WIDEINITTABLES
nothing.o    FPC_RESSTRINITTABLES
nothing.o    __heapsize
nothing.o    __fpc_valgrind

Thanks to public+alias there is a nice and clean "pubdonothing"

Now include this file

Code: Pascal  [Select][+][-]
  1. {$linklib libnothing.a}

Import "pubdonothing"

Code: Pascal  [Select][+][-]
  1. procedure pubdonothing; stdcall; external;

And try to compile it

Code: Pascal  [Select][+][-]
  1. Error: Multiple defined symbol "PASCALMAIN"
  2. Error: Multiple defined symbol "INITFINAL"
  3. Error: Multiple defined symbol "FPC_THREADVARTABLES"
  4. Error: Multiple defined symbol "FPC_RESOURCESTRINGTABLES"
  5. Error: Multiple defined symbol "FPC_WIDEINITTABLES"
  6. Error: Multiple defined symbol "FPC_RESSTRINITTABLES"
  7. Error: Multiple defined symbol "__heapsize"
  8. Error: Multiple defined symbol "__fpc_valgrind"

So, how do I get rid of these unwanted symbols from the .o file? Or should I just modify the index in the .a file? That even possible?

Warfley

  • Hero Member
  • *****
  • Posts: 1469
Re: Building static libraries (.a)
« Reply #1 on: October 03, 2023, 12:11:23 pm »
The problem is that any pascal program always starts of with initializing the RTL, as this is required for the runtime.
When building a library you can't expect that it is used within a program that uses the same RTL (maybe it's a non pascal program which does not use the RTL at all, maybe it's an fpc program with a different compiler version, etc.)

This doesn't matter with dynamic libraries because they are much more seperated and can all have their own RTL instance, but when using a static library this of course gives collisions.

There are two options, option
1. rename the RTL functions: you could compile not to a binary but simply to an ASM file and then do simple string replace over the ASM labels. Then use your favorite assembler to build the binary
Note that the host application must call the Initialize for every used libraries RTL manually

2. Use a common RTL. How C gets around this problem is quite simple, instead of compiling a libc into the program like the RTL is, all c programs on the system use the same ABI stable system libc installed with your OS. This way you don't have collisions because all programs parts of the program use the same libc.
With pascal you could do that by instead of building a static library, which contains the RTL, only build the object files without linking them to the RTL and link those into your host program, which provides the RTL. For embedding in non RTL programs (like C) you could simply provide an additional static RTL library. Note that the host language then needs to call the initialization code for the RTL library manually

Because the RTL is not ABI stable, you should probably go with the first option
« Last Edit: October 03, 2023, 12:13:01 pm by Warfley »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11079
  • FPC developer.
Re: Building static libraries (.a)
« Reply #2 on: October 03, 2023, 12:29:16 pm »
A LIBRARY module is meant to be a separately linked entity, i.e. a dynamic library.

If you want to experiment with static libraries, simply using the .o's of units make more sense and ARing them together. 

Units however also have some finalization/initialization symbols, but I'd guess that is less than going from a .o meant to build a DLL/.so

Of course the practicallity is low due to RTL concerns, which is why it is rarely done, at least not in a generic way.

Fibonacci

  • Full Member
  • ***
  • Posts: 219
  • #PDK
Re: Building static libraries (.a)
« Reply #3 on: October 03, 2023, 12:43:13 pm »
@Warfley

Brilliant!

Changed donothing to actually do something

Code: Pascal  [Select][+][-]
  1. procedure donothing; stdcall; [public, alias: 'pubdonothing'];
  2. begin
  3.   writeln('donothing called');
  4. end;

Quote
fpc -al nothing.pas

... rename RTLs in the .s file

Quote
as -o nothing.o nothing.s

Quote
ar rcs libnothing.a nothing.o

Thanks very much!

PascalDragon

  • Hero Member
  • *****
  • Posts: 5281
  • Compiler Developer
Re: Building static libraries (.a)
« Reply #4 on: October 03, 2023, 08:58:35 pm »
Brilliant!

That only works because you linked it into a FPC program which has all the symbols that might be needed anyway. If you'd link the library into a C program you'd get errors about missing symbols - in your specific case related to the Writeln.

Fibonacci

  • Full Member
  • ***
  • Posts: 219
  • #PDK
Re: Building static libraries (.a)
« Reply #5 on: October 03, 2023, 09:10:02 pm »
That only works because you linked it into a FPC program which has all the symbols that might be needed anyway. If you'd link the library into a C program you'd get errors about missing symbols - in your specific case related to the Writeln.

Yes, thats the issue now

Facts
1. libnothing.a has nothing.o inside
2. nothing.o uses SysUtils
3. nothing.o has 1 procedure (named "ints") using IntToStr

{$linklib libnothing.a} + "ints" works if full RTL is provided with -Fu <-- this is because of what you said

Now, assume I have only ppc386.exe, or its not even FPC but a different language, how do I link in full RTL from a single .a file?

I did:
- go to units\i386-win32\rtl
- unpack all .a (libimp*.a) to get .o files
- AR all .o to single .a

I have ~9 MB .a file with full RTL

If I
{$linklib librtl.a}
and {$linklib libnothing.a}
and call "ints"
and dont provide a path to the RTL with -Fu
= I get an error there is no RTL, "cannot find system"

So I created minimal RTL - system.pas, sysinitpas.pas, objpas.pas and fpintres.pas

This minimal RTL alone works fine, I can import some functions from WinAPI and use them. Binary output is exactly 3.5 kB (header + 6 sections, 512 bytes align), thats nice.

Back to librtl.a, if I include this file with minimal RTL I get these errors: (and more)

Quote
Error: Multiple defined symbol "fpc_pchar_to_shortstr"
Error: Multiple defined symbol "FPC_PCHAR_TO_SHORTSTR"
Error: Multiple defined symbol "fpc_ansistr_decr_ref"
Error: Multiple defined symbol "FPC_ANSISTR_DECR_REF"
Error: Multiple defined symbol "fpc_ansistr_assign"
Error: Multiple defined symbol "FPC_ANSISTR_ASSIGN"

I assume thats not because there are 2 versions (lower and upper case), but there are multiple .o's with these functions

So I looked in what files "fpc_pchar_to_shortstr" exists

Quote
grep -Ril "fpc_pchar_to_shortstr" .
./dos.o
./exeinfo.o
./heaptrc.o
./strings.o
./strings.ppu
./system.o
./system.ppu
./sysutils.o

So these functions exists in multple files, so If I create all-in-one RTL there will be duplicates.

Im stuck here.

Can RTL be shipped in one .a file?

Or FPC is just a bad choice to build static libraries and I should give up?
« Last Edit: October 03, 2023, 09:23:43 pm by Fibonacci »

PascalDragon

  • Hero Member
  • *****
  • Posts: 5281
  • Compiler Developer
Re: Building static libraries (.a)
« Reply #6 on: October 03, 2023, 09:39:30 pm »
So I looked in what files "fpc_pchar_to_shortstr" exists

Quote
grep -Ril "fpc_pchar_to_shortstr" .
./dos.o
./exeinfo.o
./heaptrc.o
./strings.o
./strings.ppu
./system.o
./system.ppu
./sysutils.o

So these functions exists in multple files, so If I create all-in-one RTL there will be duplicates.

These are not all declarations of the symbol, but also usages (cause the o-files contain the literal symbol names so that the linker can resolve them).

Im stuck here.

Can RTL be shipped in one .a file?

One possible solution is the following: Compile your code as an ordinary shared library, but pass -sh to the compiler. This will create a ppas.sh-file which references a linkXXX.res file in which you'll find a list of all the object files required. Please note that you'll have to call the initialization and finalization functions of the units as well.

Or FPC is just a bad choice to build static libraries and I should give up?

Simply put: yes, it is. Best create a dynamic library or compile a FPC program.

Fibonacci

  • Full Member
  • ***
  • Posts: 219
  • #PDK
Re: Building static libraries (.a)
« Reply #7 on: October 03, 2023, 09:50:39 pm »
One possible solution is the following: Compile your code as an ordinary shared library, but pass -sh to the compiler. This will create a ppas.sh-file which references a linkXXX.res file in which you'll find a list of all the object files required. Please note that you'll have to call the initialization and finalization functions of the units as well.

That sounds promising, thanks, I will have some fun tomorrow :)

Fibonacci

  • Full Member
  • ***
  • Posts: 219
  • #PDK
Re: Building static libraries (.a)
« Reply #8 on: October 04, 2023, 04:15:55 am »
If I dont have fpc_initializeunits in my RTL system.pas
Code: Pascal  [Select][+][-]
  1. Error: Unknown compilerproc "fpc_initializeunits". Check if you use the correct run time library.

After I add this function
Code: Pascal  [Select][+][-]
  1. Error: Multiple defined symbol "fpc_initializeunits"

Looks like I should have a different RTL in the .a file, with everything renamed

So FPC can make static libs only for use in FPC, with the same RTL; or the lib would need to be written without using any RTL (not using even strings)

Game over, case closed, not worth

 

TinyPortal © 2005-2018