System Abstraction Layer

The System Abstraction Layer (SAL) contains all the modules that contain code that is platform specific and necessary to run LibreOffice. There are really a number of modules that fall into this layer - the Runtime Library (RTL) and the Operating System Layer (OSL) are wholly included in the SAL, whilst the Visual Components Library has parts that fall into the SAL. However, it also includes a grabbag of other classes and code that don't easily fit into these modules, and includes a lightweight debug logging framework, a macro to allow for a cross-platform program entry point, some floating point support routines and macros, a cross platform type system and a number of macros that provide a way in which to support a variety of compilers, including ones that only really support C++03.

The header files for the SAL module are distributed amongst the following directories:

The headers in the include/sal directory handle a number of pieces of functionality, described here.

Program entry point

The main() entry point into LibreOffice is located in main.h, and is designed as a bunch of macros. As LibreOffice is a cross-platform application that runs on both Unix-based and Windows operaing systems, it must have a flexible way of starting up the program. It does this by using the C preprocessor.

The macros SAL_MAIN_WITH_ARGS_IMPL and SAL_MAIN_IMPL both define the main() function of LibreOffice. The difference, as the name suggests, is that one takes arguments from the command line, and the other does not. We shall focus on SAL_MAIN_WITH_ARGS_IMPLas they are both exactly the same except for one function call. The macro is defined as:

#define SAL_MAIN_WITH_ARGS_IMPL \
int SAL_DLLPUBLIC_EXPORT SAL_CALL main(int argc, char ** argv) \
{ \
int ret; \
sal_detail_initialize(argc, argv); \
ret = sal_main_with_args(argc, argv); \
sal_detail_deinitialize(); \
return ret; \
}

SAL_MAIN_IMPL is exactly the same, only it calls on sal_main() instead of sal_main_with_args(). These macros do the following:

  1. Initializes LibreOffice through sal_detail_initialize(). This init function ensures that OS X closes all its file descriptors because non-sandboxed versions of LibreOffice can restart themselves (normally when updating extensions), but not close all their descriptors. It initializes the global timer, and on systems that have syslog sets this up for logging. It then prepares the command line arguments.

  2. Runs sal_main_with_args() which runs the main LibreOffice program logic

  3. When sal_main_with_args() ends, it calls on sal_detail_deinitialize()

  4. Returns an exit code and closes the application

This works across Windows and other platforms because the implementation macro calls SAL_MAIN_WITH_ARGS_IMPL and SAL_MAIN_IMPL ensure that WinMain() is defined on Windows systems, and expands to nothing on non-Windows systems. WinMain() on Windows systems is the default entry-point of the Windows C Runtime Library (Windows CRT) - LibreOffice does all the heavy lifting in sal_main(), which you define using the SAL_MAIN_WITH_ARGS_IMPL and SAL_MAIN_IMPL macros like this:

#include <sal/main.h>
SAL_IMPLEMENT_MAIN()
{
DoSomething();
return 0;
}
SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
{
DoSomethingWithArgs(argc, argv);
return 0;
}

macro.h

A number of primitive macros are defined:

Name

Description

SAL_N_ELEMENTS

Gets the number of elements in an array

SAL_BOUND

Checks to see if the value is between two other values

SAL_ABS

Gets the absolute value of the number

SAL_STRINGIFY

Takes a token and turns it into an escaped string

Type system

The include/sal/types.h header contains a number of macros, typedefs and namespace aliases that allow LibreOffice to be cross-platform - and even build under different compilers. The compilers supported are:

  • gcc

  • clang

  • MinGW

  • Microsoft Visual C/C++

A particularly useful shortcut was added by Michael Meeks in v4.0 - it aliases the namespace com.sun.star to css. You will find yourself using this frequently as you interact with UNO and the API.

A number of types are defined for portability reasons:

Name

Equivalent C type

Size (bytes)

Format specifier

sal_Bool

unsigned char

1

%c or %hhu

sal_Int8

signed char

1

%c or %hhi

sal_uInt8

unsigned char

1

%c or %hhu

sal_Int16

signed short

2

%hi

sal_uInt16

unsigned short

2

%hu

sal_Int32

signed long signed int ††

4

SAL_PRIdINT32

sal_uInt32

unsigned long unsigned int ††

4

SAL_PRIuUINT32

sal_Int64

__int64 (Windows)

8

SAL_PRIdINT64

sal_Int64

signed long int signed long long (GNU C) †††

SAL_CONST_INT64

sal_uInt64

unsigned __int64 (Windows) unsigned long int unsigned long long (GNU C) †††

8

SAL_PRIuUNIT64

sal_Unicode

wchar_t (Windows) †††† sal_uInt16 (non-Windows) †††††

2

Depends on platform...

sal_Handle

void *

size of pointer

n/a

sal_Size

sal_uInt32 sal_uInt64

native width

SAL_PRI_SIZET

sal_sSize

sal_Int32 sal_Int64

native width

SAL_PRI_SIZET

sal_PtrDiff

result of pointer subtraction

native width

SAL_PRI_PTRDIFFT

sal_IntPtr

native width of integers

size of pointer

SAL_PRIdINTPTR

sal_uIntPtr

native width of integers

size of pointer

SAL_PRIuUINTPTR

sal_Bool is deprecated in favour of bool, however it is still used in the UNO API so cannot be completely removed. All code other than the API should use bool

†† on 32-bit architectures int is 4 bytes wide, but on 64-bit architectures a long is 4 bytes wide (a long is also called a long int)

††† on 32-bit architectures, a long int is 8 bytes wide, but as a long int is 4 bytes wide on a 64-bit architecture, a long long is needed for 8 byte wide longs

†††† on Windows, wchar_t is a typedef to unsigned int, however MinGW has a native wchar_t which is the reason for this

††††† in internal code, sal_Unicode points to char16_t

There are a few types that are now deprecated:

Name

Equivalent C type

sal_Char

char

sal_sChar

signed char

sal_uChar

unsigned char

A number of macros have also been defined to get the maximum values of int types. The macros have the form SAL_MIN_[U]INT*<bit-width>* and SAL_MAX_[U]INT*<bit-width>*. The macros assume that the sal_Int\* types use two's complement to represent the numbers.

There are also a number of function attributes macros that have been defined, in order to be cross platform and utilize compiler features when they are available:

Name

Function attribute

Compiler

SAL_DLLPUBLIC_EXPORT

__declspec(dllexport)

Microsoft C

SAL_DLLPUBLIC_EXPORT

__attribute__((visibility("hidden")))__attribute__((visibility("default"))) ††

GNU C Clang

SAL_JNI_EXPORT

__declspec(dllexport)

Microsoft C

SAL_JNI_EXPORT

__attribute__((visibility("default")))

GNU C Clang

SAL_DLLPUBLIC_IMPORT

__declspec(dllimport)

Microsoft C

SAL_DLLPUBLIC_IMPORT

__attribute__((visibility("hidden")))__attribute__((visibility("default"))) ††

GNU C Clang

SAL_DLLPRIVATE

__attribute__((visibility("hidden")))

GNU C Clang

SAL_DLLPUBLIC_TEMPLATE

__attribute__((visibility("hidden")))__attribute__((visibility("default"))) ††

GNU C Clang

SAL_DLLPUBLIC_RTTI

__attribute__((type_visibility("default")))

Clang

SAL_DLLPUBLIC_RTTI

__attribute__((visibility("default)))

GNU C

SAL_CALL

__cdecl

Microsoft C

SAL_CALL_ELLIPSE

__cdecl

Microsoft C

SAL_WARN_UNUSED

__attribute__((warn_unused_result))

GNU C >= 4.1 Clang

SAL_NO_VTABLE

__declspec(novtable)

Microsoft C

† if dynamic library loading is disabled

†† if dynamic library loading is enabled

Function attributes for exception handling on GCC (but not MinGW) are:

Name

Function attribute

SAL_EXCEPTION_DLLPUBLIC_EXPORT

__attribute__((visibility("default")))SAL_DLLPUBLIC_EXPORT ††

† if dynamic library loading is disabled

†† if dynamic library loading is enabled

alloca()

The alloca() function allocates (as its name suggests) temporary memory in the calling functions stack frame. As it is in the stack frame and not in the heap, it automatically gets freed when the function returns. However, it is a "dangerous" function in that if you allocate too much to the stack you can actually run out of stack space and your program will crash.

The alloca() function, however, resides in a variety of locations on different operating systems - on Linux and Solaris, the function is stored in alloca.h; in OS X, BSD and iOS systems it is in sys/types.h and on Windows it is in malloc.h. Due to this quirk, LibreOffice defines its own alloca.h in include/sal/alloca.h

Note: alloca() is considered dangerous because it returns a pointer to the beginning of the space that it allocates when it is called. If you pass this void* pointer to the calling function you may cause a stack overflow - in which case the behaviour is undefined. On Linux, there is also no indication if the stack frame cannot be extended.

Seeing it in action

I have written some programs you can take from the branch private/tbsdy/workbench. To get them, do the following:

git checkout private/tbsdy/workbench

From the core directory, you can run each of the programs via bin/run <programname>.

Program

Description

Source files

How to invoke

salmain

Cross platform main()

sal/workben/salmain.cxx

bin/run salmain

salmainargs

Cross platform main() with arguments

sal/workben/salmainargs.cxx

bin/run salmainargs

config

Shows platform specific quirks

sal/workben/config.cxx

bin/run config

macro

A variety of useful sal macros

sal/workben/macro.cxx

bin/run macro

alloca

Show how alloca() function works

sal/workben/alloca.cxx

bin/run alloca