[Previous]  [Up]  [Next]

Implementation notes

Environment



Various aspects of the shared C library's behaviour on termination conflict with the requirements of standard C++: if a C++ program terminates abnormally (i.e. via abort()) static destructors should not be called; assuming normal termination, calls to atexit() functions should be interleaved with calls to static destructors (ie. an atexit() function registered after a given static contructor has been called should be called before the corresponding static destructor is called). These requirements make it necessary to replace the shared C library's abort() and atexit() functions - in the first case to allow abnormal termination to be flagged to the C++ runtime system, in the second to allow individual static destructors to be registered as atexit() functions (which could easily outrun the SCL limit of 32 registered functions). Link editing has been used to create a new SCL stubs with the SCL implementations of these functions renamed.

Hooks for the initialization and finalization of higher layers libraries is provided. These hooks are implemented using weak links, which, if present in a fully linked binary, will be called before the dynamic initialization phase of startup (i.e. before static constuctors) and on termination (i.e. after static destructors). This has been provided to allow e.g., the the shared C library's allocator to be replaced by an application library (to allow heap allocation from a dynamic area rather than application space). If provided, library hooks must conform to the following declarations:

    extern "C" void rts_lib_init();
    // called before static constructors
    
    extern "C" void rts_lib_final(bool in_exit_handler,
                                  bool abnormal_termination);
    // called after static destructors.
    //
    // in_exit_handler == false      => called as an atexit
    //                                  function.
    //                 == true       => called from application
    //                                  exit handler, so caution
    //                                  required.
    //
    // abnormal_termination == false => application did not exit
    //                                  via abort().
    //                      == true  => application did exit via
    //                                  abort().
    //
    // function will be called twice on termination: first with
    // in_exit_handler false, second with in_exit_handler true

Hooks are also provided for the intialization of the standard streams. These are again implemented via weak links, to avoid building in a dependency on the streams components if they aren't used. This method of initializing the standard streams has the consequence that stream Init objects are unnecessary which has advantages both practical (reduced code size) and theoretical (Init objects can cause virtual memory thrashing on startup and termination).

Registration functions are provided to allow the allocators used by operator new(), for stack chunks be configured at run-time. Typically these calls would be made by a higher library layer from it's implementation of rts_lib_init(). The default allocators used are the shared C library's malloc() and free(). The registration interface is of the form:

    typedef void* (*rts_alloc_fn_t)(size_t);
    typedef void  (*rts_free_fn_t)(void*);
    
    void rts_set_alloc(rts_alloc_fn_t, rts_free_fn_t);
    void rts_set_stack_alloc(rts_alloc_fn_t, rts_free_fn_t);
The standard C library functions calloc() and realloc() do not have C++ library equivalents. If the default allocators are used then the shared C library versions of these functions are used, otherwise calloc() uses the specified allocator and realloc() always returns NULL.

See the header file rts.h for protoypes, and the file test.c++.tstterm for examples.


 [Previous]  [Up]  [Next]