Difference between revisions of "System unit"

From Lazarus wiki
m (Add page template and remove categories (defined in the template))
(Initialization and finalization of units)
 
(33 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
{{System_unit}}
 
{{System_unit}}
  
In [[FPC]] the <syntaxhighlight lang="pascal" enclose="none">system</syntaxhighlight> unit is the unit that is [[Uses|included]] by ''every'' [[Program|program]].
+
In Free Pascal the '''System''' unit is the unit that is included by ''every'' program.
FPC's [[RTL|run-time library]] comes with a <syntaxhighlight lang="pascal" enclose="none">system</syntaxhighlight> unit where most, if not all of its functionalities work on ''every'' available [[Platform list|platform]].
+
FPC's [[RTL|run-time library]] comes with a System unit where most, if not all of its functionalities work on every available [[Platform list|platform]].
  
== compilation of system units ==
+
Also depending on the platform it's not true that no further unit is added, because for example on Windows the '''SysInit''' unit is also used, because that contains the entry point for the binary.
To compile a system unit one calls FPC with the <syntaxhighlight lang="bash" enclose="none">-Us</syntaxhighlight> flag.
 
This will tell the compiler that only basic type definitions are being made, and including a system unit is skipped.
 
Types such as [[Integer|<syntaxhighlight lang="pascal" enclose="none">integer</syntaxhighlight>]] won't be available.
 
  
=== mandatory tasks of system unit ===
+
== Compilation of System unit ==
 +
For quite some time already FPC automatically applies the -Us switch to the unit named System, so you don't need the -Us switch. The -Us switch tells the compiler that only basic type definitions are being made.
 +
Types such as [[Integer]] are not yet available.
 +
 
 +
=== Mandatory tasks of System unit ===
 
If you attempt to create your own system unit, you'll incrementally notice what the compiler needs or what the generated code expects.
 
If you attempt to create your own system unit, you'll incrementally notice what the compiler needs or what the generated code expects.
 
On a Linux target, the minimal system unit has to contain:
 
On a Linux target, the minimal system unit has to contain:
<syntaxhighlight lang="pascal" line start="1">
+
<syntaxhighlight lang="pascal">
 
unit system;
 
unit system;
  
 
interface
 
interface
 
</syntaxhighlight>
 
</syntaxhighlight>
The type definition of <syntaxhighlight lang="pascal" enclose="none">hresult</syntaxhighlight>.
+
The type '''hresult''' must be defined, for example:
<syntaxhighlight lang="pascal" line start="5">
+
<syntaxhighlight lang="pascal">
 
type
 
type
hresult = longint;
+
  hresult = LongInt;
 
</syntaxhighlight>
 
</syntaxhighlight>
The identifier <syntaxhighlight lang="pascal" enclose="none">operatingsystem_result</syntaxhighlight> has to be declared.
+
The identifier '''operatingsystem_result''' has to be declared.
 
The data stored at that position are communicated to the operating system as return value.
 
The data stored at that position are communicated to the operating system as return value.
Whether <syntaxhighlight lang="pascal" enclose="none">hresult</syntaxhighlight> and <syntaxhighlight lang="pascal" enclose="none">operatingsystem_result</syntaxhighlight> are defined in the <syntaxhighlight lang="pascal" enclose="none">interface</syntaxhighlight> section is irrelevant.
+
Whether '''hresult''' and '''operatingsystem_result''' are defined in the "interface" section is irrelevant.
However, it is very plausible to put those into the <syntaxhighlight lang="pascal" enclose="none">interface</syntaxhighlight> section, so programs using this <syntaxhighlight lang="pascal" enclose="none">system</syntaxhighlight> unit can manipulate those.
+
However, it is very plausible to put those into the "interface" section, so programs using this system unit can manipulate those.
<syntaxhighlight lang="pascal" line start="8">
+
<syntaxhighlight lang="pascal">
 
var
 
var
exitCode: hresult = 0; export name 'operatingsystem_result';
+
  ExitCode: hresult = 0; export name 'operatingsystem_result';
  
 
implementation
 
implementation
 
</syntaxhighlight>
 
</syntaxhighlight>
 
Here comes to light what the system responsibilities are:
 
Here comes to light what the system responsibilities are:
It has to initialize (or should) initialize units, that means call every unit's [[Initialization|<syntaxhighlight lang="pascal" enclose="none">initialization</syntaxhighlight> routines]].
+
It has to (or should) initialize units, that means call every unit's [[Initialization]] routines.
For that the label <syntaxhighlight lang="pascal" enclose="none">FPC_INITIALIZEUNITS</syntaxhighlight> has to be defined.
+
For that the label '''FPC_INITIALIZEUNITS''' has to be defined.
<syntaxhighlight lang="pascal" line start="13">
+
<syntaxhighlight lang="pascal">
procedure initializeUnits; alias: 'FPC_INITIALIZEUNITS';  
+
procedure InitializeUnits; alias: 'FPC_INITIALIZEUNITS';  
 
begin
 
begin
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
Also the label <syntaxhighlight lang="pascal" enclose="none">FPC_DO_EXIT</syntaxhighlight> has to be defined.
+
Also the label '''FPC_DO_EXIT''' has to be defined.
 
Here, system unit programmers have the chance to finalize units.
 
Here, system unit programmers have the chance to finalize units.
<syntaxhighlight lang="pascal" line start="17">
+
<syntaxhighlight lang="pascal">
 
procedure doExit; alias: 'FPC_DO_EXIT';
 
procedure doExit; alias: 'FPC_DO_EXIT';
 
begin
 
begin
Line 50: Line 51:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Furthermore FPC will possibly complain the unit <syntaxhighlight lang="bash" enclose="none">fpintres.pp</syntaxhighlight> were missing.
+
Furthermore FPC will possibly complain the unit FPIntRes is missing.
So create a file <syntaxhighlight lang="pascal" enclose="none">fpintres.pas</syntaxhighlight>:
+
So create a file fpintres.pas:
<syntaxhighlight lang="pascal" line start="1">
+
<syntaxhighlight lang="pascal">
 
unit FPIntRes;
 
unit FPIntRes;
 
interface
 
interface
Line 59: Line 60:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
After doing so, you can compile your system unit with <syntaxhighlight lang="bash" enclose="none">fpc -Us system.pas</syntaxhighlight>, or directly create an empty program and compile it to examine the impact on the binary's size.
+
After doing so, you can compile your system unit (flag -Us is not needed), or directly create an empty program and compile it to examine the impact on the binary's size.
On a x86-64 Linux target the executable had a size of 2744 bytes.
+
On a x86-64 Linux target the executable had a size of 2.7 Kbytes.
 
The big trade-off is no convenience.
 
The big trade-off is no convenience.
 
All functionality that can't be “implemented” by the compiler is gone.
 
All functionality that can't be “implemented” by the compiler is gone.
For example {{Doc|package=RTL|unit=system|identifier=addr|text=<syntaxhighlight lang="pascal" enclose="none">addr</syntaxhighlight>}} or {{Doc|package=RTL|unit=system|identifier=ord|text=<syntaxhighlight lang="pascal" enclose="none">ord</syntaxhighlight>}} still work, while {{Doc|package=RTL|unit=system|identifier=sin|text=<syntaxhighlight lang="pascal" enclose="none">sin</syntaxhighlight>}} or {{Doc|package=RTL|unit=system|identifier=random|text=<syntaxhighlight lang="pascal" enclose="none">random</syntaxhighlight>}} are not available.
+
For example [[Addr]] or [[Ord]] still work, while Sin() or Random() are not available.
 +
Confer the procedure '''create_intern_symbols''' in "compiler/psystem.pas".
  
 
{{Note|Depending on what your program uses, there might ''other'' mandatory tasks, too.
 
{{Note|Depending on what your program uses, there might ''other'' mandatory tasks, too.
Especially OOP won't work without a functional <syntaxhighlight lang="pascal" enclose="none">objpas</syntaxhighlight> unit.}}
+
Especially OOP won't work without a functional ObjPas unit.}}
  
=== initialization and finalization of units ===
+
=== Initialization and finalization of units ===
For those who are curious, how to implement <syntaxhighlight lang="pascal" enclose="none">FPC_INITIALIZEUNITS</syntaxhighlight>:
+
For those who are curious, how to implement FPC_INITIALIZEUNITS:
FPC puts into every program's data section a <syntaxhighlight lang="asm" enclose="none">INITFINAL</syntaxhighlight> table.
+
FPC puts into every program's data section a INITFINAL table.
Compiling a program with the <syntaxhighlight lang="bash" enclose="none">-al</syntaxhighlight> flag will retain the [[Assembly language|assembler]] file.
+
Compiling a program with the "-al" flag will retain the Assembler file "*.s".
Examining it you will encounter something like:
+
Examining this Assembler file, you will encounter something like:
<syntaxhighlight lang="asm" line start="33" highlight="5-8">
+
<syntaxhighlight lang="asm">
 
.section .data.n_INITFINAL
 
.section .data.n_INITFINAL
 
         .balign 8
 
         .balign 8
Line 83: Line 85:
 
         .quad  FINALIZE$_$SOMEUNIT
 
         .quad  FINALIZE$_$SOMEUNIT
 
</syntaxhighlight>
 
</syntaxhighlight>
For further investigations have a glimpse at an actual implementation helps:
+
For further investigations have a glimpse at an actual implementation: [https://gitlab.com/freepascal.org/fpc/source/-/blob/main/rtl/inc/system.inc "rtl/inc/system.inc"].
[https://svn.freepascal.org/cgi-bin/viewvc.cgi/tags/release_3_0_4/rtl/inc/system.inc?view=markup#l862 <tt>rtl/inc/system.inc</tt>].
 
  
 
{{Note|In general, implementing an “own” system unit is a stupid idea.
 
{{Note|In general, implementing an “own” system unit is a stupid idea.
 
<!-- Also, it's a waste of man-hours. -->
 
<!-- Also, it's a waste of man-hours. -->
There is “smartlinking” if [[Size Matters|size matters]].
+
There is “smart-linking” if [[Size Matters|size matters]].
 
<!-- Help by [[Creating A Patch|creating a patch]] instead. -->}}
 
<!-- Help by [[Creating A Patch|creating a patch]] instead. -->}}
  
== comparative remarks ==
+
=== Other issues with compilation ===
Other compilers such as [[GNU Pascal|GPC]] or [[Delphi]] use similar constructs, but to varying extent, and the system unit is not necessarily named <syntaxhighlight lang="bash" enclose="none">system.pp</syntaxhighlight>.
+
 
 +
* Q: Now FPC wants to compile the ObjPas unit.
 +
* A: That is because your project's mode is set to ObjFPC (Lazarus will pass a -MObjFPC parameter to the compiler then) which automatically leads to an insertion of the ObjPas unit. Either change your project's mode in Project Settings -> Compiler Settings -> Parsing or add a {$mode fpc} to your main program file.
 +
 
 +
----
 +
 
 +
* Q: Now FPC cannot find this:
 +
 
 +
Unknown compilerproc "fpc_initializeunits". Check if you use the correct run time library.
 +
 
 +
* A: You need to add a
 +
<syntaxhighlight lang="pascal">
 +
procedure fpc_initializeunits; compilerproc;
 +
</syntaxhighlight>
 +
both in the interface and implementation section.
 +
 
 +
----
 +
 
 +
* Q: I am stuck, FPC says:
 +
 
 +
Internal type "TEXCEPTADDR" was not found
 +
 
 +
* A: The compiler expects some system defined types to be defined when parsing a subroutine such as the implementation section of System. This means you have to add some type definitions to System. It appears that this check was added to 3.2.0.
 +
 
 +
== Example of System unit ==
 +
 
 +
The example is made by user Okoba [https://forum.lazarus.freepascal.org/index.php?topic=49434.msg367677#msg367677 at forum].
 +
 
 +
File system.pas:
 +
 
 +
<syntaxhighlight lang="pas">
 +
unit system;
 +
{$MODE FPC}
 +
interface
 +
 
 +
type
 +
  hresult = LongInt;
 +
  DWord = LongWord;
 +
  Cardinal = LongWord;
 +
  Integer = SmallInt;
 +
  UInt64 = QWord;
 +
  TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat, tkSet,
 +
    tkMethod, tkSString, tkLString, tkAString, tkWString, tkVariant, tkArray,
 +
    tkRecord, tkInterface, tkClass, tkObject, tkWChar, tkBool, tkInt64, tkQWord,
 +
    tkDynArray, tkInterfaceRaw, tkProcVar, tkUString, tkUChar, tkHelper, tkFile,
 +
    tkClassRef, tkPointer);
 +
 
 +
type
 +
  jmp_buf = packed record
 +
    rbx, rbp, r12, r13, r14, r15, rsp, rip: QWord;
 +
    {$IFDEF win64}
 +
    rsi, rdi: QWord;
 +
    xmm6, xmm7, xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15: record
 +
      m1, m2: QWord;
 +
    end;
 +
 
 +
    mxcsr: LongWord;
 +
    fpucw: word;
 +
    padding: word;
 +
    {$ENDIF win64}
 +
  end;
 +
 
 +
  Pjmp_buf = ^jmp_buf;
 +
  PExceptAddr = ^TExceptAddr;
 +
  TExceptAddr = record
 +
    buf: Pjmp_buf;
 +
    next: PExceptAddr;
 +
    {$IFDEF CPU16}
 +
    frametype: SmallInt;
 +
    {$ELSE CPU16}
 +
    frametype: LongInt;
 +
    {$ENDIF CPU16}
 +
  end;
 +
 
 +
  PGuid = ^TGuid;
 +
  TGuid = packed record
 +
    case Integer of
 +
    1:
 +
    (Data1: DWord;
 +
      Data2: word;
 +
      Data3: word;
 +
      Data4: array [0 .. 7] of byte;
 +
    );
 +
    2:
 +
    (D1: DWord;
 +
      D2: word;
 +
      D3: word;
 +
      D4: array [0 .. 7] of byte;
 +
    );
 +
    3:
 +
    ( { uuid fields according to RFC4122 }
 +
      time_low: DWord; // The low field of the timestamp
 +
      time_mid: word; // The middle field of the timestamp
 +
      time_hi_and_version: word;
 +
      // The high field of the timestamp multiplexed with the version number
 +
      clock_seq_hi_and_reserved: byte;
 +
      // The high field of the clock sequence multiplexed with the variant
 +
      clock_seq_low: byte; // The low field of the clock sequence
 +
      node: array [0 .. 5] of byte; // The spatially unique node identifier
 +
    );
 +
  end;
 +
 
 +
var
 +
  ExitCode: hresult = 0; export name 'operatingsystem_result';
 +
 
 +
procedure fpc_initializeunits; compilerproc;
 +
 
 +
procedure fpc_do_exit; compilerproc;
 +
 
 +
implementation
 +
 
 +
procedure fpc_initializeunits;
 +
begin
 +
end;
 +
 
 +
procedure fpc_do_exit;
 +
begin
 +
end;
 +
 
 +
end.
 +
</syntaxhighlight>
 +
 
 +
File sysinit.pas:
 +
 
 +
<syntaxhighlight lang="Pascal">
 +
unit sysinit;
 +
{$MODE FPC}
 +
 
 +
interface
 +
 
 +
procedure _FPC_mainCRTStartup; stdcall; public name '_mainCRTStartup';
 +
 
 +
implementation
  
== see also ==
+
procedure _FPC_mainCRTStartup; stdcall;
* FPC RTL {{Doc|package=RTL|unit=system|text=<syntaxhighlight lang="pascal" enclose="none">system</syntaxhighlight> unit reference}}
+
begin
 +
end;
 +
 
 +
end.
 +
</syntaxhighlight>
 +
 
 +
File fpintres.pas:
 +
 
 +
<syntaxhighlight lang="Pascal">
 +
unit FPIntRes;
 +
{$MODE FPC}
 +
 
 +
interface
 +
implementation
 +
end.
 +
</syntaxhighlight>
 +
 
 +
== Comparative remarks ==
 +
Other compilers such as [[GNU Pascal]] or [[Delphi]] use similar constructs, but to varying extent, and the file is not necessarily named "system.pas".
 +
 
 +
== See also ==
 +
* FPC RTL System unit reference
 
* [[System unit structure]], some explanation about the distributed system unit
 
* [[System unit structure]], some explanation about the distributed system unit
 
* [[Minimal RTL]]
 
* [[Minimal RTL]]
 
* [[RTL development articles]]
 
* [[RTL development articles]]
 +
 +
[[Category: Code]]

Latest revision as of 20:25, 7 January 2022

English (en) français (fr)

In Free Pascal the System unit is the unit that is included by every program. FPC's run-time library comes with a System unit where most, if not all of its functionalities work on every available platform.

Also depending on the platform it's not true that no further unit is added, because for example on Windows the SysInit unit is also used, because that contains the entry point for the binary.

Compilation of System unit

For quite some time already FPC automatically applies the -Us switch to the unit named System, so you don't need the -Us switch. The -Us switch tells the compiler that only basic type definitions are being made. Types such as Integer are not yet available.

Mandatory tasks of System unit

If you attempt to create your own system unit, you'll incrementally notice what the compiler needs or what the generated code expects. On a Linux target, the minimal system unit has to contain:

unit system;

interface

The type hresult must be defined, for example:

type
  hresult = LongInt;

The identifier operatingsystem_result has to be declared. The data stored at that position are communicated to the operating system as return value. Whether hresult and operatingsystem_result are defined in the "interface" section is irrelevant. However, it is very plausible to put those into the "interface" section, so programs using this system unit can manipulate those.

var
  ExitCode: hresult = 0; export name 'operatingsystem_result';

implementation

Here comes to light what the system responsibilities are: It has to (or should) initialize units, that means call every unit's Initialization routines. For that the label FPC_INITIALIZEUNITS has to be defined.

procedure InitializeUnits; alias: 'FPC_INITIALIZEUNITS'; 
begin
end;

Also the label FPC_DO_EXIT has to be defined. Here, system unit programmers have the chance to finalize units.

procedure doExit; alias: 'FPC_DO_EXIT';
begin
end;

end.

Furthermore FPC will possibly complain the unit FPIntRes is missing. So create a file fpintres.pas:

unit FPIntRes;
interface
implementation
end.

After doing so, you can compile your system unit (flag -Us is not needed), or directly create an empty program and compile it to examine the impact on the binary's size. On a x86-64 Linux target the executable had a size of 2.7 Kbytes. The big trade-off is no convenience. All functionality that can't be “implemented” by the compiler is gone. For example Addr or Ord still work, while Sin() or Random() are not available. Confer the procedure create_intern_symbols in "compiler/psystem.pas".

Note-icon.png

Note: Depending on what your program uses, there might other mandatory tasks, too. Especially OOP won't work without a functional ObjPas unit.

Initialization and finalization of units

For those who are curious, how to implement FPC_INITIALIZEUNITS: FPC puts into every program's data section a INITFINAL table. Compiling a program with the "-al" flag will retain the Assembler file "*.s". Examining this Assembler file, you will encounter something like:

.section .data.n_INITFINAL
        .balign 8
.globl  INITFINAL
        .type   INITFINAL,@object
INITFINAL:
        .quad   1,0
        .quad   INIT$_$SOMEUNIT
        .quad   FINALIZE$_$SOMEUNIT

For further investigations have a glimpse at an actual implementation: "rtl/inc/system.inc".

Note-icon.png

Note: In general, implementing an “own” system unit is a stupid idea. There is “smart-linking” if size matters.

Other issues with compilation

  • Q: Now FPC wants to compile the ObjPas unit.
  • A: That is because your project's mode is set to ObjFPC (Lazarus will pass a -MObjFPC parameter to the compiler then) which automatically leads to an insertion of the ObjPas unit. Either change your project's mode in Project Settings -> Compiler Settings -> Parsing or add a {$mode fpc} to your main program file.

  • Q: Now FPC cannot find this:
Unknown compilerproc "fpc_initializeunits". Check if you use the correct run time library.
  • A: You need to add a
 procedure fpc_initializeunits; compilerproc;

both in the interface and implementation section.


  • Q: I am stuck, FPC says:
Internal type "TEXCEPTADDR" was not found
  • A: The compiler expects some system defined types to be defined when parsing a subroutine such as the implementation section of System. This means you have to add some type definitions to System. It appears that this check was added to 3.2.0.

Example of System unit

The example is made by user Okoba at forum.

File system.pas:

unit system;
{$MODE FPC}
interface

type
  hresult = LongInt;
  DWord = LongWord;
  Cardinal = LongWord;
  Integer = SmallInt;
  UInt64 = QWord;
  TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat, tkSet,
    tkMethod, tkSString, tkLString, tkAString, tkWString, tkVariant, tkArray,
    tkRecord, tkInterface, tkClass, tkObject, tkWChar, tkBool, tkInt64, tkQWord,
    tkDynArray, tkInterfaceRaw, tkProcVar, tkUString, tkUChar, tkHelper, tkFile,
    tkClassRef, tkPointer);

type
  jmp_buf = packed record
    rbx, rbp, r12, r13, r14, r15, rsp, rip: QWord;
    {$IFDEF win64}
    rsi, rdi: QWord;
    xmm6, xmm7, xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15: record 
      m1, m2: QWord;
    end;

    mxcsr: LongWord;
    fpucw: word;
    padding: word;
    {$ENDIF win64}
  end;

  Pjmp_buf = ^jmp_buf;
  PExceptAddr = ^TExceptAddr;
  TExceptAddr = record 
    buf: Pjmp_buf;
    next: PExceptAddr;
    {$IFDEF CPU16}
    frametype: SmallInt;
    {$ELSE CPU16}
    frametype: LongInt;
    {$ENDIF CPU16}
  end;

  PGuid = ^TGuid;
  TGuid = packed record
    case Integer of
    1:
     (Data1: DWord;
      Data2: word;
      Data3: word;
      Data4: array [0 .. 7] of byte;
    );
    2:
     (D1: DWord;
      D2: word;
      D3: word;
      D4: array [0 .. 7] of byte;
    );
    3:
    ( { uuid fields according to RFC4122 }
      time_low: DWord; // The low field of the timestamp
      time_mid: word; // The middle field of the timestamp
      time_hi_and_version: word;
      // The high field of the timestamp multiplexed with the version number
      clock_seq_hi_and_reserved: byte;
      // The high field of the clock sequence multiplexed with the variant
      clock_seq_low: byte; // The low field of the clock sequence
      node: array [0 .. 5] of byte; // The spatially unique node identifier
    );
  end;

var
  ExitCode: hresult = 0; export name 'operatingsystem_result';

procedure fpc_initializeunits; compilerproc;

procedure fpc_do_exit; compilerproc;

implementation

procedure fpc_initializeunits;
begin
end;

procedure fpc_do_exit;
begin
end;

end.

File sysinit.pas:

unit sysinit;
{$MODE FPC}

interface

procedure _FPC_mainCRTStartup; stdcall; public name '_mainCRTStartup';

implementation

procedure _FPC_mainCRTStartup; stdcall;
begin
end;

end.

File fpintres.pas:

unit FPIntRes;
{$MODE FPC}

interface
implementation
end.

Comparative remarks

Other compilers such as GNU Pascal or Delphi use similar constructs, but to varying extent, and the file is not necessarily named "system.pas".

See also