Static Linking

From Freepascal Amiga wiki
Revision as of 13:39, 25 September 2016 by Molly (talk | contribs) (→‎Linking Method 2: Initial content for step 4)
Jump to navigation Jump to search

This topic is about static linking against AROS sdk libraries/objects with FPC compiler.

First let's take this moment here to inform that the acquired information and results could not have been obtained and created without the really fantastic help and support provided by AROS developer Deadwood. We can't thank him enough for his patience and shared knowledge. Also a big thank you to Chain-Q for helping us through this process by implementing new calling mechanism support in FPC for AROS.


When source-code is compiled by the compiler, there is a special program being used to accomplish the task of generating a workable executable for you. That program is called a linker.

Wikipedia explains what is a linker [1]:
In computing, a linker or link editor is a computer program that takes one or more object files generated by a compiler and combines them into a single executable file, library file, or another object file.

And on the topic of static linking Wikipedia writes [2]:
Static linking is the result of the linker copying all library routines used in the program into the executable image. This may require more disk space and memory than dynamic linking, but is more portable, since it does not require the presence of the library on the system where it is run.

The latter description being what we would like to accomplish with Free Pascal and AROS. The AROS SDK comes accompanied with quite some interesting available static libraries which we would like to use. Besides the SDK, the AROS archives also contains static libraries that were ported to AROS.

In case wondering, Wikipedia writes about a static library [3]:
In computer science, a static library or statically-linked library is a set of routines, external functions and variables which are resolved in a caller at compile-time and copied into a target application by a compiler, linker, or binder, producing an object file and a stand-alone executable.

Linking and Free Pascal

Free Pascal is just like any other compiler out there in that it uses the binutils provided linker (ld, or in the specific case of AROS it calls ld indirectly using the collect-aros binutil) in order to link against (static) objects in order to incorporate the linked code into the final produced executable.

An important thing to realize before any attempt is made to link against object files, is that the compiler is to be told where exactly it is able to locate those objects (in case you do not have them lined up side by side to your source-code).

TODO: Add links to setting up paths

Some keywords with regards to linking and Free Pascal

  • directive {$LINK} [4]
  • directive {$LINKLIB} [5]
  • Errors of assembling/linking stage [6]

Some (Free Pascal) caveats:

  • The Free Pascal compiler is only able to show linker results when the main program source is compiled
  • The Free Pascal compiler will only link when at least one functionality of the static libraries is actually invoked inside your code. Just linking against the object(s) does not necessarily means the linker will link in all the requested objects.
  • When exported symbols are not 'used' in the eyes of the compiler, they will get optimized away, even when optimizations are turned off. This is a very important caveat to realize and it requires some trickery in order to force the compiler to do our bidding.

There are also some additional caveats depending on which linking method is being used, for that please refer to their corresponding paragraphs which describes those caveats.


In the process of experimenting with this topic different approaches were taken in order to achieve results.

It is always a bit of a difficult spot to give a name to something that was recently accomplished as the mind is still full of impressions focused on accomplishing a single goal, thereby losing oversight of the bigger picture (so in case reader has a better name for them, please feel free to suggest). So, at first idea i describe the different approaches:

  • Method 1: controlled linking with FPC
  • Method 2: 'Abuse' AROS' default startup/linking system

Linking Method 1

For a practical example see ...

Linking Method 2

For a practical example see ...

  • Step 1: Link in basic required objects
For this approach at least these 3 default items should be linked to your project:
  • libarosc.a
  • libautoinit.a
  • startup.o
These 3 link objects are the bare minimum required to resolve the symbols that are needed to get your chosen library linked in correctly in order to be used inside your Pascal project.
{$LINKLIB libarosc.a}
{$LINKLIB libautoinit.a}
{$LINK startup.o}
While libarosc takes care of resolving the clib functions, libautoinit supplies initialization routines (also symbols needed by libarosc) while startup.o takes care of providing the actual startup code and resolves the last bits of symbols needed by libarosc and libautoinit.
On the Pascal side of things we would need to implement functionality that initializes things as AROS would do. More on this later.
  • Step 2: Link in library of choice
If the above 3 objects are linked in successfully then we are left with the most important part namely linking in the static library of your choice.
This is done in a similar way:
{$LINKLIB NameOfYourPreferredLibray.a}
... where you replace the part "NameOfYourPreferredLibray.a" with the filename of the library that you actually wish to link to.
When done so successfully, you are able to use the functionality from this library to be used from Pascal side.
Note that sometimes a static library needs other objects to be able to resolve all missing symbols, in which case additional objects would need to be linked in as well. Which objects that are exactly depends completely on the library that is linked against and would require some proper research.
In case you are familiar with c-coding on AROS you are probably already aware which objects are used by the different available static libraries.
  • Step 3: Declaring Pascal headers for the linked library
Although said in the previous step that you could now use the functionality from the static library, we can only make such thing happen when all types, constants and functions have been declared in a so called Pascal wrapper.
That is also reason it is easier to link against the library of your choice in an existing header file that is already available for your library. In worse case scenario such header file does not exist and in which case you would have to convert from original sdk of that library.
It is a bit out of scope to make here a full example, therefor please see here. (TODO: change link)
Be sure to at least read the chapter "Linking Issues" of the Free Pascal manual. There you are able to read about keywords/modifiers such as "external", "cvar" and "name" which more or less are used to import and export variables and procedures/functions.
  • Step 4: Mimic startup code
In order to get the initialization process used by AROS working for use with Free Pascal we are obligated to mimic some of that behaviour, otherwise we would be linking but none of the linked in objects would then be properly initialized (and so things would crash on usage).
The code we need to mimic originates from the file startup.c (it is indeed the original source-file that created the startup.o object that we linked against). (TODO: Add link to original startup.c file).
The two functions that we need to call are:
  • __startup_entries_init;
  • __startup_entries_next;
But, in order to be able to do so we also need some additional code:
  • Export and intialize SysBase variable
  • Export and intialize DOSBase variable
  • import function __startup_entries_init()
  • import function ___startup_entries_next()
  • Create a wrapper function __startup_entries_next() that invokes ___startup_entries_next()
If that's implemented then we are almost there, albeit some additional tuning for Free Pascal is required (more on that later).
Let's start with implementing the basic things first (and remember that exported variables mingle with c-code and that c-code is case sensitive), and let's use the main program file for this case in order to do so
  // Import variables SysBase and DOSBase
  SysBase         : Pointer; cvar; export;
  DOSBase         : Pointer; cvar; export;

  // Import the two startup entries functions
  procedure __startup_entries_init; cdecl; external;
  procedure ___startup_entries_next(SystemBase: Pointer); cdecl; external;

// startup_entries_next() wrapper function
procedure __startup_entries_next;

// AROSC Initialization routine
procedure AROSC_Init;
  // Intialialize SysBase variable
  SysBase := AOS_ExecBase;

  // Initialize DOSBase variable
  DOSBase := AOS_DosBase;

  // Call startup_entries_init()

  // Call startup_entries_next()

Under no cirumstances run this code yet, or at least, not with calling the function __startup_entries_next() yet (you can comment it out to see the results sofar if you like).
Now, proceed to the following step