Appendix A1. Loader Table Structure
KEY
The loader tables are held in a file, T#LOAD, which is created at
subsystem start up time. This file is initially 3 pages long but has the
capability to expand up to a limit of 1Mb or the maximum file size
permitted in the process, whichever is the smaller. After 32 bytes of
header information, the remainder of the first page is used for
references, the second for permanently loaded entries and the third for
temporarily loaded entries. The start address and length of each table
is held in a record array SSLOADTAB.
There are certain common features in the way each table
operates. The first 1008 bytes are used as 252 integers numbered from 0
to 251. These function as listheads from which chains of records are
attached. Which particular listhead a given entry or reference is
chained to is determined by hashing the entry or reference name. The
hash constant for the hashing function is the prime number 251 so that
the result of the hash function is an integer in the range 0 - 250.
Listhead 251 is not used in the reference table but is used in the
entries tables to hold a chain of filenames. This is described at
greater length below.
Within each table, including the listheads, all links and pointer
fields are relative to the start of that table i.e. listhead 0 has
relative address 0. Each table is therefore entirely self contained and
is independent of the actual address of the start of that table. Thus a
table can be moved about as a unit, the only updating required is to the
SSLOADTAB record array. This design makes it easy to expand tables when
circumstances demand.
For example, suppose the permanent entries table, table 2, is full
but we need to add more entries to it - a common requirement during a
load. The procedure is as follows: CHANGEFILESIZE is called to extend
T#LOAD by 1 Epage. Table 3, the table of temporary entries, is moved 1
Epage up the VM and SSLOADTAB(3)_START updated. The page vacated by
table 3, which is at the end of table 2, is filled with a 'not used'
pattern and assigned to table 2 by incrementing SSLOADTAB(2)_LEN and the
load can continue. Each table can expand itself in a similar way as
circumstances dictate until T#LOAD reaches the file size limit of the
process or 1 Mb, whichever is the smaller.
Although there are two tables of entries they should really be
regarded as one since they have exactly the same table structure, the
difference lying in how they are administered. We are therefore
describing two types of table; reference and entry.
An entry or reference within each table is uniquely identified by
its name and its type, e.g. we could have a code entry called TRIAL and
a data entry called TRIAL in the entry tables.
These then are the common features of each type of table but to
proceed farther it is now necessary to look at each type separately.
Reference Table
KEY
Of the two types of table this is the more complex to
operate. References can remain in the tables for long or short periods
e.g. if five files are loaded one after the other, references generated
by the first could be satisfied by entries in any or none of the
following files. Thus while references arrive in the tables in solid
chunks as a file is loaded, they are removed in dribs and drabs as
further files are loaded or explicit searches are undertaken. Space is
released at unpredictable times in unpredictable locations. One of the
main problems in operating the reference table is therefore efficient
space management. Equally several files can refer to the same item so
that occurrences of the same reference can be be widely scattered during
a load. It is not desirable to duplicate information unnecessarily.
Before we can seriously consider the space management problem we
must decide how to store the information that must be held for a
reference. The solution adopted in the loader is that there are two
kinds of records associated with a reference, a basic record of which
there is one per unique entry (ref name+ref type) and an information
record of which there is one per reference occurrence. The basic
records are chained off the listheads while the information records for
a reference are chained off its basic record.
Information Record Structure
An information record consists of 4 integer fields as follows:
DR0 -> This integer is assigned in the following manner:
2**31 -> if set then the reference is a common reference
2**30 -> if set then the reference is a dynamic reference
2**29 -> if set then the reference is an unsatisfied
reference
2**28 - 2**24 -> load level at which the reference was
generated
2**23 - 2**0 -> If this is a data reference then the length
of the data item else 0
DR1 -> The address of the reference in the gla
ADYNR/OFFSET -> If the reference is a dynamic or unresolved code or
data reference then the address of the escape table
else if the reference is a single word reference
then the value of the offset to the address of the
entry point else 0.
LINK -> Offset of the next information record in the chain relative
to the start of the table or -1 if this is the last record
in the chain.
Basic Record Structure
KEY
This consists of a name field double word aligned and 4 integers.
NAME -> name of the reference double word aligned
TYPE -> This integer is assigned as follows:
2**31 -> If set then at least one of the information records
attached to this basic record is a common reference.
2**30 -> if set then at least one of the information records
attached to this basic record is a dynamic reference.
2**29 -> If set then at least one of the information records
attached to this basic record is unsatisfied.
2**28 -> If set then ALL the unsatisfied references on the
information record chain have been made unresolved;
i.e. a complete search for an entry to satisfy this
NAME has failed but LOADPARM(LET) was set. Note
that this bit and the 2**29 bit cannot both be set.
2**27 - 2**8 -> free
2**7 - 2**0 -> Reference type, 1=data, 2=code,
3=single word i.e code!data
FIRST -> Offset relative to the start of the reference table of the
first information record associated with this basic record.
LAST -> Offset relative to the start of the reference table of the
last information record associated with this basic record.
LINK -> Offset of the next basic record chained to this listhead or
-1 if this is the last record in the chain.
The design of the basic record is geared to finding out what we need
to know about the information records during loading without the
overhead of chaining through them. Thus during a cascade load we can
find the next reference that we should search for by inspecting the
basic records for ones which have bit 2**29 set in the TYPE field.
Similarly when adding a new information record we wish to go
directly to the last in the chain rather than follow the chain to the
end. More subtly if a reference is currently unresolved then we don't
want to add a new unsatisfied reference to the table as this would
trigger off a complete search which was bound to fail anyway; we want to
make it unresolved right away.
Table Space Management
KEY
When we want to add a new basic or information record to the
reference table the space management problem boils down to finding a
hole big enough to take the record. The minimum size record that will be
searched for is 16 bytes for information records whereas basic records
will be in the range 24 to 48 bytes long depending on the size of
NAME. For each basic record there could be many information records. The
strategy adopted is as follows.
The most probable task is to find a 16 byte hole so we start by
searching for one of these from the beginning of the table. If we don't
find one then we must extend the reference table. If we do then if we
are actually looking for a 16 byte hole then we have succeeded but to
avoid searching the table from the beginning next time round we store
the offset of this hole +16 in a global integer array element,
NEXTAD(1). We can start searching from there next time. If however we
were looking for a bigger hole we store the offset of the start of the
hole in NEXTAD(1) as a future starting point and carry on looking from
there for the larger hole. If we can't find one then we must extend the
table otherwise we have succeeded. This strategy minimises the
searching necessary to find holes of the correct size.
When we satisfy a reference then we are releasing space. If the
offset of the start of the space released is < NEXTAD(1) then NEXTAD(1)
is reset to this value.
Entry tables
Entries are better behaved than references in that they arrive and
depart at predictable times. All the entry points from an object file
are added to the entry table when that file is loaded and they are all
removed when the file is unloaded. Further, more than one entry with a
given name and type cannot be in the entry table at the same time.
In fact entries are so well behaved that we can regard the entry
table as being like a stack. This has enormous advantages when we come
to unload. Files loaded in a specific order will be unloaded in reverse
order. The one exception to this is that at any time we can ask for a
file to be 'permanently' loaded, either explicitly as in PRELOAD or
implicitly as in a request to load a piece of standard system
software. To keep the advantages of pseudo stack operation we use two
tables, one for permanently loaded entries and one for temporarily
loaded entries.
As implied, we do not have the space management problems with entry
tables that we had with references and all we need to remember is the
current 'top of the stack' for permanent and temporary entries. These
are stored in the global integer array elements NEXTAD(2) and NEXTAD(3)
respectively in the main body of the loader.
When an object file is loaded we add an entry record for each entry
point. An entry record consists of a word aligned name field followed by
four integers as follows:
NAME -> Name of the entry point single word aligned
TYPE -> This integer is assigned as follows:
2**31 - If set then NAME is a main entry point
2**30 - If set then this is a pseudo data entry created by
DATASPACE
2**29 - If set then NAME is an alias of some other entry
point which is already loaded i.e. created by
ALIASENTRY
2**28 - 2**8 Free
2**7 - 2**0 The entry type; 1=data, 2=code
(4=macro - not fully implemented as yet)
DR0 -> If NAME is a code entry then an appropriate 2900 descriptor
else if NAME is a data entry then the length of the data
area in BYTES.
DR1 -> Address of the entry point
LINK -> Offset of the next entry record chained to this listhead
or -1 if the record is the chain terminator.
However, as well as the entry points from an object file it is also
desirable to store other relevant information at the same time. For
instance, it may be necessary to create a T#CODE file or a T#GLA file or
both. These must be destroyed when the original object file is
unloaded. Equally it turns out to be extremely advantageous in
diagnosing problems with mismatching data reference/data entry lengths
to store the range of gla (and initialised stack, if any) regardless of
whether a T#GLA file has been created or not. To achieve these aims,
then, after adding the entry points, we add a series of filename records
- one for the object file itself and one for each of the other useful
pieces of information that we need.
In the worst case, therefore, there could be 4 filename records
associated with an object file; the object filename, a record describing
the gla used by this object file (which would have a T#GLA filename in
its NAME field if one had been created or +GL if it hadn't), a record
describing its range of initialised stack (which would have +IS in its
NAME field) and a T#CODE record.
It is not sensible to hash these filename records, instead they are
all chained off the extra listhead, listhead 251. This is convenient for
providing information to the user and vital in finding out quickly which
files to unload at unload time.
The information we want to store in a filename record depends on the
type of file. The loader recognises four different types - object files,
T#CODE files, T#GLA files and character or store mapped files used by
DATASPACE. Filename records are the same structure as other entry
records; a word aligned name string followed by four integers. We deal
with each type in turn.
Object filename records
KEY
NAME -> Full EMAS 2900 filename
TYPE -> 0
MAINEP -> If the object file has one or more main entry points then
the address of the first main entry point encountered else 0
DUM1 -> 0 (not used)
LINK -> Offset of the next filename record or -1 if the last in the
chain.
T#CODE filename records
KEY
NAME -> EMAS 2900 filename i.e. no username
TYPE -> 0
DUM2 -> 0 (not used)
DUM3 -> 0 (not used)
LINK -> Offset of next filename record or -1 if the last in the chain
T#GLA/gla/initialised stack filename records
KEY
NAME -> EMAS 2900 filename i.e. no username/+GL/+IS
TYPE -> 0
GLAFROM -> Address of first byte of gla/gla/initialised stack
in T#GLA/T#UGLA or T#BGLA/T#USTK
GLATO -> Address of last byte of gla/gla/initialised stack
in T#GLA/T#UGLA or T#BGLA/T#USTK
LINK -> Offset of next filename record or -1 if the last in the chain.
DATASPACE filename records
KEY
NAME -> Full EMAS 2900 filename
TYPE -> X'40000000'
USECOUNT -> Number of separate DATASPACE entry points defined in
NAME
ACCESSMODE -> Connect mode of NAME, i.e. 1=R, 3=W, 11=WS
LINK -> Offset of next filename record or -1 if the last in the chain.