OS aware RTL

From Lazarus wiki
Revision as of 07:51, 10 March 2006 by Almindor (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

The problem

Currently there's a runtime problem with binaries created by FPC. The problem lies in libC/syscall numbers but it spans to userland as I'll explain later.

I'll talk mostly about the Linux RTL since it's the most chaotic OS out there but this really applies to any OS including Windows.

1. Syscall number changes
2. LibC functions missing

It's a known fact that syscall numbers change. This happens on all unices out there more or less frequently. The problem with FPC RTL is that it's smartlinked into the binary. Altho this is also a big advantage, the drawback is that things like syscall numbers are hardcoded. This means one of two things for ABI compatibility:

a: Old syscalls will be used on new OSes if the binary was compiled on old ones
b: Non-existing syscalls will be used on old OSes if the binary was compiled on new ones

There's more: as an example I'll use the newly added epoll* functions in linux.pp. These are used as syscalls if {$FPC_USE_LIBC} is false. This way the binaries will compile even on 2.4 kernel where epoll doesn't exist. The problem comes if someone tries to create a libfprtl.so on these systems (or use one from 2.6) since smartlinking cannot "hide" the missing functions anymore.

The proposal

Currently the only thing used to alleviate this is {$ifdef} and good luck. This won't last forever and if fpc wants to both use latest features and be backwards ABI compatible, something which I call "OS aware binaries" must be created.

Lets say we change the unit which defines syscall numbers on Linux to something like this: (note: I know it's chopped up to include file but let's keep it simple)

unit
  Syscalls;

interface

var
  syscall_nr_epoll_create: Integer; // defined them as variables
  ...

implementation

uses
  Linux, Kernel24, Kernel26;

initialization
  if VersionToInt(LinuxVersion()) > VersionToInt('2.5.66') then begin
    sycall_nr_epoll_create:=...;
  end else begin
    ...
  end;

end.

Another solution which applies to the epoll/libC problem is this:

unit Linux;

interface
...
function epoll_create(size: cint): cint;
...
implementation

uses
  Syscalls, Epoll;

type
  TEpollCreateFunction = function(cint): cint;

var
  epoll_create_func: TEpoll_CreateFunction;

function epoll_create(size: cint): cint;
begin
  epoll_create_func(size);
end;

initialization
  if VersionToInt(LinuxVersion()) > VersionToInt('2.5.66') then
    epoll_create_func:=@Epoll.epoll_create // assign as it's there
  else
    epoll_create_func:=@Epoll.epoll_create_dummy;
end.

And epoll would look something like:

unit Epoll;

interface

function epoll_create(size: cint): cint; {$ifdef FPC_USE_LIBC} cdecl; external name 'epoll_create'; {$endif}

function epoll_create_dummy(size: cint): cint;

implementation

{$ifndef FPC_USE_LIBC}
function epoll_create(size: cint): cint;
begin
  // do syscall stuff
end;
{$endif}

function epoll_create_dummy(size: cint): cint;
begin
  raise TOSMismatchException.Create; // we don't have this one here...
end;

end.

This way even a libfprtl.so could be compiled and linked on any Linux version.

Ofcourse there are cons and pros, and inherited problems in this solution.

The pros:

  • ABI compatible binaries
  • Fixes certain future problems (epoll/FPC_USE_LIBC)
  • It would be just cool since FPC would be the only compiler with proper ABI compat. on linux

The cons:

  • Overhead both memory and CPU (initialization slows down, smartlinking would be useless on these things)
  • Detection of OS version is more than tricky even with standardizef POSIX.1 functions like uname