ARM C Library Porting Guide
===========================


Introduction
------------

The retargetable ARM C library conforms to the ANSI C library specification. 
Example code is included which targets the library:

 1. at the common operating environment supported by the ARM emulator 
    (ARMulator), the ARM Evaluation and Development boards

 2. at Acorn's proprietary RISC OS operating system (RISC OS is a cooperative 
    multi-tasking system, but the targetting is at a level such that its 
    multi-tasking nature does not obtrude).


Source Organisation
-------------------

The supplied source structure holds the following directories:


stdh

Contains the ANSI header files (which should require no change in 
retargetting). These files are also built into <armcc>.


util

Contains the source of the <makemake> utility, written in classic C.


semi

Contains targetting code for the <semi-hosted> C Library, which targets the 
debug monitor supported by:

 *  the ARMulator;

 *  ARM  Platform Independent Evaluation (PIE) card for the ARM60;

together with SunOS-hosted make definitions and library build options.

(The library is called <semi-hosted> because many functions such as file I/O 
are implemented on the host computer, via the host's C library).

In principle, a targetting of the library requires both a target directory and 
a host directory; however, where there is only one hosting, it is convenient to 
amalgamate the two directories.


riscos

Contains targetting code for Acorn's RISC OS operating system for its ARM-based 
Archimedes computers, together with SunOS-hosted make information.


fpe340

Contains object code of the Floating Point Emulator (for which source code is 
not provided).

and at the top level:


*.c, *.h, *.s

Contain target-independent source code.

The target-independent code is mostly grouped into one file per section of the 
ANSI library (though with exceptions: <stdlib> is implemented partly in alloc.c 
and partly in stdlib.c), with use of conditional compilation or assembly to 
enable construction of a fine-grain library (approximately one object file per 
function). The ARMulator-targetted code is grouped similarly.

The first stage in constructing a makefile for a targetting of the library uses 
the utility program <makemake>. This allows description of library variants in 
a host-independent manner and permits building the library on a host which 
severely limits the number of files in a directory.

The <makemake> utility takes as input the files <makedefs> from the host 
directory and <sources> and <options> from the target directory (see below for 
a description of their content), and produces as output a makefile called 
<Makefile >in the host directory (often, the host directory and the target 
directory will be the same).

The arguments to <makemake> are the name of the host directory and, if 
distinct, the name of the target directory.

In order to retarget the library, at least the following files must be 
provided:


makedefs (in the host directory)

Host-dependent definitions of tools, paths, options etc. to include in the 
constructed Makefile for the library. Use the file <makedefs> from the <semi> 
directory as a template.


options (in the target directory)

Library variant selection (a number of lines, each of the form option_name = 
value). See "<Variant selection>". Use the file <options> 
from the <semi> directory as a template.


sources (in the target directory)

List of objects to include in the target library, and sources from which they 
are to be constructed. Each line (other than those controlling variant 
selection) has one of the forms:

 *  object_name source_name

 *  object_name source_name [compiler_options]

Where object_name lacks the .o extension.  Variant selection involves lines of 
the form:

 *  #if <expression>

 *  #elif <expression>

 *  #else

 *  #end

with the obvious significance. Expression primaries are option_name = value and 
option_name != value, and expression operators are && and || (of equal 
precedence, note). Use the file <sources> from the <semi> subdirectory as a 
template, modifying it as needed.


hostsys.h (in the target directory)

Defines the functions which must be supplied for a full re-targetting of the 
library, and also defines certain target-dependent values required by 
target-independent code. Use the file <hostsys.h> from the <semi> subdirectory 
as a template, changing the values in it appropriately (see "<Retargetting the 
Library>" and <"Details of Target-Dependent Code>" starting 
on page53).


config.h (in the target directory)

Contains the hardware description. The version of this file in the <semi> 
directory will suffice for a little-endian ARM with mixed-endian doubles; a 
big-endian ARM needs BYTESEX_ODD defined (and BYTESEX_EVEN not). Truly 
little-endian floating-point values are not supported by the floating-point 
emulator.

Of course, the files containing the target-specific implementation code are 
also provided in the target directory.


Building a Target-Specific Library
----------------------------------

When the target-dependent files have been provided, construction of a library 
proceeds as follows:

    cd util
    cc -o makemake makemake.c

(Since makemake is written portably in 'classic' C it should just compile and 
go. The options to C compilers vary, but most support this way of making an 
executable program called <makemake> from the source <makemake.c>)

    cd ..
    util\makemake targetdir [hostdir]

(<hostdir> is needed only if it is different from <targetdir>) (under DOS, use 
<util/makemake> ...)

    cd hostdir
    make depend

(this augments <Makefile>; as a side-effect it also makes the assembler-sourced 
objects.)

    make

(this makes <armlib.o> ... if everything succeeds).


Retargetting the Library
------------------------


Variant selection
.................

The following generic variants are available as 'tick box' options through the 
<options> file in the target directory:


fp_type

    =linked     causes the object module containing the floating point emulator 
                to be included in the library (and linked into any image), 
                along with a small interface module to take control of the 
                illegal instruction vector on startup, and relinquish it on 
                closedown.

    =module     floating point emulation is provided externally in some way 
                (present in ROM, for example). In this case, if the 
                target-dependent kernel follows the code of the riscos example, 
                functions __fp_initialise, __fp_finalise and 
                __fp_address_in_module must be provided (see "<Floating-Point 
                Support>").


memcpy

    =small      memcpy, memmove and memset are implemented by generic C code 
                (which attempts to do as much as possible in word units): each 
                occupies about 100 bytes.

    =fast       memmove and memcpy are implemented together in assembler, which 
                attempts to do the bulk of the move 8 words at a time using 
                LDM/STM (about 1200 bytes). memset is implemented similarly 
                (about 200 bytes).


divide

    =small      the fully rolled implementations.

    =unrolled   unsigned and signed divide are unrolled 8 times for greater 
                speed, but obviously use more code.
                Complete unrolling of divide is possible, but should be done 
                with care since the significant size increase might give 
                decreased rather than increased performance on a cached ARM. 
                Whichever variant is selected, fast unsigned and signed divide 
                by 10 are included.


stack= (see later subsection <Address Space Model>).


stdfile_redirection

    =on         _main extracts Unix-style stdstream connection directives from 
                the image's argument string (< , >, >>, >&, 1>&2).


backtrace

    =on         the default signal handler ends by producing a call-stack 
                traceback to stderr. (Use of this variant is not encouraged, 
                since it increases the proportion of the library that is linked 
                into all images, while providing functionality better obtained 
                from a separate debugger).


Basic Choices
.............

After the tick box choices have been made, basic choices then have to be made 
about the address-space model and the I/O model the library will follow.


Address Space Model
...................

Two address space models are supported: contiguous stack and chunked stack.

Choosing <stack> = <contiguous> gives:

       +----------------+    <--- top of memory (high address)
       | Stack space    |
       |................|    <--- stack pointer (sp)
       | Free stack     |
       |................|    <--- stack limit pointer (sl)
       +----------------+    <--- stack low-water mark (sl - StackSlop)
       |                |
       | Unused memory  |
       |                |
       +----------------+    <--- top of heap (HeapTop)
       |                |
       | Heap space     |
       |                |
       +----------------+    <--- top of application (Image$$RW$$Limit)
       | Static data    |  }
       |................|  } the application's memory image
       | Code           |  }
       +----------------+    <--- application load address


Choosing <stack> = <chunked> gives:

       +----------------+    <--- initial top of memory (HeapLimit)
       |                |    (may be raised - see __osdep_heapsuppt_extend)
       | Unused memory  |
       |                |
       +----------------+    <--- top of heap (HeapTop)
       |                |
       | Heap space     |    chained stack chunks within heap.
       |                |
       +----------------+    <--- top of application (Image$$RW$$Limit)
       | Static data    |  }
       |................|  } the application's memory image
       | Code           |  }
       +----------------+    <--- application load address


A third variant, like the first, but with the stack outside of the heap and not 
under the application's control, can easily be synthesised. This may be a more 
appropriate variant if there is a skeletal operating system which implements an 
address-mapped stack segment.


I/O model
.........

The library, as supplied, only conveniently handles byte-stream files, (which 
is not to say that other file types can not be handled in the target-dependent 
IO support level, but such support may well be complicated; block stream files, 
for example, are simple to support in the absence of user-supplied buffers).


Details of Target-Dependent Code
--------------------------------


ANSI library functions
......................

The following ANSI standard functions necessarily have an implementation 
completely dependent on the target operating system. No functions are used 
internally by the library (so if any are unimplemented, only clients which 
directly call the functions will fail).

    clock_t clock(void)

(The compiler is expected to predefine __CLK_TCK if the units of clock_t differ 
from the default of centiseconds. If this is not done, time.h must be adjusted 
to define appropriate values for CLK_TCK and CLOCKS_PER_SEC).

    void _clock_init(void) (declared weak)

Clock_init() (if you provide it) is called from the library's initialisation 
code, (clock() needs initialising if a read-only timer is all it has to work 
with).

    time_t time(time_t *timer)
    int remove(const char *pathname)
    int rename(const char *old, const char *new)
    int system(const char *string)
    char *getenv(const char *name)
    void getenv_init(void) (declared weak)

Getenv_init() is called from the library's initialisation code if you provide 
an implementation of it.


I/O Support
...........

If any I/O function is to be used hostsys.h must define the type FILEHANDLE, 
values of which identify an open file to the host system. There must be at 
least one distinguished value of this type, defined by the macro NONHANDLE, 
used to distinguish a failed call to _sys_open.

For an unaltered __rt_lib_init, the macro TTYFILENAME must be defined to a 
string to be used in opening a file to terminal.

The macro HOSTOS_NEEDSENSURE should be defined if the host OS requires an 
ensure operation to flush OS file buffers to disc if an OS write is followed by 
an OS read which requires a seek, (the flush happens before the seek). The RISC 
OS targetting needs this macro to be defined, thanks to an OS file-buffering 
bug; it is unlikely to be wanted in any retargetting.

    FILEHANDLE _sys_open(const char *name, int openmode)

<Openmode> is a bitmap, whose bits mostly correspond directly to the ANSI mode 
specification: for details, see <hostsys.h> in "<Source Organisation>" starting 
on page48 (target-dependent extensions are possible in which case freopen() 
must be extended too). The function _sys_open() is needed by fopen() and 
freopen(), which in turn are required if any I/O function is to be used.

    int _sys_iserror(int status)

A _sys_iserror() function, or a _sys_iserror() macro, is required if any of the 
following int-returning functions is provided (to determine whether the return 
value indicates an error).

    int _sys_close(FILEHANDLE fh)

The return value is 0 or an error indication. It must be defined if any I/O 
function is to be used.

    int _sys_write(
          FILEHANDLE fh, const unsigned char *buf, unsigned len, int mode)

The mode argument is a bitmap describing the state of the FILE connected to fh. 
(See the _IOxxx constants in ioguts.h for the its meaning: only a few of these 
bits are expected to be needed by _sys_write). The return value is the number 
of characters NOT written (i.e. non-0 denotes a failure of some sort), or an 
error indicator. This function must be defined if any output function or 
sprintf variant is to be used.

    int _sys_read(FILEHANDLE fh, unsigned char *buf, unsigned len, int mode)

The mode argument is a bitmap describing the state of the FILE connected to fh, 
as for _sys_write. The return value is the number of characters NOT read (i.e. 
len - result <were> read), or an error indication, or an EOF indicator. The 
target-independent code is capable of handling either early EOF (the last read 
from a file returns some characters plus an EOF indicator), or late EOF (the 
last read returns just EOF). The EOF indication involves the setting of 
0x80000000 in the normal result. The function _sys_read() must be defined if 
any input function or sscanf variant is to be used.

    int _sys_seek(FILEHANDLE fh, long pos)

This function positions the file pointer at offset <pos> from the beginning of 
the file. The result is >= 0 if OK, negative for an error. The function must be 
defined if any input or output function is to be used.

    int _sys_ensure(FILEHANDLE fh);

A call to _sys_ensure() flushes any buffers associated with fh, and ensures 
that the file is up to date on the backing store medium. The result is >= 0 if 
OK, negative for an error. This function is only required if you define 
HOSTOS_NEEDSENSURE (see above).

    long _sys_flen(FILEHANDLE fh);

The above function returns the current length of the file fh (or a negative 
error indicator). It is needed in order to convert fseek(, SEEK_END) into (, 
SEEK_SET) as required by _sys_seek. It must be defined if fseek() is to be 
used. (Note: it is possible to adopt a different model here if the underlying 
system directly supports seeking relative to the end of a file. In this case, 
_sys_flen() can be eliminated.)

    void _ttywrch(int ch)

This function writes a character, notionally to the console. Used (in the 
host-independent part of the library) in the last-ditch error reporter, when 
writing to stderr is believed to have failed or to be unsafe (e.g. in default 
SIGSTK handler). This function must be defined.

    int _sys_istty(FILE *)

This function returns non-0 if the argument file is connected to a terminal. It 
is used to provide default unbuffered behaviour (in the absence of a call to 
set(v)buf), and to disallow seeking. It must be defined if any output function 
(including sprintf variants) or fseek is to be used.

    void _sys_tmpnam(char *name, int fileno);

This function returns the name for temporary file number <fileno> in the buffer 
<name>. It must be defined if tmpnam() or tmpfil() are to be used.


Floating-Point Support
......................

    int __fp_initialise(void)
    void __fp_finalise(void)

If the variant fp_type == module is selected, and the target-dependent library 
kernel follows the pattern of the RISC OS example, these two functions must be 
supplied (though they need not do anything). The function __fp_initialise() 
returns 1 if floating-point instructions are available, otherwise 0.

    bool __fp_address_in_module(void *)

If the variant fp_type == module is selected and the supplied abort handlers 
are used, then the above function must be provided. It is intended to return 1 
if the argument address falls within the code of the fp emulator, (to allow the 
abort handler to describe what is really an abort on a floating-point load or 
store as such, rather than somewhere within the emulator's code).


Kernel
......

The Kernel handles the entry to, and exit from, an application linked with the 
library. It also exports some variables for use by other parts of the library. 
Details of what the kernel must do are strongly dependent on details of the 
target environment. It is suggested that the ARMulator version of this file 
(kernel.s in the <semi >directory) be used as a prototype. The following are 
the main interfaces to the kernel:

    __main()

The entry point to the application. Must perform low-level library 
initialisation, then call _main. (What initialisation needs to be done is 
target environment dependent: it may include heap, stack, fp support, calling 
various osdep_xxx_init() functions if they exist). __rt_lib_init must be called 
to initialise the body of the library.

    void __rt_exit(int);

This function finalises the library (including calling atexit() handlers), then 
returns to the OS with its argument as a completion code. It must be provided.

    char *__rt_command_string(void);

This function returns the address of (maybe a copy of) the string used to 
invoke the program. It must be provided.

    void __rt_trap(__rt_error *, __rt_registers *);

__rt_trap() handles a fault (processor detected trap, enabled fp exception, or 
the like). The argument register set describes the processor state at the time 
of the fault, with the pc value addressing the faulting instruction (except 
perhaps in the case of imprecise floating-point exceptions). This function must 
be provided. The implementation in the ARMulator kernel will usually be 
adequate.

    unsigned __rt_alloc(unsigned minwords, void **block);

__rt_alloc() is the low-level memory allocator underlying malloc(). (malloc() 
allocates only memory between HeapBase and HeapTop; a call to __rt_alloc() 
attempts to move HeapTop: cf Unix sbrk()). __rt_alloc should try to allocate a 
block of sensible size >= minwords. If this is not available, and if 
__osdep_heapsupport_extend is defined, it should call that to attempt to move 
HeapLimit. Otherwise (or if the call fails) it should allocate the largest 
possible block of sensible size. The return value is the size of block 
allocated, and *block is set to point to the start of the allocated block (the 
return may be 0 if no sensibly sized block can be allocated). Allocations are 
rounded up to a suitable size to avoid an excessive number of calls to 
__rt_alloc.

    void *(*__rt_malloc)(size_t)

This is a function pointer, which the kernel should initialise to some 
primitive memory allocation function. The library itself contains no calls to 
malloc(), (other than those from functions of the malloc family, such as 
calloc()), instead the function pointed to by __rt_malloc is called. 
__rt_malloc is set to malloc during initialisation (if malloc is linked into 
the image). The use of __rt_malloc ensures that allocations made before malloc 
is initialised succeed, and prevents malloc from being necessarily linked into 
an image, even when unused.

    extern void (*__rt_free)(void *);

This is a function pointer, which the kernel should initialise to some 
primitive memory-freeing function. (see __rt_malloc above).


Miscellaneous
.............

    void __osdep_traphandlers_init(void)

This arranges to catch processor aborts (and pass them to __rt_trap).

    void __osdep_traphandlers_finalise(void)

This removes the processor abort handlers installed by ..._init().

    void __osdep_heapsupport_init(HeapDescriptor *)

This function must be provided, but may be null.

    void __osdep_heapsupport_finalise(void)

This function must be provided, but may be null.

    { int, void *} __osdep_heapsupport_extend(int size, HeapDescriptor *)

This function requests extension of the heap by at least size bytes. The return 
values are number of bytes acquired, and base address of the new acquisition. 
This function must be provided, (but a null version just returning 0 will 
suffice if heap extension is not needed).

    char *_hostos_error_string(int no, char *buf);

This function is called to return a string describing an error outside the set 
ERRxxx defined in errno.h. (It may generate the message into the supplied buf 
if it needs to do so). It must be defined if perror() or strerror() is to be 
used.

    char *_hostos_signal_string(int no)

This function is called to return a string describing a signal whose number is 
outside the set SIGxxx defined in signal.h.

 

