MULTICS DESIGN DOCUMENT                                   MDD-020


To:       MDD Distribution

From:     Melanie Weaver

Date:     March 12, 1987

Subject:  The Multics Runtime Environment


Abstract:

Explanation  of   the  runtime  environment,   including  process
structure  and initialization,  ring crossing  mechanisms, object
format  and  dynamic  linking,  area  management,  and  condition
signalling and handling.


Revisions:

         REVISION  DATE           AUTHOR
         initial   March 12, 1987 Melanie Weaver

























_________________________________________________________________

Multics Design Documents are  the official design descriptions of
the Multics Trusted Computing Base.  They are internal documents,
which may be released outside  of Multics System Development only
with the approval of the Director.


                                i
MDD-020                                       Runtime Environment









                             CONTENTS


                                                         Page

                 1:  Overview . . . . . . . . . . . . .   1
                 1.1:  Overview of Process Creation . .   1
                 1.2:  Overview of Process Directory
                  Contents  . . . . . . . . . . . . . .   1
                 1.3:  Overview of Stack Management . .   2
                 1.4:  Overview of Ring Crossing  . . .   2
                 1.5:  Overview of the Dynamic Linker .   3
                 1.6:  Overview of Area Management  . .   4
                 1.7:  Overview of Condition Signalling
                  and Handling  . . . . . . . . . . . .   4
                 1.8:  Overview of Segment and Process
                  Termination . . . . . . . . . . . . .   5
                 1.9:  Overview of the Ring 0
                  Environment . . . . . . . . . . . . .   5
                 2:  Process Directory Contents . . . .   6
                 2.1:  Ring 0 Process Segments  . . . .   6
                 2.1.1:  Descriptor Segment . . . . . .   6
                 2.1.2:  Process Initialization Table .   6
                 2.1.3:  The Process Data Segment . . .   7
                 2.1.4:  The Known Segment Table  . . .   7
                 2.2:  Per-Ring Process Segments  . . .   7
                 2.2.1:  The Stack  . . . . . . . . . .   8
                 2.2.1.1:  The Stack Header . . . . . .   8
                 2.2.1.2:  The Stack Frames . . . . . .   8
                 2.2.1.3:  Manipulating the Stack . . .   8
                 2.2.1.3.1:  Normal Call  . . . . . . .   9
                 2.2.1.3.2:  Push . . . . . . . . . . .   9
                 2.2.1.3.3:  Normal Return  . . . . . .   9
                 2.2.1.3.4:  Stack Truncation . . . . .   9
                 2.2.1.3.5:  Use of Operators . . . . .   10
                 2.2.1.3.6:  Abnormal Returns . . . . .   10
                 2.2.1.4:  Other Stack Segments . . . .   11
                 2.2.2:  The User Free Area . . . . . .   12
                 2.2.3:  Temporary Segments . . . . . .   12
                 3:  Process Creation . . . . . . . . .   14
                 3.1:  Preparation by the Creating
                  Process . . . . . . . . . . . . . . .   14
                 3.2:  Preparation by the New Process .   15
                 3.2.1:  Initializing the Environment .   15
                 3.2.2:  Finding the Initial Procedure    16


                                ii
Runtime Environment                                       MDD-020


                         CONTENTS (cont)


                                                         Page

                 3.2.3:  Completing the User Ring
                  Initialization  . . . . . . . . . . .   18
                 3.2.4:  Initializer Differences  . . .   18
                 4:  Ring Crossing Mechanisms . . . . .   19
                 4.1:  Gates  . . . . . . . . . . . . .   19
                 4.1.1:  What Gates Are . . . . . . . .   19
                 4.1.2:  Non-Hardcore Gate Actions  . .   20
                 4.1.3:  Hardcore Gate Actions  . . . .   21
                 4.1.3.1:  bad_dir Condition Handler  .   21
                 4.1.3.2:  Metering . . . . . . . . . .   22
                 4.1.3.3:  Checking for Ring Alarms . .   22
                 4.1.3.4:  Obtaining the Ring Zero
                  Linkage Pointer . . . . . . . . . . .   22
                 4.1.3.5:  Fast Hardcore Gates  . . . .   22
                 4.1.4:  Gate Actors  . . . . . . . . .   23
                 4.1.5:  Gate Macros  . . . . . . . . .   23
                 4.2:  Validation Level . . . . . . . .   24
                 4.3:  The Ring Alarm Mechanism . . . .   27
                 4.4:  Brief Summary of Fault Handling    27
                 4.5:  Exiting a Lower Ring . . . . . .   28
                 4.5.1:  Normal Returns . . . . . . . .   28
                 4.5.2:  Condition Crawlouts  . . . . .   28
                 4.5.3:  Transfers to Labels in Higher
                  Rings . . . . . . . . . . . . . . . .   29
                 4.5.4:  Outward Calls  . . . . . . . .   29
                 4.5.4.1:  outward_call_handler . . . .   30
                 4.5.4.2:  call_outer_ring_ . . . . . .   31
                 4.5.4.3:  User Coded Procedures  . . .   31
                 5:  Linking  . . . . . . . . . . . . .   33
                 5.1:  What Dynamic Linking Is  . . . .   33
                 5.2:  The Effect of Dynamic Linking on
                  Process Structure . . . . . . . . . .   34
                 5.2.1:  Linking and the Object Segment
                  Format  . . . . . . . . . . . . . . .   34
                 5.2.1.1:  Linkage Section  . . . . . .   34
                 5.2.1.2:  Definition Section . . . . .   35
                 5.2.1.3:  Unsnapped Links  . . . . . .   35
                 5.2.1.4:  Obsolete Feature of
                  Definitions in the Linkage Section  .   35
                 5.2.1.5:  Historical Note on Separate
                  Static  . . . . . . . . . . . . . . .   36
                 5.2.2:  The Linkage and Static Offset
                  Tables  . . . . . . . . . . . . . . .   37
                 5.2.3:  Name Space Issues  . . . . . .   39
                 5.2.4:  External Variables . . . . . .   40
                 5.2.4.1:  How Allocation Is
                  Implemented . . . . . . . . . . . . .   41


                               iii
MDD-020                                       Runtime Environment


                         CONTENTS (cont)


                                                         Page

                 5.2.4.1.1:  Historical Note on the
                  Allocation of External Variables  . .   42
                 5.2.4.2:  Heap Variables . . . . . . .   42
                 5.2.5:  Traps at First Reference . . .   43
                 5.3:  Static Linking . . . . . . . . .   44
                 5.3.1:  The bind Command . . . . . . .   44
                 5.3.2:  The linkage_editor Command . .   45
                 5.3.2.1:  Object Multi-Segment Files .   45
                 5.3.2.1.1:  External Definitions . . .   46
                 5.3.2.1.2:  Inter-Component Links  . .   46
                 5.3.2.1.3:  External Variable
                  Initialization  . . . . . . . . . . .   46
                 5.3.2.1.4:  Prelink First Reference
                  Trap  . . . . . . . . . . . . . . . .   47
                 5.3.3:  Alternatives to Static Linking   47
                 5.3.3.1:  set_fortran_common . . . . .   47
                 5.3.3.2:  Run Units  . . . . . . . . .   48
                 5.4:  Description of the Dynamic
                  Linker  . . . . . . . . . . . . . . .   49
                 5.4.1:  link_snap  . . . . . . . . . .   49
                 5.4.2:  fs_search  . . . . . . . . . .   50
                 5.4.3:  link_man . . . . . . . . . . .   51
                 5.4.3.1:  link_man$other_linkage . . .   51
                 5.4.3.2:  link_man$own_linkage . . . .   51
                 5.4.3.3:  link_man$combine_linkage . .   51
                 5.4.3.4:  link_man$grow_lot  . . . . .   51
                 5.4.3.5:  link_man$get_initial_linkage   52
                 5.4.3.6:  link_man$assign_linkage  . .   52
                 5.4.3.7:  link_man$set_lp  . . . . . .   52
                 5.4.3.8:  link_man$get_lp  . . . . . .   52
                 5.4.4:  get_defptr_  . . . . . . . . .   52
                 5.4.5:  set_ext_variable_  . . . . . .   53
                 5.4.6:  list_init_ . . . . . . . . . .   53
                 5.4.7:  Trap Processing  . . . . . . .   53
                 5.4.7.1:  trap_caller_caller_  . . . .   54
                 5.4.7.2:  link_trap_caller_  . . . . .   54
                 5.4.7.3:  How First Reference Traps
                  Get Turned Off  . . . . . . . . . . .   54
                 5.4.8:  Error Handling . . . . . . . .   55
                 5.4.9:  Lot Fault Handling . . . . . .   55
                 5.4.10: Security Issues  . . . . . . .   56
                 5.5:  The C Execution Environment  . .   57
                 6:  Area Management  . . . . . . . . .   58
                 6.1:  General Purpose Area Management    58
                 6.1.1:  Logical System Areas . . . . .   58
                 6.1.1.1:  User Free Area . . . . . . .   58
                 6.1.1.2:  System Free Area . . . . . .   58


                                iv
Runtime Environment                                       MDD-020


                         CONTENTS (cont)


                                                         Page

                 6.1.1.3:  Combined Linkage Area  . . .   59
                 6.1.1.4:  Combined Static Area . . . .   59
                 6.1.1.5:  hcs_$assign_linkage Area . .   59
                 6.1.1.6:  C Heap . . . . . . . . . . .   59
                 6.1.1.7:  RNT Area . . . . . . . . . .   59
                 6.1.1.8:  Ring 0's Use of Areas  . . .   59
                 6.1.2:  Freeing Allocations  . . . . .   60
                 6.1.3:  Area Format  . . . . . . . . .   60
                 6.1.3.1:  Area Header Format . . . . .   60
                 6.1.3.1.1:  The Free List  . . . . . .   61
                 6.1.3.2:  Area Block Format  . . . . .   61
                 6.1.3.3:  Area Extend Blocks . . . . .   62
                 6.1.4:  Algorithms Used  . . . . . . .   62
                 6.1.4.1:  Standard Allocation Method .   62
                 6.1.4.1.1:  Allocation . . . . . . . .   62
                 6.1.4.1.2:  Freeing  . . . . . . . . .   63
                 6.1.4.1.3:  Initialization . . . . . .   64
                 6.1.4.1.4:  Asynchronous Reinvocation
                  Protection  . . . . . . . . . . . . .   64
                 6.1.4.2:  No Freeing Method  . . . . .   65
                 6.1.5:  Modules Used . . . . . . . . .   65
                 6.1.5.1:  alloc_ . . . . . . . . . . .   65
                 6.1.5.1.1:  Entrypoints in alloc_  . .   66
                 6.1.5.2:  Entrypoints in define_area_    67
                 6.1.5.2.1:  define_area_ . . . . . . .   67
                 6.1.5.2.2:  get_next_area_ptr_ . . . .   67
                 6.1.5.2.3:  release_area_  . . . . . .   67
                 6.1.6:  Area Commands  . . . . . . . .   68
                 6.2:  Obsolete Buddy Block Mechanism .   68
                 6.3:  File System Areas  . . . . . . .   68
                 7:  Condition Signalling and Handling    70
                 7.1:  Condition Signalling . . . . . .   70
                 7.1.1:  Static Conditions  . . . . . .   70
                 7.1.2:  Normal Condition Signalling  .   71
                 7.1.3:  Crawlouts  . . . . . . . . . .   72
                 7.2:  How signal_ Is Invoked . . . . .   72
                 7.2.1:  Restarting a Condition . . . .   74
                 7.3:  Condition Handling . . . . . . .   74
                 7.3.1:  Condition Walls  . . . . . . .   74
                 7.3.2:  System Default Condition
                  Handling  . . . . . . . . . . . . . .   75
                 7.3.2.1:  default_error_handler_ . . .   75
                 7.3.2.1.1:  Entry $wall  . . . . . . .   76
                 7.3.2.1.2:  Entry
                  $standard_default_handler_  . . . . .   77
                 7.3.2.1.3:  Entry
                  $default_error_handler_ . . . . . . .   77


                                v
MDD-020                                       Runtime Environment


                         CONTENTS (cont)


                                                         Page

                 7.3.2.1.4:  Entry $wall_ignore_pi  . .   77
                 7.3.2.1.5:  Entry $ignore_pi . . . . .   77
                 7.3.2.1.6:  Entry
                  $condition_interpreter_ . . . . . . .   77
                 7.3.2.1.7:  Entry
                  $reinterpret_condition_ . . . . . . .   77
                 7.3.2.1.8:  Entry
                  $interpret_condition_ . . . . . . . .   78
                 7.3.2.1.9:  Entry
                  $reprint_error_message_ . . . . . . .   78
                 7.3.2.1.10: Entry
                  $change_error_message_mode_ . . . . .   78
                 7.3.2.1.11: Entry $add_finish_handler    78
                 7.3.2.2:  message_table_ . . . . . . .   78
                 7.3.2.3:  find_pathname_ . . . . . . .   78
                 7.3.2.4:  get_ppr_ . . . . . . . . . .   79
                 7.3.2.5:  get_tpr_ . . . . . . . . . .   79
                 7.3.2.6:  interpret_info_struc_  . . .   79
                 7.3.2.7:  interpret_oncode_  . . . . .   79
                 7.3.2.8:  linkage_error_ . . . . . . .   79
                 7.3.2.9:  special_messages_  . . . . .   79
                 7.3.3:  Reentering Command Level . . .   80
                 8:  Segment and Process Termination  .   81
                 8.1:  Segment Termination  . . . . . .   81
                 8.2:  Process Termination  . . . . . .   82
























                                vi
Runtime Environment                                       MDD-020


_1_:  _O_V_E_R_V_I_E_W

The  Multics  runtime  environment,  as  described  in  this MDD,
consists of the basic system  support and conventions for running
programs in a  single process.  However, this MDD  does not cover
in detail several aspects that  are described in other MDDs, such
as  name   and  address  space  management   and  fault/condition
processing.   Conversely, several   system runtime  databases are
described here which are not covered elsewhere.

The  topics covered include  process creation, the  basic support
segments used by a process,  the conventions governing use of the
stack, ring crossing mechanisms and conventions, dynamic linking,
the ring 0 environment, and some system runtime databases.

This section summarizes the following sections so that the reader
can  get a simpler  picture of what  is going on  before plunging
into the details.  It seems  choppy because many different topics
are discussed briefly.  However,  the topics are connected enough
so that each later section has to assume some overview knowledge.


_1_._1_:  _O_v_e_r_v_i_e_w _o_f _P_r_o_c_e_s_s _C_r_e_a_t_i_o_n

Process  creation creates  the environment,  initializes it,  and
makes  the  process  known  to  the  supervisor.   It  begins  in
hphcs_$create_proc,   which   is    generally   called   by   the
initializer/answering   service   (hphcs_   stands   for   highly
privileged  hardcore  segment).   create_proc  creates  a runtime
directory, known as the process  directory, for the exclusive use
of the new  process.  It then creates and initializes  the ring 0
data segments that  are necessary for the process to  run at all.
Finally  it  acquires  and  initializes  an  entry  in the active
process table to enable the process to be scheduled on its own.

The nascent process begins life  in init_proc, whose main goal is
to  figure  out  where  to  go  next.   It determines the initial
(non-zero)  ring to  be used  and calls  makestack to  set up the
environment for  that ring.  Then  it determines the  name of the
first   procedure  to   run  in   the  initial   ring  and  calls
call_outer_ring_ to invoke it.

The  first  procedure(s)  to  run  in  the  initial  ring is(are)
responsible for initializing I/O switches, establishing condition
handlers including the ones for the timers, invoking the start_up
exec_com, setting the ring's  working directory, and entering the
command level request loop.







                                1
MDD-020                                       Runtime Environment


_1_._2_:  _O_v_e_r_v_i_e_w _o_f _P_r_o_c_e_s_s _D_i_r_e_c_t_o_r_y _C_o_n_t_e_n_t_s

The process directory is used to store three types of per-process
data:

ox    data used by the hardcore supervisor

ox    program  activation history,   program variable  storage and
     dynamic linking data

ox    temporary files


The ring  0 segments are the process  initialization table (PIT),
the  process data segment  (PDS), the descriptor  segment (dseg),
and the known segment table (KST).

There is  a set of runtime  storage segments for each  ring used.
Each set includes  a stack, which is used  for program activation
history and  automatic variables, and one or  more area segments,
which are used for based, controlled and heap variables, internal
and  external  static  variables,  and  inter-segment links.  The
beginning of the stack is reserved for per-ring process data.

The temporary  files are those  used by compilers,  editors, etc.
Storing  them in  the process  directory eliminates  the need for
users to worry about the amount of space they require.


_1_._3_:  _O_v_e_r_v_i_e_w _o_f _S_t_a_c_k _M_a_n_a_g_e_m_e_n_t

The only hardware involvement with stack management in Multics is
the   choice  of   stack  segment   by  the   call6  instruction.
Nevertheless  the   system  has  strict  protocols   for  passing
arguments and pushing/popping stack frames.  Languages on Multics
are encouraged to use  shared assembly language operator segments
to actually manipulate the stack frames.

The  system provides  a facility  for performing  nonlocal goto's
(transfers  back  to  previous  activation  frames).  Intervening
stack  frames are  deleted.  Of  course this  depends on everyone
using the standard stack frame format.


_1_._4_:  _O_v_e_r_v_i_e_w _o_f _R_i_n_g _C_r_o_s_s_i_n_g

Since rings  on Multics are hierarchical, the  user normally runs
in the highest (least privileged) ring being used by the process.
From time  to time calls are  made to programs in  inner rings to
perform some service.  The only way to invoke a specified program
in an inner ring is to use the call6 instruction to transfer to a
segment  that  has  the  appropriate  gate  ring  brackets.   The


                                2
Runtime Environment                                       MDD-020


caller's  ring of execution  must be in  the call bracket  of the
gate.

It is  possible to invoke a  program that will execute  in a ring
higher than the calling ring, but not with an ordinary call.  The
procedure  call_outer_ring_ initializes   the environment  in the
outer  ring  if  necessary,  links  to  the  program  using  that
environment, and "transfers" to the entrypoint.

In all cases,  when control moves from an inner  ring to an outer
ring the inner ring's stack history is truncated so that an inner
ring program cannot be returned to from an outer ring.

Sometimes  an  inner  ring  must  know  the  ring  number  it  is
servicing.   In these  situations it  uses the  validation level.
Often  the validation  level must  be temporarily  changed to the
lower ring  number so that  inner ring segments  can be accessed.
The  validation level  can never  be set  lower than  the current
ring.

The ring alarm mechanism helps  to ensure that certain operations
are  cleaned up  before returning   from a  ring.  These  include
resetting the validation level and unlocking.


_1_._5_:  _O_v_e_r_v_i_e_w _o_f _t_h_e _D_y_n_a_m_i_c _L_i_n_k_e_r

A Multics process always has  programs from more than one segment
in its address space at a  time.  The linking between segments is
done at  runtime by the dynamic  linker.  One reason for  this is
that  at the hardware  level segments can  only be addressed  via
segment numbers, and these numbers  are only assigned at runtime.
A segment's number  is not necessarily the same  in each process.
This much could be done by  just inserting segment numbers into a
load  module.  However  dynamic  linking  is much  more flexible.
Links are filled  in only when they are referenced  for the first
time in  a process and search  rules (a list of  directories) are
used  to locate the  target.  If the  target is removed  from the
process's address space, any links to it are usually reset.  What
all this means in flexibility to the user is explained elsewhere.

Another effect of having the  segment numbers assigned at runtime
is that each process must have its own copy of a program's links.
However  the process  does not  need its  own copy  of the entire
program.   The  object  code  itself  is  pure  and  stays  in  a
non-writeable  segment that  can be  shared, while  the links and
internal static  data are copied  to the process  area.  The term
used for copying  the links is combining the  linkage section, in
the  sense of  putting it  in the  same writeable  segment as the
other combined linkage sections.




                                3
MDD-020                                       Runtime Environment


The linker is usually presented with  a segment name and an entry
name  that it  must convert   to a  pointer containing  a segment
number and an offset.  To do  this it first calls the file system
to locate  the segment using a  set of search rules,  assigning a
segment number if necessary; it copies the target segment's links
into the process  area if necessary; then it looks  at the target
segment's  list of  externally referenceable  entrypoints for the
entry name and corresponding offset.

The  linker  is  responsible   for  allocating  and  initializing
non-permanent  external  variables,  which  are  also  referenced
through links.

There are  three main reasons  for the linker  to run in  ring 0.
First, it  does not have  to link itself  since ring 0  is linked
during system initialization.  Second, it can snap links to gates
on  behalf of  rings that  are within  the call  bracket but  not
within the  read/execute bracket.  Third, it can  find the search
rule directories much  faster since it doesn't have  to deal with
pathnames.


_1_._6_:  _O_v_e_r_v_i_e_w _o_f _A_r_e_a _M_a_n_a_g_e_m_e_n_t

Dynamic  allocation is used  extensively in Multics.   The system
uses two allocation routines:  one for directory contents and one
for  everything   else.   The  general-purpose  routine   uses  a
first-fit algorithm  for allocation.  It  has options for  a more
efficient  but no-freeing  algorithm, multi-segment  areas, and a
choice  of  whether  to  initialize  area  contents  to zero when
allocating or freeing.


_1_._7_:  _O_v_e_r_v_i_e_w _o_f _C_o_n_d_i_t_i_o_n _S_i_g_n_a_l_l_i_n_g _a_n_d _H_a_n_d_l_i_n_g

All condition/fault/IPS  event signalling is done  via signal_ in
the ring of occurrence.  Signal_  first checks the ring's list of
static  handlers.   If  an  appropriate  static  handler does not
exist, signal_ searches the stack for the most recent relevant on
unit.   The pseudo  condition any_other  "matches" all  condition
names.  When a handler is found, signal_ invokes it.

A handler has three return options.  One is to return to signal_,
causing the action that caused  the condition to be restarted.  A
second  is   to  return  to  signal_,  telling   it  to  continue
signalling.   A third  is to  do a  nonlocal goto  to a  previous
invocation, unwinding the stack.

The  system provides  a default  handler for  all conditions  and
establishes it as the any_other handler in the first frame of the
user ring stack.



                                4
Runtime Environment                                       MDD-020


_1_._8_:  _O_v_e_r_v_i_e_w _o_f _S_e_g_m_e_n_t _a_n_d _P_r_o_c_e_s_s _T_e_r_m_i_n_a_t_i_o_n

Dynamically removing a segment from  the address space is done in
ring 0, but it is recommended  that the user ring procedure term_
be used in order to properly clean up the user ring environment.

A  process  terminates  itself  by  closing  all  its  files  (if
possible) and then signalling the answering service to unschedule
it and delete its temporary storage.


_1_._9_:  _O_v_e_r_v_i_e_w _o_f _t_h_e _R_i_n_g _0 _E_n_v_i_r_o_n_m_e_n_t

The ring 0 environment has several differences from that in other
rings.  Its  segment numbers are  assigned and its  links snapped
during system initialization, which allows  these to be shared by
all processes.   In particular, ring 0's  internal static storage
is shared by all users so ring 0 programs don't use it very much.
The stack for ring 0 does  not live in the process directory, but
is  one of  a group  of stack  segments assigned  only to  active
processes.  A few processes that are  known to never leave ring 0
have  no need  for address  space or  linking support.   They are
called hprocs and are created in a special way.































                                5
MDD-020                                       Runtime Environment


_2_:  _P_R_O_C_E_S_S _D_I_R_E_C_T_O_R_Y _C_O_N_T_E_N_T_S

The process  directory contains the per-process  temporary files.
It  is  part  of  the  Multics   file  system  as  a  branch  off
>process_dir_dir, but  the storage it uses is  independent of any
permanent record  quota the user may have.   Only the Initializer
and the  process itself have access  to it; all others  have null
access.   Its  contents  are  what  distinguish  one process from
another.

The process  directory segments can be classified  into two major
groups.  One  group is writeable only  in ring 0 by  the hardcore
supervisor.  Each segment in the  other group "lives" in a single
non-zero ring  and is used  directly by programs  running in that
ring.


_2_._1_:  _R_i_n_g _0 _P_r_o_c_e_s_s _S_e_g_m_e_n_t_s

The  process directory's  ring 0  segments are  created there  by
hphcs_$create_proc, which runs in the initializer process.  These
segments must  be initialized before  the process can  run on its
own.


2.1.1:  DESCRIPTOR SEGMENT

The  descriptor segment, known  as dseg, is  an array of  segment
descriptor words (SDWs).   There is an SDW for  each segment used
in the process,  indexed by segment number.  An  SDW contains the
location of  the segment's page table  as well as the  gate entry
bounds,  if any, and  the process's access  to the segment.   The
hardware uses  the segment number  portion of an  address to find
the SDW, which  will enable it to determine whether  and where to
access  the   segment.   The  hardware  locates   the  descriptor
segment's page  table via the descriptor base  register, which is
loaded by the privileged ldbr instruction.

It  can be seen,  then, that the  descriptor segment defines  the
address  space  of  the  process.   It  is  the  loading  of  the
descriptor base  register by the  scheduler that makes  a process
run.

The  ring brackets  of the   descriptor segment  are 0,0,0.   The
setting and  use of the fields  in an SDW are  described in other
documents.


2.1.2:  PROCESS INITIALIZATION TABLE

Most of  the information contained in  the Process Initialization
Table  (PIT)  relates  to  the  process's  interaction  with  the


                                6
Runtime Environment                                       MDD-020


answering service.   This includes information about  such things
as  the terminal being  used, the user's  login dialogue, how  to
contact the  answering service to logout, and  flags that specify
the  user's  privileges,  such  as  the  ability  to have several
processes  at  a  time.   The  PIT  also  contains the accounting
information  that  is  made  accessible  to  the  user,  such  as
month-to-date  expenditures and  times for  several categories as
well as spending/time limits.  A  third major function of the PIT
is  to provide  a place  for  the  answering service  to put  the
information  for an  absentee process  that it  received from the
submittor.

The ring  brackets on the  PIT are 0,N,N  where N is  the initial
nonzero  ring, so  it is  directly accessible  to the  user.  The
contents are written in the answering service's user ring by cpg_
and    simply   copied    into   the    process   directory    by
hphcs_$create_proc.


2.1.3:  THE PROCESS DATA SEGMENT

The  Process Data  Segment (PDS),  as its  name implies, contains
most  of the  process's static  variable data  needed in  ring 0.
There are too many different kinds  of items to describe here; it
is  more appropriate to  discuss each when  it is relevant.   The
process's security attributes and  basic initial information such
as the lock ID are filled in by hphcs_$create_proc.

The  ring  brackets  on  the  PDS  are  0,0,0  but  some  of  the
information is available through gates.


2.1.4:  THE KNOWN SEGMENT TABLE

The Known Segment Table (KST) is the main hardcore data base used
for  address  space  management.   It  maps  segment  numbers for
non-hardcore segments  into their location within  the hierarchy.
It  contains a  header, the  KST entries  themselves, the private
logical volume connection  table and the KST UID  hash table.  It
is described in MDD006, on the Multics File System.

The ring brackets on the KST are 0,0,0.


_2_._2_:  _P_e_r_-_R_i_n_g _P_r_o_c_e_s_s _S_e_g_m_e_n_t_s

These all have ring brackets of  N,N,N, where N is the associated
nonzero ring.






                                7
MDD-020                                       Runtime Environment


2.2.1:  THE STACK

The  stack  is  a  ring's  most  important  runtime  segment.  It
consists of a header and the procedure call activation frames.


2.2.1.1:  The Stack Header

The stack header  contains or points to most of  a ring's runtime
environment information,  excluding some that needs  the security
of ring 0.   Since it is expected that pointer  register 6 always
points to the current stack frame, everything at the beginning of
the stack  can be accessed  very efficiently.  Most  of the items
are   pointers  to   such  things   as  tables,  call/push/return
operators,  areas and  condition signalling  programs.  The stack
header  format  and  contents   are  documented  in  the  Multics
Programmer's  Reference  Manual,  order  number  AG91-04 (at this
writing; revision number may change).

Many  processes also  keep the   linkage offset  table (LOT)  and
internal static offset table (ISOT)  in the stack segment.  These
tables  each consist  of one  word  per  segment number  up to  a
certain limit, with a default of 512 words.  However, since these
tables are  not used for ring  0 segments (below about  200), the
beginning of the tables can  overlay other information.  Thus the
LOT logically begins at the beginning of the stack segment, which
is  also the location  of the stack  header.  Likewise, the  ISOT
logically  begins immediately  after the  LOT, which  is also the
location of the System Condition Table (SCT).


2.2.1.2:  The Stack Frames

The other  main purpose of the  stack segment is to  serve as the
location of  the procedure call activation  frames, which contain
the  procedures' automatic storage.   The first frame  is located
after the ISOT and is pointed to by stack_header.stack_begin_ptr.
The  rest   of  the  segment   is  reserved  for   stack  frames.
stack_header.stack_end_ptr points  to where the next  stack frame
should be  laid down, which  is also the  end of the  most recent
valid  frame being used.   Each frame's header  must adhere to  a
standard format, which is  documented in the Multics Programmer's
Reference Manual.   Failure to conform  can cause the  process to
behave in unpredictable ways.


2.2.1.3:  Manipulating the Stack

This section discusses conventions  for pushing and popping stack
frames.  The formats and conventions are described in the Mulitcs
Programmer's Reference Manual.



                                8
Runtime Environment                                       MDD-020


2.2.1.3.1:  Normal Call

The caller generally  creates the argument list in  its own stack
frame.  The  list consists of ITS  or ITP pointers to  the actual
arguments  and to their  descriptors.  Negative offsets  from the
beginning  of the next  stack frame may  not be used  because the
next  stack   frame  does  not  always  belong   to  the  program
referencing the argument list.

After creating the argument list, the caller obtains a pointer to
the entrypoint to  be called.  Such pointers are  stored as links
in the  copied version of  the object segment's  linkage section.
If the  referenced link has  not yet been  "snapped", the dynamic
linker is invoked to find the desired entrypoint (see the section
on the dynamic linking mechanism below).

Finally  the  caller  transfers  to  a  shared  assembly language
"operator" segment to make the actual call.  Because the language
of  the callee  is not  known until  dynamic linking  time, it is
necessary  to have  certain system-wide  conventions when  making
calls:   pointer register  0 must   point to  the argument  list,
pointer register 2 must point to the callee's entrypoint, pointer
register  6 must point  to the current  stack frame, and  pointer
register 7 must point to the current ring's stack header.


2.2.1.3.2:  Push

The called  entrypoint loads its  stack frame size  into an index
register  (the choice  of register  is defined  by the  operators
used) and  then immediately transfers to  an entry/push operator.
This operator initializes several fields in the new stack frame's
header, generally loads a pointer to the segment's active linkage
section  in PR4,  updates stack_header.stack_end_ptr,  and may do
other  initialization before  returning.  It  is also responsible
for saving the values of the machine's indicators.


2.2.1.3.3:  Normal Return

When the  called procedure returns,  it transfers to  yet another
operator.   This operator  updates  the  stack end  pointer among
other things and returns to  the location specified by the return
pointer in the caller's stack  frame.  The machine indicators and
pointer register 6 must be restored and pointer register 0 set to
stack_frame.operator_pointer,* (of the  frame being returned to).
No other registers need to be restored.







                                9
MDD-020                                       Runtime Environment


2.2.1.3.4:  Stack Truncation

None of the operators above touch the program's automatic storage
area.  This means that  uninitialized variables generally contain
garbage.  However, when the process  blocks, the stack segment is
truncated  to the  length actually  being used.   This cleans  up
garbage that may have been left by  a lot of nested calls and may
reduce paging.


2.2.1.3.5:  Use of Operators

All languages  on Multics are  required to use  operator segments
for  pushing and popping  stack frames.  In  particular, compiled
code should  not automatically reference the  stack frame header.
This  contributes to  system flexibility.   (Of course  there are
always some  programs that are explicitly coded  to reference the
stack frame header.)

The  most  heavily  used  operator  segment,  pl1_operators_, has
pointers to  it in the stack header.   However, several languages
have their  own operator segments  and their own  conventions for
communicating  with them.   The entries  of programs  compiled by
these  languages  should  locate   their  operator  segments  via
assigned  pointers  in  the  operator_pointers_  transfer vector,
which is pointed to by stack_header.trans_op_tv_ptr.


2.2.1.3.6:  Abnormal Returns

There  are  times  when  one  wants  to  return  to  a  procedure
activation  that precedes  one's caller  and throw  away all  the
stack frames in between.  Usually  an error has just occurred and
it is necessary to discard all history associated with the error.
Because  the  same  environment  will  continue  to  be used, all
procedures with  activations to be discarded should  clean up any
changes they  made that are  no longer appropriate  or would have
been cleaned up anyway if they had returned normally.

Multics  provides  an  unwinding  facility  that  is usually used
implicitly through language-defined constructs.   It is also used
by the  release mechanism at  command level.  It  is accessed via
stack_header.unwinder_ptr by one of the operators in the operator
segment.   Its  argument  is  a  label,  which  consists  of  two
pointers:  one to the code location  to transfer to and the other
to the stack frame to be used.

The  program that  the system   provides for  this is  unwinder_.
unwinder_ determines the target stack frame from the label, calls
unwind_stack_   to   "clean   up"   the   intervening   procedure
activations, and prepares for  the transfer.  The actual transfer
is done by nonlocal_goto_.


                                10
Runtime Environment                                       MDD-020


unwind_stack_  processes  every  stack  frame  from the specified
starting frame up to, but  not including, the target frame.  Each
frame is  searched for a  handler for the  cleanup condition.  If
found, it  is invoked.  These  handlers are "established"  by the
frames' owners  to do any environmental  cleanup necessary during
an abnormal return.   Before a cleanup handler is  invoked, it is
unthreaded from the frame's list of condition handlers so that it
cannot  be reinvoked.   After a  cleanup handler  has returned to
unwind_stack_,  the  frame's  entire  condition  list is disabled
because  the frame/activation has  logically ceased to  exist and
should no longer be searched by the signalling mechanism.

unwind_stack_ is also called by  other programs, such as signal_,
that want to  clean up (logically discard) part of  the stack but
that also  want to make  their own transfer  arrangements.  It is
possible to specify that the entire stack preceding and including
the starting point be processed.

After  the stack  frames following   the target  frame have  been
cleaned up, unwinder_ calls  nonlocal_goto_$same_ring to make the
transfer, passing it the  label's pointers as separate arguments.
nonlocal_goto_ resets the stack  end pointer and pointer register
6  together  with  interrupts  inhibited,  resets  the  few other
registers required  for a return, and "returns"  to the specified
location.  In general, the code  at a nonlocal label cannot count
on the values  of any registers except pointer  register 6, which
points  to the  stack frame,   and pointer  register 0,  which is
restored to stack_frame.operator_ptr,*.


2.2.1.4:  Other Stack Segments

It is possible for the user  (least privileged) ring to have more
than one stack segment.  There are several reasons for wanting to
do this.

One  is   that  a  language   may  want  to   use  a  nonstandard
call/push/return sequence that would  confuse the system programs
that walk the stack.  In  this case, the original, standard stack
is still  used for condition signalling and  handling, and should
be used  when making calls  to programs that  expect the standard
sequence.   It  is  not  absolutely  necessary  to  keep  pointer
register 6 pointing to the original stack since the stack pointer
is  stored in  pds$stacks(N) in  ring 0,  but one  should be very
careful about reusing it.

Another reason for having multiple stacks in the user ring is the
use of Control  Point Management (CPM).  In this  case, there are
several  standard stacks and  the process alternates  among them,
setting pds$stacks(N) as it goes.   The stack headers of each all
point to the  same tables and areas, so that  the main difference
between the control points is the procedure activation record.


                                11
MDD-020                                       Runtime Environment


A third (obsolete) reason for  having other stack segments was to
put the stack somewhere other than in the process directory or to
make experimental changes in format or content.

Having  multiple stacks in  inner rings is  not easy because  the
hardware  automatically uses  the "real"  stack when transferring
into an inner ring.  Also it is  difficult to get out of an inner
ring without having the stack truncated behind you.


2.2.2:  THE USER FREE AREA

The ring's area segment contains  the Reference Name Table (RNT),
ipc_'s  Event Channel  Table  (ECT),  the linker's  search rules,
copies  of programs'  linkage and  internal static  sections, and
external variables such as Fortran COMMON that do not live in the
file system hierarchy.  The whole segment  is used as a PL/I area
managed by  the Multics system  area software.  In  addition this
area  is extensible,  which means  that if  an allocation doesn't
fit, a new  segment is automatically created and  threaded to the
previous one  (as long as  the allocation size  itself is smaller
than a single segment).

The RNT is  actually allocated in a separate area  that is itself
allocated in the  user area.  This keeps the RNT  more compact so
as to avoid  excessive page faults while referencing  it.  If the
RNT area  becomes too small, a  new one is allocated  and the old
one is copied and released.

Since  the user  area is  accessed  via  a pointer  in the  stack
header, a user  may create his own area anywhere  he wants to and
is able to and then change stack_header.user_free_ptr to point to
it.  All of the system-managed  tables allocated in the user area
are  located  via  pointers  in  the  stack  header  so they will
continue to be used in their old locations.  Any tables allocated
after the area change will be in the new area.

Of course since any information in the area can be tampered with,
the  system should  never assume  otherwise when  making security
decisions.  Any  malicious or accidental change  to system tables
stored in a  ring's area should affect only that  ring or at most
the existence of  the process; it should not  affect the security
operation of an inner ring.


2.2.3:  TEMPORARY SEGMENTS

The process directory is a good  place for system programs to put
their temporary storage, since its record quota is independent of
the  user's permanent  hierarchy quota.   Thus the  user does not
need to worry about the amount of space that a compiler or editor
may  require,  but  only  about  the  space  the  completed  file


                                12
Runtime Environment                                       MDD-020


requires.  However,  there is a quota for  the process directory;
although it is generous, it is not infinite.

A temporary  segment may have any  name as long as  it includes a
unique  string.  The  mechanism normally   used is  that of  (get
release)_temp_segments_,  which  manages   a  pool  of  temporary
segments.   The  segments  are  created  as  needed.  Releasing a
temporary segment truncates it and returns it to the free pool.














































                                13
MDD-020                                       Runtime Environment


_3_:  _P_R_O_C_E_S_S _C_R_E_A_T_I_O_N

Creating a process on Multics is relatively expensive since there
are several segments  to create and initialize.  Much  of this is
due  to the  method of  address space  management, which requires
several tables and process variables for its maintenance.

The new  process must do part  of the work itself  to ensure that
the  proper non-ring-zero  environment  is  set up.   The runtime
support  procedures used  outside  of  ring zero  are dynamically
linked to.   Many users have  the ability to  specify where their
own versions are, so all of this  linking must be done by the new
process.  It also must be done before the process reaches command
level.


_3_._1_:  _P_r_e_p_a_r_a_t_i_o_n _b_y _t_h_e _C_r_e_a_t_i_n_g _P_r_o_c_e_s_s

The   creating  process   (usually  the   Answering  Service)  is
responsible for the identification and authentication of the user
and for obtaining the correct user attributes from tables such as
the Project Definition Table (PDT).  These operations are for the
most  part not  done in  ring 0  and are  described in MDD-010 on
System/User Control.

The   actual  creation   of  the   process  itself   is  done  by
hphcs_$create_proc.  Access to it is highly restricted because it
fills in the process's ring 0  data fields that define the user's
access  identity  and  authorization  information.   Before doing
anything else, create_proc checks the requested authorization and
max_authorization to make sure they are within the system bounds.
Then it sets up the process  by creating a process directory with
its ring 0  segments and obtaining an Active  Process Table (APT)
entry and filling it in.

The  process directory  is created  in >process_dir_dir  with SMA
access  for  the  user  and  the  creating  process  and null for
everyone else.   It has a  minimum quota of  10 directory records
and 20 segment records.

The ring  0 segments dseg,  KST, PDS and  PIT are created  in the
process directory with  REW access for the user  and the creating
process and null for everyone else.  The ring brackets on the PIT
are  changed to  0,N,N, where  N is  the highest  allowed ring as
obtained from  create_proc's input structure.  The  dseg, KST and
PDS  are made  encacheable because  they are  not shared.   (This
encacheability  is only  respected by   the L68  CPUs, since  all
segments are encacheable  on the DPS8/M CPUs.)  The  parts of the
dseg and  the KST that correspond  to the ring 0  segment numbers
are copied from the creating  process's dseg and KST respectively
since they are  the same for all processes.  The  contents of the
PIT are copied from a template  provided as input by the creating


                                14
Runtime Environment                                       MDD-020


process.  The PDS fields that are  filled in are those that won't
change and those that must be initialized to something other than
zero.  Many  of these values  are security-related and  come from
the input structure.

The name  of the home directory  is stored in pds$homedir  and is
normally copied  from the input  structure.  However there  is an
unused  option  to  have  create_proc  create  a  temporary  home
directory off the process directory.

It is a  little tricky to work in two address  spaces at the same
time.   Care must  be taken   to always  use the  correct segment
number.  In addition, initiating another process's ring 0 segment
is a privileged operation.

create_proc must  also make the process runnable.   It obtains an
Active Process Table entry (APTE) from pxss and fills it in.  The
use  of the APT  is described in  MDD-019 on the  Scheduler.  Two
fields in  the APTE are used  by ipc_ to validate  channel names,
ipc_r_offset   and   ipc_r_factor.    ipc_r_offset   is   set  by
create_proc.  ipc_r_factor is set after an indeterminate delay by
init_proc  so   that  its  value  can  not   be  determined  from
ipc_r_offset.

When create_proc returns  to its caller, the new  process is left
waiting to be run whenever the scheduler gets around to it.


_3_._2_:  _P_r_e_p_a_r_a_t_i_o_n _b_y _t_h_e _N_e_w _P_r_o_c_e_s_s

When the scheduler sees a new  process that has not yet been run,
it  causes  execution  to  begin   in  init_proc  in  ring  zero.
init_proc initializes some things that couldn't be initialized by
the  answering service,  creates  an  environment in  the initial
(nonzero) ring, determines the name of the initial procedure, and
finally invokes it.


3.2.1:  INITIALIZING THE ENVIRONMENT

The  items initialized are  the pathname associative  memory, the
KST's  numbers  for  the  max/min  non-ring-zero  segment numbers
allowed in the process,  and pds$stacks(0).  The validation level
is set to the initial ring  so that all segments being referenced
will actually be made known in that ring.

The environment in the initial  ring is initialized by makestack.
It creates a stack for the initial ring in the process directory,
taking care not to create through  a link.  It is responsible for
filling  in the  stack header.   Since several  of the  items are
procedure  pointers that  must  be  acquired through  the dynamic
linker,  it  is  necessary  first  to  initialize the environment


                                15
MDD-020                                       Runtime Environment


needed by the linker.   link_man$get_initial_linkage is called to
do this.

The linker needs a place  to allocate linkage/static sections and
it  needs tables  to enable  it to  find them.   To satisfy this,
link_man$get_initial_linkage  creates an  area, LOT  and ISOT for
the  ring.  The  area is  created as  a separate  segment in  the
process directory and  the area pointers in the  stack header are
set  to point to  it.  (See the  section on the  area mechanism.)
The LOT and ISOT are each max(pds$lot_stack_size, 512) words long
and are located sequentially at  the beginning of the stack, with
the  LOT  first.   Neither  is  explicitly  initialized, so their
initial entry values are zeroes.   Since the first 200 (octal) or
so  entries corresponding  to ring  zero segment  numbers are not
used  by the linker,  that space is  used for other  things.  The
stack  header overlays the  beginning of the  LOT and the  System
Condition  Table  (SCT)  overlays  the  beginning  of  the  ISOT.
stack_header.stack_begin_ptr,  which determines  the location  of
the first stack frame, is set to point to the end of the ISOT.

Back in makestack,  there is a little more to  do for the linker,
namely  to initialize  the Reference  Name Table  (RNT).  The RNT
consists of  linked nodes that  are dynamically allocated,  so it
needs an  area in which to  allocate .  In order  to keep the RNT
from being fragmented  across many pages, a separate  RNT area is
created  inside  the  linker  area.   Inside  this area makestack
initializes the RNT header and the search rules.

Now  that the  linker can   be used,  makestack gets  pointers to
various  procedures  such  as  signal_,  pl1_operators_, and some
static  condition handlers  to put   in the  stack header.   When
invoking the linker  on behalf of an outer ring,  it is necessary
to   call  it   explicitly,  rather   than  referencing   through
compiler-generated  links.  The  latter would  already have  been
prelinked  during system  initialization; even  if they  weren't,
referencing them while in ring zero would cause the linker to try
to link in  the ring zero environment.  The search  rules used at
this point  do not include  the referencing directory  and may or
may not include the working directory (see below).


3.2.2:  FINDING THE INITIAL PROCEDURE

After  the   initial  ring  environment  has   been  initialized,
init_proc  must determine  the  initial  procedure to  be called.
There is a  default for this, but some users  have the ability to
specify  alternatives.    The  mechanisms  for  doing   this  are
described below, even though they are also described elsewhere.

This  will be  easier to   explain if  the default  mechanism for
getting to  command level is  given first.  If  no alternative is
specified,  init_proc "calls  out" to  initialize_process_ in the


                                16
Runtime Environment                                       MDD-020


initial  ring.   After  doing  more  environment  initialization,
initialize_process_    calls   the    procedure   specified    by
pit.login_responder,   which   is   normally   process_overseer_.
process_overseer_   returns   the   initial   command   line   to
initialize_process_, which then calls the listener.

Now for the alternatives:  Users with the vinitproc attribute can
replace the  non-ring-zero procedures with their  own.  There are
two ways to  do this.  One is to specify  a replacement name; the
other is to put different procedures with the same name (or links
to them) in the home directory.

A replacement name  may be specified either in the  PDT or in the
login line and is identified  by the keyword initproc.  This name
is passed  to the process as the  string pit.login_responder.  If
the   keyword   direct   is   also   used,   the   name  replaces
initialize_process_   and  is   called  directly   by  init_proc.
Otherwise when initialize_process_  calls pit.login_responder the
replacement will be invoked instead of process_overseer_.

Replacements  for  all  the  non-ring-zero  procedures referenced
during  process  initialization  can  also  be  put  in  the home
directory.  This includes procedures that are pointed to by stack
header variables.  This works because the procedures are found by
the  linker using  the search  rules, and  the home  directory is
added to  the search rules  (as the working  directory) for users
with  vinitproc.  Users  without vinitproc  have null referencing
directory  and working directory  search rules until  just before
the listener is called, so their processes find everything in the
system libraries.

The two mechanisms above may  be used together.  If a replacement
name is a pathname, it will  be found in the specified directory.
But if it is just an entryname,  it will be searched for first in
the home directory.

"Direct"  procedures are  always specified  by pathname.   If the
directory is not given explicitly, the home directory is assumed.
init_proc has to  go to some trouble to make  sure that the exact
procedure specified is invoked.  The  problem is that the dynamic
linker is used  to find the procedure and it  determines the path
from  the search  rules.  Since  the first  two search  rules are
initiated segments and  referencing directory, init_proc actually
initiates the procedure, which causes the name to be added to the
RNT (list  of initiated segments), and also  passes the resulting
pointer to the  linker to be used in  determining the referencing
directory.

When init_proc has all the information  that it can get about the
initial procedure, it calls call_outer_ring_ to invoke the linker
and to transfer to the initial  procedure.  This is the only time
in the  process when call_outer_ring_  can be used  in ring zero.


                                17
MDD-020                                       Runtime Environment


See  the  subsection  on  outward  calls  in  the section on ring
crossing mechanisms.


3.2.3:  COMPLETING THE USER RING INITIALIZATION

When  the  process  reaches  the  initial  ring  it  must do more
environment  initialization, since  only the  minimum amount  was
done  in  ring  zero.   This  includes  setting  up  more  static
condition handlers such as the  ones for timers, initializing the
basic  I/O  switches,  and  establishing  a  "default"  condition
handler for the  ring.  Without these in place  the process might
easily terminate itself.

After  the environment  is complete,  initialize_process_ invokes
pit.login_responder  to determine  the initial  command line  and
optionally to go to command  level.  The login responder provided
by  the system,  process_overseer_, turns  on the  command_query_
escape  mechanism   to  the  command  processor   and  looks  for
start_up.ec,  unless the  user has  specified -no_startup  during
login.  It looks first in the home directory, then in the project
directory  and finally  in >system_control_1.   process_overseer_
lets initialize_process_ actually call the listener.


3.2.4:  INITIALIZER DIFFERENCES

Process initialization is somewhat  different for the initializer
(system   control)  process.    In  this   case  init_proc  calls
system_startup_, which  runs a basic  command level in  ring 1 to
allow the operator  to do reloads, etc.  The  environment at this
level is quite primitive.  The  "stan", "mult" or "star" commands
can  be used  to enter  a ring  4 environment  that is  much more
powerful.  See MDD-010 on System/User Control for details.




















                                18
Runtime Environment                                       MDD-020


_4_:  _R_I_N_G _C_R_O_S_S_I_N_G _M_E_C_H_A_N_I_S_M_S

This  section discusses ways  in which rings  may be entered  and
left, as well as other  mechanisms that affect how programs serve
less privileged rings.

Rings on Multics are hierarchical with the user in direct control
of only the least-privileged  (highest-numbered) ring used by the
process.   Programs in lower-numbered  rings are invoked  only to
provide relatively  short-lived services on behalf  of the user's
ring.   They do not  do direct I/O  nor do their  stack histories
remain when  the ring is left.   This is true even  under Control
Point  Management  (CPM),  when  there  may  be several user-ring
stacks.  There is  still only one stack for each  inner ring, and
all control point scheduling is  done when the process is running
in the user ring.


_4_._1_:  _G_a_t_e_s

Gates  are  executable  segments  with  special  characteristics.
Because they  are important for  security and are  implemented in
such a stylized way, they are discussed in detail.


4.1.1:  WHAT GATES ARE

In  order to  guarantee that  programs executing  in lower  rings
behave  as they were  intended, it is  necessary to restrict  how
they  can be  entered.  It   must not  be possible  to circumvent
access  checks, etc.   by transferring  to an  arbitrary location
from an  outer ring.  In Multics,  the only ways that  a user can
enter  a lower ring  are by calling  special gate programs  or by
executing certain faults.  Only a few faults are actually handled
in  the TCB  and their  processing is  narrow in  scope and  well
defined.   They  will  be  discussed  later.   For  all practical
purposes, calling a gate is the only way to enter a lower ring.

Gate segments have two main distinguishing features.  One is that
their ring brackets usually force them  to execute in a ring that
is lower than the calling  ring.  With ring brackets of r1,r2,r3,
r1:r2 inclusive  is the read/execute bracket  and r2:r3 inclusive
is the  call bracket; gates  are defined by  having r2 <  r3.  In
rings > r2,  only the call6 instruction may be  used to reference
the segment.  The second feature is an entrybound--a limit on the
text  section locations  that can  be transferred  to.  Gates are
specially coded in  ALM with a transfer vector  at the beginning;
the entrybound is  set at the end of the  transfer vector so that
gate segments can be entered only through the transfer vector.

The  entrybound  is  actually  checked  by  the  hardware when it
executes a call6 instruction that is transferring to a lower ring


                                19
MDD-020                                       Runtime Environment


via  a  gate  segment.   If  the  referencing  offset exceeds the
entrybound,  the  not_a_gate  condition  is  raised.   Since  all
hardware  references   to  a  segment  go   through  the  segment
descriptor word  (SDW), the entrybound  is kept there  along with
the ring brackets.  For non-hardcore gates, the entrybound in the
SDW  is filled  in at  segment  fault  time, i.e.,  when the  SDW
contents are updated.  At that time the entrybound is copied from
the segment's branch in the file system hierarchy if the gate bit
in the SDW indicates a gate.   Thus when installing a gate, it is
important to set the segment's entrybound attribute in the branch
before  resetting the  ring  brackets.   For hardcore  gates, the
initial   process's  SDW  entrybound   field  is  filled   in  by
init_hardcore_gates  during  system  initialization.   All  other
processes copy the hardcore SDWs during process creation.

The call6  instruction is the  only one that  recognizes the call
bracket  and thus  is the  only instruction  that can  be used to
change  the ring of  execution.  All other  transfer instructions
get a not_in_execute_bracket condition if they try to transfer to
a different  segment that has  r2 lower than  the executing ring.
The call6  instruction also sets  pointer register 7  to point to
the base of the lower ring stack.  The hardware obtains the stack
segment  number by adding  the target ring  number to the  ring 0
stack segment number found in the descriptor base register.  Thus
the  calling  ring  cannot  affect  the  inner  ring's  choice of
environment.  There  are no further software  mechanisms to allow
control points  in inner rings,  etc.  The call6  instruction may
not be used to change the ring of execution to a higher ring.  An
attempt to do so results in an outward_call condition.

Gate segments should do only the minimum amount of work necessary
to  get control transferred  to other inner-ring  segments.  This
work is  described below, but  in practice gate  coders need know
nothing of the details.  There are  easy to use macros defined in
gate_macros.incl.alm that allow one to define a gate entry simply
by giving the  entry name, target name, and  number of arguments.
In  general, gate source  segments contain only  a list of  macro
calls.  Exceptions include the hcs_ entries to return the virtual
cpu  time  used.   These  entries   are  used  for  metering,  so
eliminating  overhead is  critical; they  are coded  in the  gate
segment itself.


4.1.2:  NON-HARDCORE GATE ACTIONS

A non-hardcore  gate entry immediately  transfers to the  part of
the gate segment beyond the  transfer vector, where it "calls" an
internal  setup "subroutine",  loads the  indicator register with
zeroes, and does a short_call (no registers saved) to the target,
passing  the argument  list that  it received.   When the  target
returns, the  ordinary ALM return  operator is used  to leave the
gate.


                                20
Runtime Environment                                       MDD-020


The setup "subroutine"  is really a piece of common  code that is
transferred  into  and  out  of.   It  pushes  a stack frame with
prev_sp  pointing to  the last  stack frame  used by  the calling
ring, loads  pointer register 4  with the gate's  linkage pointer
(as  found  in  the  inner  ring's  LOT)  so  that the short_call
operator can find  the link to the target, and  checks the number
of arguments passed  to it against the number  expected, which is
stored two  words before the  "calling" gate entry's  code in the
main part of  the gate.  If the argument counts  don't match, the
gate_error condition is signalled.


4.1.3:  HARDCORE GATE ACTIONS

Hardcore gates are somewhat more complicated.  In addition to the
actions taken by non-hardcore gates, they have three extra tasks:
to establish a handler for the bad_dir condition if requested, to
keep metering data for the gate entry, and on return to check for
and to try to fix impending ring alarms.


4.1.3.1:  bad_dir Condition Handler

Gate  entries  that  want  a  handler  for  the bad_dir condition
specify "bad_dir_trap" as the fifth  argument to the hgate macro.
The bad_dir  condition is signalled by directory  control when it
encounters  a defective directory,  so all gate  entries involved
with accessing directories should have bad_dir handlers.

When bad_dir_trap is specified, the gate entry creates an on unit
for the bad_dir  condition in space reserved in  the stack frame.
The handler  checks the ring number  in PR6 to make  sure that it
was  called in ring  zero and then  unwinds back to  the original
gate entry frame, which must be  the first frame on the ring zero
stack.   (If  it  isn't,  the  signal  is  continued.)  After the
nonlocal transfer, common code in  the gate segment turns off the
condition   handler,  calls   verify_lock$verify_lock_bad_dir  to
invoke the salvager, and then transfers back to the original gate
entry for a retry, including resetting the condition handler.

As little of this extra code as possible is put into the transfer
vector  section.  What is  necessary are three  transfers--one to
the code that sets up the on unit, one to the handler itself that
unwinds, and  one to the  code that calls  verify_lock.  The last
two transfers, as well as all the related code in the "main" part
of the gate,  are shared among all the gate  entries with bad_dir
handlers.   The on  unit setup  code stores  the location  of the
original gate  entry in the stack  frame so that the  common code
will know where to transfer to after calling verify_lock.





                                21
MDD-020                                       Runtime Environment


4.1.3.2:  Metering

Each hardcore gate entry keeps  its own metering data in internal
static.  It  stores cpu time,  virtual cpu time,  page waits, and
number of  calls.  Of course  this metering includes  any retries
because of the bad_dir condition.   The actual code used for this
is  shared  among  all  gate  entries.   The  common  setup  code
initializes  some variables  in the  stack frame  and the  common
return code adds the results to the variables in internal static.
The return code is passed the  address of the gate entry's static
storage.   This address  is also  stored in  the word immediately
preceding the gate entry's non-transfer-vector code.


4.1.3.3:  Checking for Ring Alarms

The hardcore gate common return code also checks for an impending
ring alarm.  The ring alarm mechanism is discussed in more detail
in  another  section  of  this  MDD;  briefly,  if the ring alarm
register contains a number greater than the target ring number of
a  transfer instruction,  a ring_alarm  fault is  signalled.  The
reason for the ring alarm is stored in the PDS; in some cases the
problem can be corrected and the  ring alarm can be reset.  It is
up to  the gate return  code to check  whether the ring  alarm is
set.  If it is set, the ring alarm count meter is incremented and
ring_alarm$poll is  called to try to reset  the alarm.  Depending
on the output from ring_alarm$poll,  the gate entry either does a
normal  return through  the alm   return operator  or an  in-line
"short   return"   and   transfer   to   the  traffic  controller
(pxss$pre_empt_poll).   In the  latter  case  the stack  frame to
return to is stored in pds$pre_empt_poll_return.


4.1.3.4:  Obtaining the Ring Zero Linkage Pointer

Hardcore gates obtain their linkage pointers from a text location
instead of  from the LOT.   (The pointers are  initialized during
system  initialization; there  are external  definitions for  the
pointers'  text locations.)   This is  necessary because hardcore
gates each  have two segment  numbers--one for ring  zero and the
other for rings greater  than zero--and the non-ring-zero segment
numbers, which  they were called  by, are not  represented in the
ring  zero LOT.   Pointers to   the gates'  linkage sections  are
written  into the  gates'  text  sections following  the transfer
vectors by init_hardcore_gates during system initialization.


4.1.3.5:  Fast Hardcore Gates

Some hardcore gate entries do only the absolute minimum to get to
their  targets--load  the  indicators  and  linkage  pointer  and
transfer  to the target.   Since they do  not push stack  frames,


                                22
Runtime Environment                                       MDD-020


they  are not  returned to,  which means  that they  do not  have
bad_dir handlers  or meter or  check for ring  alarms.  Therefore
they cannot be used for activities that require such support.


4.1.4:  GATE ACTORS

All gate segments have a  special unnamed entrypoint that returns
information  about any  of the  other gate  entrypoints.  This is
helpful when one wants to see  such information but is not within
the read/execute bracket.  In this case,  the user has a right to
know what gate  entries can be called but cannot  read the gate's
definitions directly.   These special entypoints are  called gate
actors.

A gate  actor entry must be  within the transfer vector  and at a
known offset, since  it has no external name.  It  is actually at
gate location 0.  Its calling sequence consists of two arguments,
a  PL/I varying  character string  and a  fixed bin  (35) number,
corresponding   to  the   desired  entry's   name  and   location
respectively.   If the  name is  null, the  specified location is
used to find  the name.  Otherwise, the name is  used to find the
location.   All the work  is done within  the gate itself  in the
non-transfer-vector section.

Gate actors  in non-hardcore gates  work only if  the process has
already snapped a link to one  of the gate's other entries.  They
depend on the linkage section having been combined in the ring of
execution,  which for gates  is only done  by the linker  when it
processes a link fault.

Gate actors  are not really  necessary since there  could be ring
zero    utilities    to    provide    the    same    information.
hcs_$get_defname_ does return the  entry name associated with the
specified  offset, but  the system   does not  provide a  general
utility for the reverse.


4.1.5:  GATE MACROS

As  can be seen  from the discussion  above, the reliability  and
maintainability of gates depends on having macros to generate all
the code.   It is very  easy to read  gate source code,  which is
just  a list  of macro  calls.  It  is harder  to understand  the
generated code and  much harder to look at  the macros themselves
to  figure out what  they do.  The  macros use multiple  location
counters, labels with  the same names in different  macros, and a
lot of small "sub" macros.   The following description of how the
macros work probably should be in the source, but isn't.

The gate macros use three location counters for the text section:
transfer_vector, tv_end and main, joined in that order.  The code


                                23
MDD-020                                       Runtime Environment


itself is in the main part; the transfer vector contains only one
word per gate  entry that transfers to the entry's  code in main.
tv_end specifies the entrybound  and, in hardcore gates, includes
the segdef .tv_end with the value of the entrybound.  The linkage
location  counter  is  used  for  the  metering  data in internal
static.   The  metering  data  for  a  gate  entry  has the label
<entry_name>.t.

The  first statement in  a gate source  segment, after the  name,
invokes  either the  gate_info or  the hardcore_gate_info  macro.
Both include a transfer for the gate actor and use the gate_actor
macro to put the actor code in main.  They differ in the way they
obtain the  linkage pointer.  Both gate_info  macros also include
the   setup  code,    labelled  by   .setup.   hardcore_gate_info
additionally includes  segdefs for .tv_end and .my_lp  as well as
the  code for metering  and ring alarm  checking to be  performed
when  the gate  entry returns.    It also  defines the  automatic
variables to be used for metering and bad_dir condition handling.

The macros  that source programs  use to define  gate entries are
gate,  hgate and  fgate.  All  of these  use the  gentry macro to
produce the  transfer vector transfer and to  define the transfer
target location  in main.  fgate  uses no other  macros or common
main code pieces.  gate and  hgate then transfer to .setup, which
in both cases  pushes a stack frame and uses  code from the macro
gcheck to check the argument count.  gcheck code then signals the
gate_error  condition  if  the  argument  count  is  not what was
expected.  The .setup code used by hgate also stores the starting
information for the meters.

hgate  macros use  other pieces   of common  code.  If  the fifth
argument is  "bad_dir_trap", a transfer is made  to establish the
bad_dir  condition  handler.   This   code  in  included  by  the
bad_dir_handler macro, which is put  in the source after the last
hgate macro.  When the gate  entry target returns, the hgate code
loads  a pointer to  the entry's internal  static meter data  and
transfers  to the  common return  code.  hgate  also defines  the
internal static space for the entry's meter data.

The code inserted by the bad_dir_handler macro has several parts:
transfers in  the transfer vector  so that signal_  and unwinder_
can invoke  code in the  gate, code labelled  by .set_dir_trap to
establish the  bad_dir on unit,  code labelled by  .handler to be
invoked   as  the   bad_dir   handler,   and  code   labelled  by
.handler_restart_point to which the handler unwinds.


_4_._2_:  _V_a_l_i_d_a_t_i_o_n _L_e_v_e_l

The  validation level can  be thought of  as the logical  ring of
execution.   It  is  a  software  mechanism  used  by  inner ring
programs to determine on which ring's behalf they are acting.  It


                                24
Runtime Environment                                       MDD-020


is used by  ips_, ipc_, ioi_, the linker, the  file system access
mechanism, and the salvager, among others.

Most of the time the validation  level is set to the highest ring
being used in the process since  that is where the user "is".  It
is set to  that value by call_outer_ring_ when the  ring is first
entered.  A lower ring program  can temporarily reduce the level,
but it must  save the old value and restore  it before returning.
There should be a cleanup handler to make sure that the old value
is restored.

It is important that the validation level never be lower than the
executing ring because that would allow the executing ring to get
the TCB  to do things  on behalf of  an inner ring.   In order to
enforce this, the  level number itself is maintained  by the TCB.
It   is  stored   in  pds$validation_level,   which  is  directly
accessible  only  in  ring  zero.   The  only  programs  that are
supposed  to reference  it directly  are the  hardcore procedures
level and  ring_alarm, so that the appropriate  checks can always
be made.  Non-hardcore programs  can call level$set and level$get
via cu_, which is just a writearound to hcs_ in this case.

Because  it  is  so  important  for  the  level  to  be  restored
correctly, and  because the restoration process  is voluntary and
can be bypassed occasionally, it is necessary to have a fail-safe
way  to make sure  that the level  is restored correctly  when an
outer ring is  returned to.  The hardware ring  alarm register is
used for this.  It is  maintained by the procedure ring_alarm and
can be set to fault when  a specified ring is entered from below.
If a  ring_alarm fault occurs,  ring_alarm$fault has a  chance to
fix certain things before the target ring is returned to.  In the
case  of the  validation level,  the pds$ring_alarm_val  array is
used to determine the correct level to restore.

Of course the TCB must make  sure that the ring alarm register is
turned  on whenever the  validation level is  changed to a  value
that would be wrong for a  higher ring.  The actual rule enforced
is more exclusive:  when an inner ring is left for an outer ring,
the  validation level  must be  the  same  as when  the ring  was
entered,  unless the outer  ring is being  entered for the  first
time.  The  validation level -  ring alarm connection  is made by
having level$set call ring_alarm$reset and by having everyone who
wants to change  the level call level$set.  Outside  of ring zero
it is possible to change the level only by calling hcs_$level_set
(via cu_$level_set).

level  and  ring_alarm  communicate  via  the  pds$ring_alarm_val
array.  level  sets the values  in the array  and then ring_alarm
looks at it  to determine what to do.  level$set  makes sure that
the only values in  pds$ring_alarm_val(current_ring) are either 0
or  the validation level  at the time  the ring was  entered.  It
does   this   by   saving   the   old   level   value  only  when


                                25
MDD-020                                       Runtime Environment


ring_alarm_val(current_ring)  is zero  and by  storing zero  only
when  the  new  level  value  is  the  same  as the stored value.
level$set  calls  ring_alarm$reset,  which  sets  the  ring alarm
register to current_ring+1 if pds$ring_alarm_val(current_ring) is
not  zero  and  which  turns  off  the  ring  alarm  register  if
pds$ring_alarm_val(current_ring)  is zero  (unless there  is some
other reason to turn it on).  pds$ring_alarm_val(current_ring) is
always reset to zero when the  current ring is returned from (see
below).  level$set calls ring_alarm$reset whenever the validation
level  is changed,  but really   only needs  to call  it whenever
pds$ring_alarm_val is changed.

If the ring  alarm goes off, or a hardcore  gate return discovers
an impending ring alarm, another entry in ring_alarm is called to
restore the validation level  if necessary.  The validation level
is set in turn to  nonzero values of pds$ring_alarm_val(ring) for
every ring  from ring 0 to  target_ring - 1, thus  ending up with
the  correct  value.   ring_alarm  does  ensure  that  the  final
validation  level is  not less   than the  target ring.   If this
operation  did  indeed  change  the  validation  level, syserr is
optionally called to print a message.

Although level always runs in ring 0, it must know the ring it is
acting  on behalf of.   This is accomplished  by having the  hcs_
entries to level be fgates.  fgates  do not push stack frames, so
PR6  still points  to a  stack frame  in the  calling ring.  This
enables level to get the "real" ring number from PR6.

If level finds that the ring  number argument is invalid it calls
level_error,   which   is   coded   in   PL/I,   to   signal  the
validation_level_error  condition.  This  condition will  usually
make it out to the user ring since it is generally not handled in
lower  rings.  If the  specified ring number  is greater than  7,
level_error  first  calls  syserr  to  print  the  address of the
caller,  since  there  is  likely  to  be  a  bug  and the caller
information  might  otherwise  get   lost  during  the  condition
signalling.

It  is possible  to "call"   an outer  ring without  updating the
validation  level, causing  the outer  ring to  run with  a lower
validation level.  This can be done only by a non-system provided
procedure.   It is  unlikely that  a ring  would want  to give  a
higher  ring such  privilege.  Also   the lower  level might  not
stick.  If the  new ring is ever returned to  with the ring alarm
set,  ring_alarm will  set the  validation level  to the  correct
value.

level is also the keeper of pds$no_audit_ring1_fs_object_ops.  It
is  used by the  audit mechanism to  disable auditing of  certain
file system operations done on behalf of the ring one part of the
TCB.  It is set only by level$set_admin_gate, which can be called
only from rings 0 and  1.  This entrypoint automatically turns on


                                26
Runtime Environment                                       MDD-020


pds$no_audit_ring1_fs_object_ops if  the initial ring  is greater
than one and if active_hardcore_data$audit_ring1_fs_object_ops is
off.   It also  automatically sets   the validation  level to  1.
level$set   turns   off   pds$no_audit_fs_object_ops   when   the
validation level  to be set  is greater than  1.  ring_alarm also
turns off the flag when the target ring is greater than one.


_4_._3_:  _T_h_e _R_i_n_g _A_l_a_r_m _M_e_c_h_a_n_i_s_m

The  hardware ring alarm  register can be  used by the  system to
make  sure that  certain things   are cleaned  up or  checked for
before  a  ring  is  left  for  a  higher  ring.  If the register
contains a nonzero number, and a transfer is made to a ring equal
to or higher than that number,  the hardware signals a ring alarm
fault.  This mechanism is used by level, ips_ and set_privileges.

The  program   ring_alarm  does  all  the   ring  alarm  register
management, including the fault  handling.  The $reset entry uses
information in the pds and apt  to calculate the lowest ring that
needs  the ring alarm  register to be  set.  The $set  entry just
sets  the register to  the specified ring,  with the risk  that a
lower ring might be bypassed.   It is called by proc_int_handler,
which  turns process  interrupts  into  ips signals.   The $fault
entry is the handler for the ring_alarm fault.  It resets certain
pds      fields      such       as      validation_level      and
no_audit_ring1_fs_object_ops to  be correct for the  target ring.
It then "calls"  the reset entry, since another,  higher ring may
now need a ring alarm.  The  $poll entry is similar to the $fault
entry but is  called by the hardcore gates before  they return if
they  detect an impending  ring alarm.  This  not only saves  the
cost of signalling but allows  the opportunity of an orderly call
to pxss (the traffic controller) if the process is supposed to be
preempted or stopped.  Since the ring alarm software is available
only in ring zero, non-hardcore gates cannot use it.


_4_._4_:  _B_r_i_e_f _S_u_m_m_a_r_y _o_f _F_a_u_l_t _H_a_n_d_l_i_n_g

Fault  handling is discussed  in detail in  MDD-021 on Fault  and
Interrupt  Handling,  but  some  explanation  is  helpful  in the
context of this  MDD.  After all, a process does  enter ring zero
when a hardware fault occurs.  The procedure fim (fault intercept
module) is  usually invoked when  there is a  hardware fault.  It
decides which faults should be handled in ring zero and calls the
appropriate handlers for them.  Such faults include link, segment
and  page  faults.   In  these  cases  the  fim saves the machine
conditions as they were when the fault occurred and automatically
restores  them when  the  called  handler returns.   The intended
result  is  to  make  the  fault  service  invisible  to the user
program.  All  faults not specifically  handled in ring  zero, as
well as  faults whose ring zero handlers  encountered errors, are


                                27
MDD-020                                       Runtime Environment


signalled  by the procedure  signaller in the  ring in which  the
fault occurred.  In no case is the fault mechanism intended to be
used explicitly  by non-hardcore programs to  request services of
the supervisor.


_4_._5_:  _E_x_i_t_i_n_g _a _L_o_w_e_r _R_i_n_g

There are  several ways in which  a lower ring may  be left for a
higher  one.  The  most common  one by  far is  a return  from an
inward call through a gate.  However it is also possible to leave
because of an unexpected condition, to execute a nonlocal goto to
a higher ring, and even to  explicitly call out.  All methods use
the  rtcd  (return  control   double)  instruction  to  make  the
transfer,  because that  is the  only instruction  that makes the
ring numbers in all the pointer  registers be equal to the target
ring.  An attempt to run  with pointer register ring numbers less
than  the current ring  would soon lead  to faults.  All  methods
also result in the inner ring's stack history being truncated.


4.5.1:  NORMAL RETURNS

Most of the  time an inner ring is entered by  calling a gate and
left  by a  normal return.   The gate  does a  normal push, which
stores  the value of  PR6 at the  time of the  call in the  stack
frame's prev_sp.  The normal return  copies prev_sp back into PR6
and  calculates PR7.  It  then uses the  return_ptr in the  stack
frame pointed to by the restored  PR6.  There is no stack history
left on the  inner ring stack after the return  since the stack's
first  frame was abandoned.   fgate entries generally  don't push
stack frames, but then they don't change PR6 either.


4.5.2:  CONDITION CRAWLOUTS

The term "crawlout"  is used when a condition  has been signalled
in an inner ring but has  no handlers in that ring.  signal_ then
"crawls  out"  of  the  inner  ring  to  continue  signalling the
condition in  the calling ring.  (Rings between  the current ring
and the ring  that called it have no stack history  and so can be
ignored.)  Before leaving, signal_ creates  a frame for itself on
the next higher ring's stack and "unwinds" the inner ring's stack
by calling unwind_stack_ to invoke any cleanup handlers.  signal_
does not explicitly mask ips signals for the ring being left, but
probably  should.   It   calls  nonlocal_goto_$different_ring  to
actually make the transfer.

nonlocal_goto_$different_ring  is called  with a  pointer to  the
target stack frame and a pointer  to the location to be "returned
to".  It completes the logical truncation of the inner ring stack
by  resetting stack_header.stack_end_ptr,   uses the  stack frame


                                28
Runtime Environment                                       MDD-020


argument to reset PR6 and PR7,  resets the indicators, and does a
rtcd to the specified location.

The  crawlout  mechanism  should  not  be  confused  with the fim
mechanism.  The fim is basically a dispatcher, figuring out where
to  start  signalling.   Crawlouts  occur  after  a condition has
already  been signalled in  an inner ring  but has no  handler in
that ring.  Sometimes  several rings are crawled out  of before a
handler is found.


4.5.3:  TRANSFERS TO LABELS IN HIGHER RINGS

A nonlocal  goto to a  label in a  higher ring works  in much the
same way as a crawlout.  In this case it is unwinder_ rather than
signal_  that creates  a stack  frame for  itself in  the calling
ring's stack and  calls nonlocal_goto_$different_ring.  Of course
in this case the stack being  left will already have been cleaned
up.


4.5.4:  OUTWARD CALLS

The Multics system provides two  mechanisms for making "calls" to
procedures in outer rings:  the older outward_call_handler, which
handles    the   outward_call     condition,   and    the   newer
call_outer_ring_, which avoids the condition.  Neither one can be
used for  arbitrary calls, since there  are several restrictions.
Only a null argument list is passed, and it is created in a fixed
location in the  target ring's stack by the  call mechanism.  The
calling program can  never be returned to because  the inner ring
stack history will be destroyed.  To  enforce this, PR6 is set to
null  before the  actual transfer.   This becomes  prev_sp in the
called program's stack  frame and will cause a fault  if a return
is  attempted.   The  null  prev_sp  also  means  that the called
program's  stack frame  is not   threaded to  any previous  stack
history  in the  target ring;   however that  stack history  does
remain  because  the  called  program  starts  its  frame  at the
location specified by stack_header.stack_end_ptr.

Only one  call out of ring  zero is permitted during  the life of
the process, to call the  initial nonhardcore procedure.  This is
enforced by  the flag pds$first_call.  It is  up to configuration
management  to ensure  that any  ring zero  caller of  rtcd_util_
(used   by    call_outer_ring_)   or   its    equivalent   checks
pds$first_call.   This  first  transfer   out  of  ring  zero  to
bootstrap the  process into the  initial ring is  the main reason
for the existence of the call out mechanism.

Both programs  truncate the stacks  in all the  rings between the
caller (included) and the target (not included), mask ips signals
in  those rings  as well,  and set  the validation  level to  the


                                29
MDD-020                                       Runtime Environment


target ring.   The stack truncation  is done simply  by resetting
stack_header.stack_end_ptr,   without   executing   any   cleanup
handlers.   The truncation has  two consequences that  the reader
should be  aware of.  One is  that after the truncation,  no call
should be made in the current ring to a program that might push a
stack frame.   Although the stack  is logically truncated,  it is
still  being used  for automatic   storage, so  that a  new frame
allocated at the beginning of the stack could destroy part of the
calling program's stack frame.  In  addition, the new frame would
be  back-threaded  to  the  calling  frame,  so  at best the back
threads  would loop, become  garbage, or go  off on a  wild goose
chase.   The  restriction  on  calls  applies  to  the  condition
signalling  mechanism  as  well,  which  is  one  reason that ips
signals are masked at this point.  The other consequence of stack
truncation is that all on-units in the stack are wiped out.  This
especially relevant when a call is  being made out of the initial
ring.  It is up to the first  program called in a new higher ring
to  establish  an  any_other   handler  and  initialize  the  I/O
switches.  Otherwise the signalling of most conditions will cause
the process to terminate because of  either the lack of a handler
or  because  the  error_output  switch  is  unavailable.   If the
initial ring still needs an any_other handler, it will have to be
reestablished each time the ring is entered.

outward_call_handler  and  call_outer_ring_  differ  in  how they
prepare the target environment and affect the registers.


4.5.4.1:  outward_call_handler

outward_call_handler  is invoked  in ring  zero by  the fim  when
there is  an outward_call fault,  i.e., when there  is a non-rtcd
transfer to a higher ring.  It  spends most of its time adjusting
the  fault machine conditions.   PR6 is set  to null, PR7  to the
base  of  the  target  ring's  stack  (calculated  from  the ring
number), and PR0 to the null argument list created just after the
stack header in the target ring's stack.  The ring numbers in all
the pointer registers, as well as the prr and trr, are set to the
target ring.  The saved instructions  are both set to be straight
tra's, since a call instruction would cause the segment number in
PR6 to be  set to the segment number in PR7,  which we don't want
(we  want  PR6  to  stay  null).   It  is  probable  that an rtcd
instruction  could not be  effectively inserted into  the machine
conditions at this  point.  Setting the prr in this  case is what
will cause the process to be running in the target ring.

It is not obvious how the outer ring environment gets set up.  If
the target program  was linked to, the linker  probably found the
target using the calling ring's  search rules.  But then it would
have created  an environment for  the target ring  (if necessary)
and  combined the  linkage section  in the  target ring.   If the
target program was not linked to, its linkage section will not be


                                30
Runtime Environment                                       MDD-020


combined  in the  target ring.    However, the  target stack  and
environment are created,  if they do not already  exist, when the
null argument list is created.  That is when the stack segment is
first  referenced.   seg_fault,  seeing  that  the  segment  is a
nonexistent stack, calls makestack to create and initialize it.


4.5.4.2:  call_outer_ring_

call_outer_ring_ is intended to be  a more desireable way to make
a call to an outer ring.  It must be called intentionally.  It is
given  a name  and invokes  the linker  on it  to ensure that the
target program's environment is properly initialized.  Because it
can run  in any ring from  0 to 5, it  must call the proper  hcs_
interfaces  to  do  things  like  changing  the  validation level
instead of  changing the pds  directly.  The first  four words of
the  target stack are  used for the  null argument list.   Unlike
outward_call_handler_,  call_outer_ring_  allows  calls  to rings
inside the initial ring (e.g.  from ring 2 to ring 3).

However, even call_outer_ring_ has some less than straightforward
features.   If the  target  ring  was uninitialized,  the obscure
seg_fault mechanism is still used  to call makestack.  The linker
does call  makestack explicitly if  necessary but calls  it after
the target stack  must be referenced to obtain  the search rules.
(In  the outward_call_handler  case, the  linker would  have been
invoked before the outward_call fault,  if at all, and thus would
have   used   the   search    rules   of   the   calling   ring.)
call_outer_ring_  establishes an  any_other handler  to terminate
the process if a condition is  signalled while or after the inner
rings are abandoned.  However, as can be seen from the discussion
above  on  the  effects  of  stack  truncation,  just  the act of
signalling  at  this  point  can  cause  the  process to get into
trouble.

The alm program rtcd_util_ is called  to set PR6, PR7, PR0 and to
execute  the rtcd instruction.   It doesn't reset  the indicators
although  it probably  should.  nonlocal_goto_$different_ring  is
not used in  this case because it wants a  pointer to an existing
stack frame in the target  stack, which call_outer_ring_ does not
supply.  Instead, rtcd_util_ is passed  a pointer to the argument
list, which  it knows is at  the base of the  target stack.  This
pointer is used to set PR0 and PR7.  Unlike outward_call_handler,
rtcd_util_ does  not have to  worry about the  effect of changing
the machine state in the  middle of instruction processing, so it
can  use  the  rtcd  instruction  to  reset  the  registers' ring
numbers.







                                31
MDD-020                                       Runtime Environment


4.5.4.3:  User Coded Procedures

It is possible to code and use a non-system procedure to call out
from a nonzero ring without  doing all the steps discussed above.
The basic requirements  are to get an environment  created in the
outer  ring, to  set pointer  registers 0,  6 and  7 (or whatever
registers  would  be  required  there),  and  to  execute an rtcd
instruction.   The stack does  not have to  be truncated nor  the
validation level set to the  outer ring.  However, ignoring these
restrictions will affect the  interaction between the calling and
target rings and should be done only with extreme caution.











































                                32
Runtime Environment                                       MDD-020


_5_:  _L_I_N_K_I_N_G

The way in which linking between procedures is done in Multics is
quite  different from  the methods  used in  most other  systems.
Dynamic  linking is  widely used,   but several  forms of  static
linking are available as well.


_5_._1_:  _W_h_a_t _D_y_n_a_m_i_c _L_i_n_k_i_n_g _I_s

Most  of this  section discusses   dynamic linking,  which is  an
important part  of the Multics  system.  In Multics,  a reference
from one  compilation unit to  another is linked  at runtime when
the reference is first  actually attempted.  The system designers
wanted each program to be executed  in its own segment so that it
could  be shared  and participate  in the  active access  control
mechanism enforced via SDWs.  In Multics, all addressing of other
segments is  done through pointers that  contain segment numbers,
so that the target segment's  SDW can be indirected through.  The
segment numbers of non-hardcore segments are allocated at runtime
as they  are needed, so the  actual linking must also  be done at
runtime.  Conversely, the dynamic linking  function is one of the
ways the system knows to add a segment to the address space.

The  way  this  feature  is  implemented  offers  the  user great
flexibility.  If  a program is  not referenced, it  is not linked
to.  A program can be removed  from the address space and another
substituted  without  changing  any  of  the  other  links in the
referencing  program.  Since  per-user search  rules are  used to
locate procedures, it  is possible for several users  to be using
the same program (not copies) simultaneously and for each user to
be linked to a different set of subroutines.

Dynamic linking also offers savings of both permanent storage use
and  loading  time.   Because  subroutines  can  be shared (i.e.,
executed simultaneously by different  users), it is not necessary
to combine a copy of  a subroutine with each referencing program.
Because  each program  unit is  then smaller,  each is "combined"
into the  process by itself when  it is first referenced  and not
removed unless  explicitly requested.  It and  the subroutines it
calls are usually not recombined again during the login session.

Updating  subroutines   is  easier  because  relinking   is  done
automatically.  Program maintainers do not have to be notified to
relink manually.

Of course  there are disadvantages  as well.  Because  in general
programs  are not  recombined each  time they  are invoked, their
static   variables   (both   internal   and   external)  are  not
reinitialized  automatically each  time.  In  some cases  this is
useful,  as  when  collecting  cumulative  meters.  However, some
programs originally written on  other systems expect their static


                                33
MDD-020                                       Runtime Environment


storage  to be reinitialized.   They must use  special mechanisms
(described below) to get around  the per-process scope of Multics
dynamic linking.  Another possible disadvantage is that the names
of all segments linked to are  added to the process's name space.
People used  to Multics know  not to use  the same name  for more
than one segment,  but others may not.  Again,  the latter either
have  to change the  offending names or  hide them by  doing some
static linking (see the discussion  of the binder and link editor
below).  Still  another disadvantage is that  while programs that
are used often in a process have lower loading overhead, programs
used only once may have higher overhead.


_5_._2_:  _T_h_e _E_f_f_e_c_t _o_f _D_y_n_a_m_i_c _L_i_n_k_i_n_g _o_n _P_r_o_c_e_s_s _S_t_r_u_c_t_u_r_e

Providing the  features mentioned above has  implications for the
object  segment  format  and  requires  certain  process  tables.
Describing these at this point  will make it easier to understand
the more detailed discussion of how linking is actually done.


5.2.1:  LINKING AND THE OBJECT SEGMENT FORMAT

This section describes and justifies  the format in general.  The
reader should refer to  the Multics Programmer's Reference Manual
Appendix  G for  declarations of  the various  structures in  the
object segment.


5.2.1.1:  Linkage Section

The main effect of dynamic linking on object segments is that the
links  themselves  and  the  internal  static  variables  must be
writeable  by anyone  using the  program, while  the rest  of the
segment,  including code,  constants, symbol  table, etc.,  needs
(and should have) only read and  execute access so that it can be
shared.   One obvious  solution would  be to  have the  writeable
stuff in one segment and  the read/execute only stuff in another,
associated segment.   This was actually implemented  at one time,
with the links in a segment called program_name.link, but it tied
up two segment  numbers for each program, which  was too wasteful
even for Multics.  The links  and internal static storage usually
don't  take  up  very  much  space--in  most  processes  all  the
programs' links combined fit into  a single segment.  Also it was
a  pain to  always keep  all the  pieces together.   Thus it  was
decided to implement another solution, which was to put the whole
program, including the links,  into a single read/execute segment
and to make a copy of the  links and static for each process that
executed the program.  The writeable part was put into a separate
section of the program object  segment with its bounds defined in
an object map located at the end of the segment.  The copy of the
object's linkage section was then  combined with the other copied


                                34
Runtime Environment                                       MDD-020


(active)  linkage sections  in a  single segment  in the  process
directory.


5.2.1.2:  Definition Section

Another effect of dynamic linking on the object segment format is
that  all the  names of  both the  outgoing link  targets and the
entrypoints callable from other programs must always be kept with
the program so  that the linker can find them  easily at runtime.
This information is  not writeable and so should  not be included
in  the linkage section.   It also should  be separated from  the
code since it is not directly involved in execution.  Further, it
should be threaded in a way  that enables the linker to peruse it
quickly.   For all  these reasons,  it has  its own  section, the
definition section, which is also defined by the object map.

The entrypoint information used to resolve incoming references is
all threaded together starting at the beginning of the definition
section.  Each  entrypoint's information is called  a definition.
It includes the name, an offset,  and a number (called the class)
indicating what object segment section the offset is relative to.
The  non-writeable information  for  each  outgoing link  is also
stored in the definition section.  Each link has several items of
information threaded together, with the  offset of the first item
in  the unsnapped  link.   Each  link's information  is logically
unconnected   with  either   other  links'   information  or  the
definitions themselves.   The link information includes  the type
of link, the name, if any, of the target, an offset if the actual
target  location is  unnamed, and,   for some  links to  external
variables, the variable's initialization information.


5.2.1.3:  Unsnapped Links

An unsnapped link occupies two words,  the same size as a snapped
link.  Its  main functions are  to cause a  fault when referenced
through  and  to  tell  the  linker  where  to  find  the  target
description,  which is  in the  definition section.   To cause  a
fault, the link has a fault tag 2 instead of a normal pointer ITS
tag.  When locating the target information, the linker starts out
with only a pointer to an active copy of the unsnapped link.  The
link  contains  a  negative  offset  to  the  base of the linkage
section  header,  which  contains  a  pointer  to  the definition
section.   The link  also contains  the offset  in the definition
section of the beginning of the target information.


5.2.1.4:  Obsolete Feature of Definitions in the Linkage Section

For those interested in history  and/or more of the possibilities
of dynamic linking, the following may be of interest.  It used to


                                35
MDD-020                                       Runtime Environment


be possible to have the definitions in the linkage section.  They
were  located after  the links  and found  via an  offset in  the
linkage header.  There  were two main uses for  this feature, but
in both the  linkage section was in a different  segment from the
text  section.   In  one  case,  the  text  section contained the
storage for  external variables, which were  added dynamically as
they  were  first  referenced.   As   a  variable  was  added,  a
definition for it was added to the definition list in the linkage
section.  Needless to say, both  the text and linkage sections in
this case  were writeable.  In  the other case,  the text segment
had only execute  access.  Since the linker would not  be able to
either  find or call  from the segment  unless it could  read the
definitions  and links,  these were  both placed  in the  linkage
section  with read  access.  (Execute-only  procedures were  used
only on the 645.  The  current hardware can't read constants from
the same segment in execute-only mode.)


5.2.1.5:  Historical Note on Separate Static

There is an  option to have the object  segment's internal static
template  defined as a  separate object section  located directly
from  the  object  map.   This   feature  was  invented  for  the
prelinker, which no longer exists.  The goal of the prelinker was
to provide  a preinitialized template  process for the  user with
limited needs.  Overhead was reduced both by not having to do all
the normal process initialization and by sharing several segments
that  are usually  in the   process directory.   When creating  a
template  process, a  driver file  specified the  segments to  be
added to the address space and all the links between the included
segments were presnapped.  As long as an active prelinked process
did not  reference any unsnapped  links, it could  use the shared
copy of  the combined linkage  section.  However, any  attempt to
write  to  the  linkage   section  would  cause  a  copy_on_write
condition, whose  handler would copy the linkage  sections to the
process  directory.  Since  there has  to be  a separate  copy of
internal static for each process,  the shared links feature would
be worthless if  the static were included as part  of the linkage
section.  Thus it was decided  to make internal static a separate
section,  addressed by  a different  pointer, and  combined in  a
separate  segment which  would always  be copied  to the  process
directory.

In  hindsight,  this  problem  could  also  have  been  solved by
addressing internal static as a link to a uniquely-named external
variable,  presnapping  the  link  to  storage  "allocated"  in a
segment  meant  to  be  writeable.   The  main  thing  is for the
translators to  know not to  access it directly  from the linkage
section pointer.

The  prelinker was  never really   used because  it never  really
worked very well.   More recently, more nails were  nailed in its


                                36
Runtime Environment                                       MDD-020


coffin  because creating  template descriptor  segments and  KSTs
from the user ring was a security hole.

Even  though the  original  requirement  for separate  static has
vanished, there  is still a useful  function for it --  to permit
larger amounts of internal static  in bound segments.  The binder
(see below)  refuses to reference  any link beyond  offset 16K in
the  linkage section  because of  addressing limitations.   Since
links are  always addressed through pointer  registers, there are
only fifteen bits left for the  link offset, and the binder can't
start  using index  registers to   increase the  range.  A  bound
segment's   linkage  section   generally  consists   of  all  the
components' internal static concatenated together followed by all
the links, so that a large  amount of static would push the links
out of range.  Addressing the  links and static separately, as is
done  for separate  static, alleviates  this problem.   As in the
case  above,  addressing  internal  static  as  a  uniquely-named
external variable would also have solved this problem.


5.2.2:  THE LINKAGE AND STATIC OFFSET TABLES

The Linkage Offset  Table (LOT) is used by each  program to get a
pointer  to the  user's private   copy of  the program's  linkage
section.   It is also  used by the  dynamic linker to  locate the
linkage sections of target segments quickly.  The Internal Static
Offset Table  (ISOT) is used  similarly when the  program or link
target has a separate static section.

This paragraph  explains the rationale behind  the LOT mechanism.
As mentioned  above, dynamic linking  requires that the  links be
writeable,  while the  ability  to  share programs  with per-user
per-segment active  access control requires that  the code itself
not be  writeable.  The solution for  this was to copy  the links
into a per-process writeable segment.   Thus the code must use an
explicit  segment number  to reference  its links,  i.e., it must
address  the links  through a  pointer register.   Moreover, each
object  segment must have  a pointer directly  to its own  set of
links.   Usually  this  pointer  is  acquired  during  the  entry
processing, where  speed is important.   It is found  in the LOT,
which is an array indexed  by segment number and accessed through
a pointer in the stack header.

The method of accessing the  LOT has two implications.  The first
implication is that having the entry operator index into an array
by  segment number  depends on   the ability  of the  hardware to
obtain  the  segment  number  in  indexable  form  from a pointer
register.   The  original  Multics  hardware  did  not  have this
ability,  so the  entries were  actually in  the linkage sections
themselves  and obtained  the linkage  pointers directly  without
referencing  the  LOT.   (This   required  the  combined  linkage
sections to have execute access.)  The second implication is that


                                37
MDD-020                                       Runtime Environment


the indirect  addressing of the LOT  permits the LOT to  be moved
when it outgrows its allocated  size.  However, moving the LOT is
not always desireable, such as when a process has several control
points, all with stack headers pointing to the same LOT.  The LOT
moving  mechanism,  which  runs  in  ring  0,  doesn't know about
control points  and ignores all stack headers  except the current
one.  Therefore  the control point  manager grows the  LOT to its
maximum size before creating any control points.

The LOT  array consists of one-word entries  that are initialized
to  zero.  There is  one entry for  each segment number.   When a
segment's linkage  section is combined,  a packed pointer  to the
active linkage section is put in the segment's LOT entry.

There is, in addition, a third type of value that a LOT entry may
have.  When  a segment is  initiated, the entry  corresponding to
the assigned segment  number is set to a  special faulting packed
pointer value.  If this value  is referenced through, it causes a
lot_fault   condition  to   be  signalled.    This  feature   was
implemented to support the run  unit mechanism (see below), which
required the  ability to "stack" active linkage  sections so that
programs executing  inside the run  unit could link  to different
targets.   Normally the  address of  a program's  entry point  is
first found  by the linker,  which makes sure  that the program's
linkage section is  combined and ready to use  before the program
is entered.   A run unit,  however, may inherit  from its calling
environment snapped links to segments whose linkage sections have
not  been  recombined.   If  such  programs  are  entered without
benefit of the linker, they will raise a lot_fault condition when
attempting to obtain their linkage pointer.  The system lot_fault
handler then  automatically combines the linkage  section so that
the reference can  be completed.  The reason that  lot faults are
set  for every  segment at  initiate time  is that  the run  unit
mechanism also needs to know  what segments were initiated at the
beginning  of the  run unit.   More recently,  the execution unit
mechanism  invented for C  also makes use  of lot faults  when it
stacks the program's linkage section(s).

Since  initiate will  work only  if it  can find  a LOT  entry to
fault, the maximum size of  the LOT effectively limits the number
of segments  that a process  can initiate.  The  user can control
this   somewhat   by   adjusting   the   stack   header  variable
max_lot_size.  The KST and LOT sizes  can also be adjusted in the
PMF.

The  discussion  above  has  omitted  the  effects  of  the  ring
mechanism.   Both initiate and  the linker generally  affect only
the LOT of the requesting ring,  i.e., the ring of the validation
level.   However,  if  the  segment  is  a  gate, the linker must
combine  the linkage  section and  update the  LOT in  the target
ring, where the entry will actually execute.  Thus a gate segment
has  a lot  fault in  its calling  ring LOT  entry and  a linkage


                                38
Runtime Environment                                       MDD-020


section  pointer in  its executing   ring LOT  entry.  Lot  fault
entries in inner  rings are completely ignored since  they are of
interest only to run units, C execution environments, and control
point management,  all of which run  only in the user  ring.  Lot
fault entries  in the user ring  do not affect the  address space
used by inner rings.

The Internal  Static Offset Table  (ISOT) is similar  to the LOT,
but  only segments  with separate  static actually  use it.  Isot
faults  have been  defined but   are no  longer used.   They were
invented for  the prelinker so  that the static  section would be
copied only when it was to  be modified.  When a segment does not
have separate  static (the usual case), the  segment's ISOT entry
has the same value as the LOT entry.


5.2.3:  NAME SPACE ISSUES

Along with  dynamic linking comes dynamic  name space management.
By name space management I mean the way in which the linker finds
the  segment that  contains the  link's target  entry.  A dynamic
linker must be able to find  the segment quickly -- it can't look
at all  the segments in a  library to find the  right entrypoint.
Therefore  a  link  specifies  a  "segment"  name  as  well as an
entrypoint  name.   This  segment   name  does  not  include  the
containing  directory name  and  is  actually called  a reference
name.  The directories to be  searched for the reference name are
specified by a per-ring per-process list called the search rules.
The search rules can be modified  by the user to force the linker
to  find  the  desired  versions.   Flexibility  is  increased by
allowing  some   directories  to  be  specified   generically  as
keywords,  such  as  working_dir  and  referencing_dir.  (See the
search rule commands in the Multics Commands Manual.)

The search rule commonly  used first, initiated_segments, is very
different from  the others.  Rather than  specifying a directory,
it  specifies the  list of  reference names  that the  linker has
already found in the process.   This list is called the Reference
Name Table  (RNT) and maps  reference names into  segment numbers
and vice versa.  It improves  the linker's performance and allows
the  user  to  bypass  the   other  search  rules  by  explicitly
initiating segments  in order to  get their reference  names into
the RNT.  The effectiveness of the RNT is largely due to the fact
that the same table continues to  be used throughout a process --
usually the only  ways that names are deleted  are by translators
or  occasionally explicitly  by the  user (through  the terminate
command).  The assumption is that many of the reference names are
linked  to more than  once, either because  they are called  from
many programs  or because their associated  segments have several
entrypoints or because they specify frequently used commands (yes
the command processor uses the linker).



                                39
MDD-020                                       Runtime Environment


The persistence of the RNT does  have one disadvantage -- it does
not  allow the  process to  use duplicate  reference names.   The
effect in Multics is that users  tend not to reuse segment names,
except for different versions of the same program.  Of course not
all users like this, and some  have to deal with programs written
on other  systems.  There are some alternatives.   The binder and
link  editor   provide  static  linking  among   their  component
compilation units.  The run command temporarily pushes a new RNT.
These  are described  in a  later subsection  since they  involve
other issues as well.

It  is effectively  not possible   to either  move or  remove the
initiated_segments search rule because  the RNT is always updated
whenever the linker initiates a  segment.  The linker can be told
not to  search the RNT, but  it will not be  able to initiate the
target it does  find if the reference name is  already in the RNT
and associated with a different segment.


5.2.4:  EXTERNAL VARIABLES

Another  aspect of  dynamic  linking  is that  external variables
(variables  shared among  different compilation  units) should be
allocated only if  they are actually used.  In  other words, they
are  also linked  to dynamically  and the  linker itself  creates
(allocates) them.   They are referenced through  special types of
links  (known  as  *system  and  *heap)  that  do  not  specify a
reference   name  and    that  have   initialization  information
associated with them.  If a  referenced variable does not already
exist, the linker creates  (allocates) and initializes it.  Every
link  to   an  external  variable  has   whatever  initialization
information was available at compile time.

This  method of  supporting external  variables has  side effects
that some find -undesirable.  Both the life span of variables and
the  way  they  are  initialized  are  different  from most other
systems.   As  with  other  links,  once  the  links  to external
variables are snapped, they tend to  stay snapped for the rest of
the life of the process.  That means that the link targets -- the
external variables  themselves -- also exist for  the same length
of time.  Generally  the only way to delete  an external variable
is through  an explicit user  command (delete_external_variable).
Since   the   linker   initializes   a   variable  with  whatever
initialization information  is associated with the  variable link
referenced first in  the process, it is important  that the first
reference   be  associated    with  the   correct  initialization
information.

However many  programmers, especially using Fortran  or C, cannot
guarantee  this.   These  languages  expect  the  variables to be
defined (associated with initialization information) in one place
and  then  allocated  and  initialized  before  the  main program


                                40
Runtime Environment                                       MDD-020


begins.  In Multics,  if the variables are defined  in a separate
segment,  the  linker  when  snapping  the  first reference knows
nothing  about the  existence of  the defining  segment.  Multics
does provide several mechanisms for dealing with this situation.

The  Multics  system  programs  avoid  the  above  problems  with
external  variable  initialization  by  referencing  the defining
modules  explicitly.  In  this  case,  the variables  are usually
located in  the internal static  section of the  defining module.
The references are of the form x$y, where x is the reference name
of the  defining module and  y is an  externally defined location
(i.e.   segment x  has a  definition for  y).  The  linker simply
links in the usual way, returning an  error if it can't find x or
y.   A side  effect is  that if  any of  a module's variables are
referenced, they are all "allocated".

Segments  that exist  to  define  external variables  are usually
created by  create_data_segment, though they can  also be created
by alm.   Such segments generally do not  contain executable code
except occasionally some initialization code invoked as a trap at
first reference.  However they are often bound with segments that
do contain executable code.

An interesting consequence of  having external variables "belong"
to a segment  is that if the segment is  terminated (removed from
the address space), the variables  disappear and all the links to
them are  automatically reset.  This allows the  user to redefine
the  variables in  the middle   of the  process independently  of
everything else.  On the other hand, all the variables defined by
the segment are affected; it is not possible just to add one.  In
practice this  feature is usually  used when the  data segment is
statically linked to the referencing  programs in a bound segment
and the whole bound segment is rebound and replaced; in this case
the  variables  are  external  to  the  referencing  programs but
internal to the bound segment.

Of  course,  external  variables  that  live  in  internal static
sections  are limited  in space,  especially if  the segments are
bound.   It  is  possible  to  create  a  definition to a regular
external variable link, thus allowing both explicit references to
the defining  modules and large (255K minus  50 words) variables.
Currently Pascal and the binder  when binding Pascal are the only
translators that  handle such a construct  (for imported/exported
variables).  (References to "imported"  variables must know about
the extra indirection.)


5.2.4.1:  How Allocation Is Implemented

External  variables  are  generally  allocated  in  the process's
all-purpose area  for the referencing ring (the  user free area).
This is  an extensible (multi-segment) area, which  means that if


                                41
MDD-020                                       Runtime Environment


an allocation  doesn't fit into  the remaining free  space, a new
segment  is  added  to  the  area.   This  means  that  the space
available  for  external  variables  is  largely  limited  by the
process  directory quota  and that   each variable  may be  up to
sys_info$max_seg_size minus (size of  area header) in length.  In
addition,  if a  variable requires   a full  segment, a  separate
segment  is  created  for  it  outside  the  area.  Multi-segment
variables may also  be defined (known as very  large common), but
they  are actually  initialized by  vla_manager_ rather  than the
linker and they do not necessarily live in the process directory.
Links to these variables are  usually snapped explicitly when the
referencing program  is entered rather  than at the  actual first
reference in  order to avoid  having to trap  out to vla_manager_
(in the user ring) from ring 0.

The linker must also be able  to find the variables once they are
allocated, so it must keep a table that associates variable names
with  locations.  This is  a linked list  of nodes with  a header
containing  a hash  table.  The   table header  is pointed  to by
stack_header.sys_link_info_ptr.   The header and  node structures
are also allocated in the ring's  user free area and are declared
in system_link_names.incl.pl1.

The  program  responsible  for  creating  and allocating external
variables is set_ext_variable_, which is described below.


5.2.4.1.1:  Historical Note on the Allocation of External
Variables

In  the days  before external   variables were  allocated in  the
extensible user free area, they were mostly allocated in a single
segment  called  stat_  that   had  its  definitions  dynamically
allocated in its linkage section, stat_.link.  The allocation was
actually done  by a program called datmk_.   Originally each link
to an external variable had  a trap-before-link to datmk_ so that
datmk_  would run in  the referencing ring.   Later, when it  was
determined  that datmk_  was the   only program  trapped to,  the
relevant part of it was called  directly by the linker in ring 0.
Along  with  this  change,  external  variables  were  referenced
through  type 6  (create-if-not-found) links  that specified  the
segment name, such as stat_ or cobol_fsb_, directly.  However, if
the specified segment did not  exist it was created automatically
by the linker (in the  process directory).  Needless to say, this
caused some unpleasant suprises.   Finally the present scheme was
implemented  (when the  area mechanism  was changed  to implement
automatically extensible areas).







                                42
Runtime Environment                                       MDD-020


5.2.4.2:  Heap Variables

Heap  variables  are  essentially  the  same  as *system external
variables, but they are allocated in a separate area known as the
heap that has a different lifetime from the user free area.  This
area   has  its   own  segment(s)   in  the   process  directory.
stack_header.heap_header_ptr points to the  heap header, which is
allocated in the  heap area.  The heap header,  in turn, contains
pointers to the area itself as  well as to the heap variable name
list header so that set_ext_variable_ can find them.

Links to heap  variables are just like links  to system variables
except that  they specify a  "segment" class of  *heap instead of
*system.   They are generated  by C and  regenerated by the  link
editor.

The purpose of having *heap variables is to keep their name space
and lifetime tied to the invocation of a [C] main program.  Since
C main programs invoke  other main programs including themselves,
it  is necessary  to be  able to  have several  heaps around at a
time.  The  pointers to the  heap headers are  stacked, since the
main programs do not run concurrently within a process.  The heap
stacking is managed by  the non-hardcore procedure heap_area_mgr_
and  is expected  to be  done at  the time  a C  main program  is
entered.  In order to accomplish this on Multics, C main programs
that use the  heap are expected to be link  edited with a program
called  main_ that  pushes the   heap level  among other  things.
main_ replaces main as  the link-edited module's default external
entry point.  (This is described in more detail below.)

Each heap  has its own  area/segment in the  process directory so
that it can be simply deleted when the C main program returns.

Many Fortran  and even PL/I applications could  also benefit from
using *heap variables and a  link edited main_ program, but these
languages have not yet been adapted for this mechanism.


5.2.5:  TRAPS AT FIRST REFERENCE

Sometimes it  is necessary to  do some initialization  before the
first time a segment is actually referenced in the process.  This
is done for  such things as writing the  segment's segment number
into internal static and  for initiating and initializing related
segments.   This initialization  should  be  done only  after the
system  knows  that  the  segment  is  to  be  used but before it
actually is used, i.e., at dynamic linking time.

How  the  first  reference  trap  procedures  are  identified and
invoked is  discussed in other  subsections.  For now  it is only
necessary to say  that this is done through  the linkage section,
which means  that only object  segments can have  first reference


                                43
MDD-020                                       Runtime Environment


traps and that  the traps are invoked only when  the linker has a
pointer to the target linkage section.  They are not invoked when
simply snapping a link to the  base of the target segment.  Traps
are invoked only  at the time that the target  linkage section is
combined, so in order to rerun  the traps, one must terminate the
segment (uncombining  the linkage but not  necessarily making the
segment unknown) and relink to it.

Gate  segments  cannot  have  first  reference  traps;  the  trap
processor, which runs in the  ring of the validation level, would
fault when trying to access  the segment's linkage section, which
is  combined in  the target  ring.  Also  it is  not possible  to
invoke  first reference traps  when linking in  a ring below  the
validation level (see trap_caller_caller_).


_5_._3_:  _S_t_a_t_i_c _L_i_n_k_i_n_g

Multics does provide some methods of statically linking different
compilation units  to help offset some of  the disadvantages with
dynamic linking.  These problems  include crowding of the address
space, name  space contention and  how to get  external variables
initialized at the right time with the correct initialization.

It is  Multics policy that all translators,  including the binder
and  linkage editor,  that produce  object code  produce standard
object segments  that are directly linkable  and executable.  The
"static  linkers" do  not change  things like  calling sequences,
although  they do  relocations, change  references from  external
links  to  internal  locations   when  possible,  and  produce  a
composite object.  Basically, the  output of static linking looks
the  same as  the output  of compiling  except that  most of  the
object  sections  are  a  series   of  blocks  derived  from  the
corresponding compilation  units.  The system tools  are supposed
to work smoothly on all object segments.

The  static linkers  on Multics  are the  binder and  the linkage
editor.  It is  necessary to have some knowledge of  what they do
in order  to understand what  the dynamic linker  does with their
output.  This  section also describes the  set_fortran_common and
run commands, which provide ways to deal with the problems solved
by static linking without doing static linking.


5.3.1:  THE BIND COMMAND

The  binder was  developed  specifically  for Multics  to collect
object  segments from  input archives   and combine  them into  a
single object segment.  A  separate control file determines which
objects are  chosen to be  components--objects are not  chosen on
the  basis of   being called  by a  main program   or one  of its
descendents.


                                44
Runtime Environment                                       MDD-020


The binder has the following advantages:

ox    The address  space required is reduced  by combining several
     objects into one segment.

ox    Entrypoints can be made internal.

ox    Initializations for external variables are consolidated.

ox    Grouping objects helps to organize the system.

The  output of  the binder  is  a  single segment  object in  the
standard format.  The text section  is a concatenation of all the
component  text  sections.   The  definitions  are organized into
blocks, with one block per component.  Each block has definitions
for the  "segment" names specified  for the component  as well as
definitions  for the  externally visible  entrypoints.  There may
also be definitions for some entrypoints that the binder has made
internal, but these have a  flag instructing the linker to ignore
them.  All  the definitions are  linked together and  in addition
there  is a hash  table for faster  lookup.  The linkage  section
contains the  concatenated static sections of  all the components
plus regenerated  versions of the  links that were  not satisfied
internally.  The symbol section  contains the concatenated symbol
sections except for  the relocation bits plus a  symbol block for
the bound segment  itself that contains the bind  map.  The bound
segment itself  is not relocatable.  The bound  segment format is
described in detail in the Multics Programmer's Reference Manual.


5.3.2:  THE LINKAGE_EDITOR COMMAND

The linkage_editor  command shares many of the  advantages of the
binder but has the following differences/extensions:

ox    The input specifications are all on the command line, rather
     than in a control file.  There is somewhat less control over
     segment names.

ox    "Libraries" can  be specified to  be used in  resolving link
     references.

ox    Most importantly, the output  can be an object multi-segment
     file.  Thus for large applications the inter-segment linking
     and the  choice of external variable  initializations can be
     handled automatically.


5.3.2.1:  Object Multi-Segment Files

Object multi-segment files are described in detail in MTB-711 and
in the Reference  Manual, but some of the  features are mentioned


                                45
MDD-020                                       Runtime Environment


here for completeness.  Basically, an  object MSF is a collection
of  "bound"  object  segments  with  extensions for inter-segment
references.  These extensions are what is interesting here.


5.3.2.1.1:  External Definitions

The way  that external definitions  are handled was  motivated by
two  main  concerns.   First,  retained  external definitions may
exist in any of the component object segments but a caller should
not  have to know  which; the reference  name for any  object MSF
entry should be  that of the MSF itself.  This  leads to the idea
of funneling  all entry address  requests through component  0 to
make life easier for the linker.  Second, a caller's snapped link
must point directly  to the actual desired target  rather than to
component  0 itself.   This  leads  to generating  definitions in
component  0 that  indirect to  their targets  in other component
segments.


5.3.2.1.2:  Inter-Component Links

It  is  necessary  for  procedures  in  the  different  component
segments to be able to reference each other.  Since this requires
pointers containing  segment numbers filled in  at runtime, these
inter-component  references should be  made through links  in the
normal way.   However, since the  only thing not  known about the
targets are the segment numbers, the linker should not have to do
all  its usual  searching.   Therefore  the link  editor produces
partial links, which contain a  target's component number and the
offset  within  the  component  of  the  target  entrypoint.  For
efficiency, most of these links  are snapped when the link-edited
module is entered rather  than during individual faults, although
the linker  does know how to  interpret them.  To make  them more
obvious, unsnapped partial links contain a fault tag 3 instead of
a fault tag 2.


5.3.2.1.3:  External Variable Initialization

One of the things that a link editor must do is to make sure that
the  external variables  referenced (and  defined) in  the output
module are initialized correctly.  Preferably this should be done
within  the  dynamic  linking  mechanism  wherein  a  variable is
allocated and  initialized only when it is  first referenced.  In
an object MSF, several components  could reference a variable and
the link editor has no way  of knowing which component would make
the   first  reference,   so  it   must  somehow   associate  the
initialization  information  with   all  the  referencing  links.
Instead of actually putting a  copy of the initialization in each
referencing  component,  the  link  editor  generates  links with
deferred  initialization.   In  these  links,  the initialization


                                46
Runtime Environment                                       MDD-020


information is replaced by information that enables the linker to
find  the  link  (in  another  component)  that  does contain the
correct initialization.


5.3.2.1.4:  Prelink First Reference Trap

The  components of  an object   MSF could  get themselves  linked
together  in  the  usual  way  if  partial  links  had the normal
fault_tag_2  tags.  However,  since it  is expected  that all the
components  of an  object MSF  will end  up being  initiated, and
since  partial links  are so   trivial to  snap, there  is little
flexibility to  gain and much time  to lose by using  the dynamic
linking faulting mechanism.  Besides,  this is still a statically
linked  situation--links are  only used  because of  the need for
real pointers.   Even if many of  the partial links are  not used
during an invocation, snapping them all at once is more efficient
than taking linkage faults.

This prelinking is accomplished by  having a first reference trap
to msf_prelink_  on component 0.  msf_prelink_  initiates all the
components, combines  their linkage sections if  necessary, looks
for  and  snaps  all  the  partial  links,  and invokes any first
reference  traps for  the other   components (since  that is  the
responsibility of the linkage section combiner).


5.3.3:  ALTERNATIVES TO STATIC LINKING

Multics does provide some ways to get around some of the problems
of dynamic linking without  having to link statically.  Basically
they involve  adjusting the environment  to fit the  links rather
than adjusting the links  themselves.  In practice however, these
facilities have  proved to be  somewhat awkward and  expensive to
use.


5.3.3.1:  set_fortran_common

The  set_fortran_common  command  allocates  and  initializes the
external variables that are defined  in specified modules.  It is
executed  before   the  main  program  begins.    It  allows  the
programmer to not  have to worry about where  the first reference
to a  variable is made, because  the first reference will  not be
doing the initialization.

The disadvantages with this approach  are that the user must know
which modules contain external variable initializations, that the
command must be explicitly invoked  each time the main program is
run,  and that  all the  defined variables  are allocated whether
they are needed or not.



                                47
MDD-020                                       Runtime Environment


5.3.3.2:  Run Units

The run unit facility is implemented  by the run command and run_
subroutine.  Its goal  is to execute a program and  then clean up
the environment so that the  program leaves no lasting effects on
the  process.  In  general, the   parts of  the environment  that
contain temporary changes are kept  separate from the rest of the
process.  Thus a run unit  has its own LOT, ISOT, user_free_area,
and optionally RNT/search  rules.  It does not run  on a separate
stack, so the stack header contents  have to be restored when the
run unit finishes.   Segments that are initiated only  in the run
unit are automatically terminated,  except for segments that have
a flag indicating that their static is perprocess.

The run unit's  LOT is the key to determining  the run unit's use
of the  address space.  When it  is created, the entries  for the
perprocess  static segments  already in  use are  copied from the
calling  environment's  LOT.   The  entries  for  all  the  other
initiated segments are set to lot  faults (see the section on lot
faults  below).  initiate  in ring  zero sets  the segment's  LOT
entry  in  the  ring  of  the  validation  level  to  a lot fault
specifically  so  that  the  run  unit  mechanism  can  tell what
segments were initiated.  LOT faults are used because a reference
through  them  automatically   causes  the  associated  segment's
linkage section  to be combined  (or recombined, as  the case may
be).  At the  end of the run unit, run_ compares  the two LOTs to
determine whether any segments have  been initiated for the first
time  during  the  run  unit.   Of  these,  any perprocess static
segments have  their linkage sections copied back  to the calling
environment while all the other "new" segments are terminated.

Normal dynamic linking is used within  the run unit.  In order to
ensure  that  the  correct  name  bindings  and external variable
initializations are used, the user  can specify an exec_com to be
invoked  before the  main program.   This can  be used  to invoke
set_fortran_common, to  change the search rules  for the duration
of the run  unit, and to initiate segments.   Changing the search
rules, etc.   is only useful when  the run unit has  its own RNT.
(Unfortunately, at the end of the run unit, run_ ignores the fact
that every new  name in the new RNT also  represents an increment
in  a segment's reference  name count in  the KST; the  reference
name counts are not properly decremented.)

There are several major problems with run units.

ox    It is awkward to have to  have an exec_com associated with a
     main program to adjust the environment.

ox    The  run unit mechanism  cannot be used  recursively, mostly
     because it is too hard to track changes to perprocess static
     segments.  (They probably shouldn't  even use the run unit's
     name space.)


                                48
Runtime Environment                                       MDD-020


ox    Run  units cannot be  used in a  process along with  control
     point  management because the  control points share  much of
     the  environment that  run units  diddle.  A  run unit would
     want to affect only the control point that invoked it.


_5_._4_:  _D_e_s_c_r_i_p_t_i_o_n _o_f _t_h_e _D_y_n_a_m_i_c _L_i_n_k_e_r

The  dynamic linker  itself is  relatively straightforward.   The
driving program  is link_snap, which calls fs_search  to find the
target segment, link_man to  combine the target segment's linkage
section, get_defptr_  to search the target  segment's definitions
for  the  entry  name,  and  set_ext_variable_  to  allocate  and
initialize  external  variables.    Normally,  link_snap  returns
either an error  code or with the link  snapped.  Sometimes there
is work to be done in the  ring of the link before control can be
returned  to the  user program.   In this  case, link_snap  calls
trap_caller_caller_ to make the "outward call".


5.4.1:  LINK_SNAP

link_snap  has four entrypoints.   $link_fault is invoked  as the
handler for the fault_tag_2 fault and so is provided with machine
conditions.   The other entrypoints  are called normally  and are
available through  hcs_.  $link_force is  passed a pointer  to an
unsnapped  link.   $make_ptr  and  $make_entry  provide  a way to
invoke the linker  to get a pointer to an  external entry without
having a translator-generated link.

link_snap spends  most of its  time calling other  programs to do
the real work.   Each call is metered and the  results are stored
in ahd  (=active_hardcore_data).  The total time  and page faults
are stored in the PDS.  link_snap snaps the link (or fills in the
return pointer) when it knows  the target location and the target
is initialized/ready to run.

For most  links the processing  is straightforward.  The  link is
decoded, the  target is integrated into the  environment, and the
target  location  is  obtained.   However  indirect  definitions,
partial  links and  deferred initialization  need special mention
because they involve extra link references.

Indirect  definitions were  invented for  the transfer  vector in
component 0 of  object MSFs so that the linker  would not have to
search  the definitions  of all  the components.   They point  to
partial links  to the real target  entrypoints.  Although partial
links  are snapped  by  msf_prelink_  during the  first reference
trap,  the trap does  not occur until  after the location  of the
first  reference target  is obtained  (see the  section on  traps
below).  Since the first reference to an object MSF is through an
indirect definition,  the linker must  be able to  snap a partial


                                49
MDD-020                                       Runtime Environment


link in  this case.  When processing an  indirect definition, the
linker specifically  looks for a  partial link (fault  tag 3) and
then goes ahead and snaps it.   The linker does not invoke itself
recursively  in this case  nor does it  process partial links  in
general.

Snapping a  partial link involves obtaining  the target component
number  from   the  link,  calling   fs_search$same_directory  to
initiate  the segment  with that  number for  a name  in the same
directory  as  the  segment  containing  the indirect definition,
combining the target linkage section,  and adding the offset from
the link to the appropriate section base.

Deferred  initialization also  involves some  extra link chasing.
The  program that  allocates and  initializes external variables,
set_ext_variable_,   expects   a   pointer   to   the  variable's
initialization structure whether it needs the information or not.
The  variable  may  already  have  been  allocated  but link_snap
doesn't know because it doesn't "do" external variables.  Most of
the  time no  extra processing  is involved.   But with  deferred
initialization, link_snap must first find the real initialization
info.  The deferred initialization info only enables link snap to
find   the  link   in  another   component  that   has  the  real
initialization info.

Deferred  initialization for  an external  variable works  in the
following  way.  The  initialization information  associated with
the variable's link includes the offset  of a partial link to the
base  of the  target component's  active linkage  section and the
offset of the variable's link  within the target linkage section.
This  information is  used to  locate the  unsnapped link  in the
template    copy    of    the    linkage    section    (via   the
original_linkage_ptr in the active  linkage section header).  The
unsnapped  link  found  in  this  way  is  threaded  to  the real
initialization info to be  passed to set_ext_variable_.  Thus the
algorithm is  completely independent of  the state of  the active
link that has the real init info.


5.4.2:  FS_SEARCH

fs_search is called when the link type indicates that the link is
to  a  segment  in  the  file  system  hierarchy.  It locates and
intiates  the target segment.   $fs_search is the  entry normally
called; it uses the search  rules.  $same_directory is called for
partial  links; it looks  only for a  segment with the  specified
number-name in  the specified directory.  fs_search  is described
in more detail in MDD-006 on the Multics File System.






                                50
Runtime Environment                                       MDD-020


5.4.3:  LINK_MAN

link_man is in charge of  combining linkage sections and managing
the  LOT.   A  segment's  linkage  section  is  combined when the
segment is first  linked to because the linker needs  to have the
definition  section  pointer  handy  to  search  for  the  target
entrypoint.  Also in  most cases either the segment  will soon be
executing and will need its  linkage section or the target itself
is  in the  combined linkage   section.  Note  that none  of this
applies when the  link type specifies the base of  the segment or
when a null entryname is supplied to link_snap$make_ptr; in these
cases the target's linkage section is not combined.


5.4.3.1:  link_man$other_linkage

The  other_linkage entrypoint  returns  a  pointer to  the active
(combined)  linkage  section  of  the  specified  target segment,
combining it if necessary.  Before link_man can combine a linkage
section, it  must call object_info_$brief to find  the section in
the object  segment.  Obviously if  the segment is  not an object
segment the link  processing stops with an error.   Once the size
of  the linkage/static section  is known, space  for the copy  is
allocated by  the standard system  area package in  the user free
area of the target ring.  After  the copy has been made, pointers
to the object segment's definition section and symbol section are
put in the active linkage header so that the linker won't have to
call object_info_ again.


5.4.3.2:  link_man$own_linkage

The  own_linkage  entrypoint  returns  the  same  information  as
$other_linkage, but assumes that the segment's linkage pointer is
in the  LOT in the ring  of the validation level.   This entry is
only called  when the link  is to the  referencing segment itself
and the linkage section is guaranteed to be combined.


5.4.3.3:  link_man$combine_linkage

This     entrypoint     is     essentially     the     same    as
link_man$other_linkage, but  assumes that the linkage  section is
to be combined in the ring of the validation level and that it is
not already combined.  It is used mainly by lot_fault_handler_.


5.4.3.4:  link_man$grow_lot

This  entrypoint  increases  the  sizes  of  the  LOT and ISOT by
allocating new ones that are stack_header.max_lot_size words long
in the area pointed to by stack_header.clr_ptr in the ring of the


                                51
MDD-020                                       Runtime Environment


validation level.  The old LOT and ISOT are copied but not freed,
since they may not have been allocated in an area.


5.4.3.5:  link_man$get_initial_linkage

This  entrypoint,  along  with  makestack,  initializes  a ring's
environment.


5.4.3.6:  link_man$assign_linkage

This entrypoint simply allocates the specified number of words in
the  area pointed  to by  stack_header.assign_linkage_ptr in  the
ring of  the validation level.   It is obsolete,  a holdover from
the days when  linkage sections were not allocated  in areas.  At
that  time, allocations  made by   this entrypoint  could not  be
freed.


5.4.3.7:  link_man$set_lp

This entrypoint  is obsolete and  apparently no longer  used.  It
fills in  the LOT and ISOT  entries for the specified  segment in
the  ring  of  the  validation   level,  growing  the  tables  if
necessary.


5.4.3.8:  link_man$get_lp

This  entrypoint  is  also  somewhat  obsolete.   It  returns the
linkage pointer  for the specified  segment.  It does  not assume
that the segment number is valid  or that the segment's LOT entry
is filled in.


5.4.4:  GET_DEFPTR_

get_defptr_  searches the  target's external  definitions for the
desired  entry  name.   Since  the  Multics  object format allows
duplicate entry names as long  as they are in different component
blocks,  the  segment  name  from  the  link  is  also  passed to
get_defptr_ in case it is needed to determine the correct block.

If the target has a definition hash table, get_defptr_ uses it to
find  the  entry  name.   If  the  hash  table  indicates  a name
duplication,  get_defptr_ must  then  use  the segment  name hash
table.   If the  segment name  is not  found, the  entry name  is
ambiguous and an error is returned.

If the target  does not have a hash table,  a different algorithm
is  used involving  a linear   search.  First  the segname  class


                                52
Runtime Environment                                       MDD-020


definitions are searched  for the segment name.  If  it is found,
the non-segname class definitions in  that block are searched for
the  entry  name.   If  either   of  these  two  searches  fails,
get_defptr_ must  look at all the  non-segname class definitions.
Even if it finds a definition for the entry name, it must be sure
that there are  no duplicates since the segment  name has already
been found to be useless in resolving ambiguity.


5.4.5:  SET_EXT_VARIABLE_

set_ext_variable_  returns information   about *system  and *heap
external   variables,  allocating    and  initializing   them  if
necessary.  (*system  and *heap variables differ  mainly in where
the variables  and variable name  tables are allocated.)   If the
desired variable is  in the name table and the  allocated size is
adequate, set_ext_variable_ is  done.  (set_ext_variable_ used to
reallocate the variable  if the new size was  larger, but stopped
doing this when  it was discovered that some  programs had stored
the  original  location  in  static.)   Allocating  the  variable
involves allocating and filling in a node for the name table, and
allocating and  initializing the variable itself.   Variables are
allocated in  three ways, depending  on their size.   Either they
are  allocated in  the user  area, or  in a  separate segment, or
fortran_storage_manager_$alloc is  called to obtain  a contiguous
set of segment numbers to use.

set_ext_variable_  has  several  entrypoints  to  control what it
does.  $star_heap causes the allocations  to be done in the heap,
creating  the heap  area (in  a separate  segment) if  necessary.
*heap variables are  never too large to be allocated  in the heap
area.  $for_linker  is callable only in  ring 0 and traps  out to
the user ring if it has to invoke fortran_storage_manager_, which
does not  run in ring  0.  $set_ext_variable_ assumes  that it is
called   in    the   user   ring    and   that   it    can   call
fortran_storage_manager_.   In fact,   the trap  that $for_linker
takes calls  $set_ext_variable_ in the user ring.   (If you can't
get in the back door, go around to the front...)


5.4.6:  LIST_INIT_

list_init_  is  called  by   set_ext_variable_  to  initialize  a
variable  that has  list init  initialization information.   This
type  of  specification  consists  of  a  list  of  fields  to be
interpreted.


5.4.7:  TRAP PROCESSING

Trap  at  first  reference,  trap  before  link  (obsolete),  and
allocatng VLA common require that a specified user ring procedure


                                53
MDD-020                                       Runtime Environment


be run before the link is  snapped and/or used.  Since the linker
in  ring 0  cannot just  call a  procedure in  the user  ring and
expect  to be  returned to,  it must  abandon ring  0, invoke the
specified procedure in the user  ring, and then arrange to return
control  to   the  point  where  it  (the   linker)  was  invoked
originally, with the link snapped.

For  a trap  at first  reference, the  link is  snapped in ring 0
before the trap.  For other types of traps, the trap procedure is
expected to snap the link itself.


5.4.7.1:  trap_caller_caller_

The linker calls  trap_caller_caller_ to get out of ring  0 for a
trap.  The target  ring chosen is either the ring  the link fault
occurred in, if there are machine  conditions, or the ring of the
validation level.  (If  there are any active rings  between 0 and
the validation level, an error is returned.)  trap_caller_caller_
creates a  simulated signaller frame  in the stack  of the target
ring,  copying the  machine conditions,  if any,  and creating an
argument  list for  the trap  calling routine  link_trap_caller_.
trap_caller_caller_  obtains a  pointer to  link_trap_caller_ via
link_snap$make_ptr so  that its linkage section  will be combined
in the  target ring.  signaller$for_linker is  called to complete
the stack frame, exit ring 0, and invoke link_trap_caller_.


5.4.7.2:  link_trap_caller_

link_trap_caller_  actually  invokes  the  trap  procedures.  Its
arguments  include  several  of  the  pointers  that  the  linker
obtained  during its  own processing.    The type  of trap  to be
invoked is  determined by which arguments are  null.  (If another
type  of  trap  is  added,  another  argument  should be added to
specify  the  trap  type.)   When  the  trap  procedure  returns,
link_trap_caller_  must make  its return  look like  the trap was
invisible.   If there  are machine  conditions, link_trap_caller_
simply returns  to the fabricated frame  for restart_fault (built
by signaller),  which restores the machine  conditions.  If there
are  no  machine  conditions,  link_trap_caller_  returns  to the
caller  of the  linker by  doing a  nonlocal goto  to the  return
pointer in the frame preceding that of restart_fault.


5.4.7.3:  How First Reference Traps Get Turned Off

First reference  traps have to be  run only at the  time that the
linkage  section  is  combined,  since  they generally initialize
stuff in the linkage/static section.  This is accomplished in the
following  way.  When  link_man$other_linkage combines  a linkage
section that has a first  reference trap, it carefully leaves the


                                54
Runtime Environment                                       MDD-020


first reference offset in the second word of the combined copy of
the  linkage  section  as  it  fills  in  the  definition section
pointer.  link_snap checks  this offset to see if  it should call
trap_caller_caller_.    link_trap_caller_   zeroes   the   offset
location before invoking the trap procedures.


5.4.8:  ERROR HANDLING

If  there  is  an  error,  the  only  information that the linker
returns  is a code.   A nonzero code  returned during link  fault
processing  causes the  linkage_error condition  to be signalled.
In this case  it may be possible to fix  the problem and restart,
causing the fault to occur again and be processed successfully.

list_init_ does  not have a  code argument, so  to communicate an
error   to  its   caller  (set_ext_variable_)   it  signals   the
malformed_list_template_entry_    condition.    set_ext_variable_
handles   this   condition   by   converting   it   to  the  code
error_table_$malformed_list_template_entry  and doing  a nonlocal
goto  back to  the calling   frame.  This  seems convoluted,  but
list_init_  is also  called  in  situations such  as initializing
automatic  large  arrays  where  signalling  is  the  only way to
indicate an error.

link_trap_caller_ has its own way  of dealing with errors that it
encounters.  If it is called without machine conditions, it has a
pointer to the code argument of the call to the linker.  It fills
in the  code argument and  returns as described  above.  If there
are machine  conditions, it fills  in the code  field and signals
linkage_error itself.   A restart from this  linkage_error causes
link_trap_caller_  to return  to the  restart_fault frame,  which
will restart  the machine conditions.  If a  first reference trap
procedure aborts, unwinding  link_trap_caller_'s stack frame, the
segment being  linked to is  terminated in a  way that uncombines
its linkage  section without releasing the  segment number.  This
enables  the  first  reference  trap  procedure  to be reinvoked.
Since first reference trap procedures do not have code arguments,
the  only way  that they  can indicate  an error  is to  signal a
condition and for link_trap_caller_ to convert the condition into
a code and  do a nonlocal goto.  The only  such condition that is
currently handled is object_msf_damaged_.


5.4.9:  LOT FAULT HANDLING

Sometimes  it  is  necessary  to  recombine  a  segment's linkage
section  without terminating the  segment.  This tends  to happen
when  the  program  needs  fresh  static  storage  whenever it is
invoked, such as  during a run unit.  It is  not enough to simply
zero the segment's LOT entry because the program could be invoked



                                55
MDD-020                                       Runtime Environment


by  an already snapped  link and then  try to access  its linkage
section.

Instead, the LOT entry is set  to a value that causes a lot_fault
condition  to  be  signalled.    The  system  handler  for  this,
lot_fault_handler_,  calls  hcs_$combine_linkage   (an  entry  in
link_man) to combine the linkage and then calls link_trap_caller_
if there are first reference traps.


5.4.10: SECURITY ISSUES

There are three main reasons why the linker is in ring 0.  First,
when linking to an inner ring  gate, the linker must at least run
in the  target ring in order  to be able to  read the definitions
and combine the linkage section.  (Although in ring 0 the linkage
sections are  all pre-combined.)  Second, the  linker itself must
be prelinked to the procedures it calls; this is already done for
everything in ring 0 during  system initialization.  Third, it is
much faster to process search rules in ring 0 because directories
can be located  via segment numbers rather than  via pathnames to
be parsed.

The  extensive use  of data  stored  in  the user  ring is  not a
security  problem  because  the  actual  access  to  segments  is
protected by the hardware ring  mechanism.  The user cannot trick
the linker  into doing something  illegal.  The linker  makes its
own  decisions about what  ring to combine  a linkage section  in
based  on  the  validation  level  and  the  ring brackets of the
target.  link_man actually gets the ring brackets from the SDW of
the target segment.  When locating the target segment, the linker
depends on the file system's access checks.

The linker  runs on behalf of  the ring of the  validation level.
When  there are machine  conditions, the linker  temporarily sets
the validation level to the ring the fault occurred in.  When the
linker is called explicitly, the validation level is not changed.
In that case, it is the responsibility of the caller to make sure
that the validation level is correct.

There is an extra security issue associated with gates.  They are
initiated in  the calling ring  but their linkage  is combined in
the target ring.  For a while, it  was possible to call a gate to
get its  linkage section combined  and then to  terminate it from
the  calling ring,  leaving the  linkage section  combined in the
inner  ring.  Needless  to say,  this was  a security  hole.  The
solution was for link_man to effectively initiate the gate in the
inner ring  by calling segno_usage$increment_other_ring  when the
linkage section  is combined.  This prevents the  gate from being
terminated once it is used.




                                56
Runtime Environment                                       MDD-020


_5_._5_:  _T_h_e _C _E_x_e_c_u_t_i_o_n _E_n_v_i_r_o_n_m_e_n_t

C main programs need much  of the environment initialization done
for run  units, but must also  be able to be  called recursively.
Since they can  be reinvoked directly from command  level after a
condition, they must be able to push a new environment after they
are entered.  This  is taken care of by a  procedure called main_
that is link  edited with the main program and  is the entrypoint
found by the linker when no entryname is specified.

main_ detects that it is  being called recursively by checking an
internal  static  flag.   If  the  flag  is  on, main_ pushes the
environment   of  its  own   link  edited  module   only.   main_
accomplishes this by saving the values  of its LOT entries in its
stack frame, resetting the entries to lot fault, and then calling
hcs_$make_ptr  on itself.   (The call  to hcs_$make_ptr  actually
uses the  link in the  "old" linkage section.)   This linker call
recombines  the linkage  section and  reruns any  first reference
traps.  In its reincarnation after  the call, main_ sees that the
flag  is off  and so  can get  down to  its usual business.  This
involves  pushing the  heap level  (there is  a new  one for each
invocation of a C main program), initializing some I/O variables,
establishing  condition handlers,  and invoking  the "real"  main
program.  When  the earlier incarnation of main_  is returned to,
it pops the heap level, calls  term_ to dispose of the second set
of  linkage  sections,  and  restores  the  LOT  entries to their
previous values.



























                                57
MDD-020                                       Runtime Environment


_6_:  _A_R_E_A _M_A_N_A_G_E_M_E_N_T

Dynamic allocation mechanisms are heavily used in Multics.  There
are two  major kinds--one for directories and  one for everything
else.  There  are also two other kinds--the  obsolete buddy block
mechanism and one used by Pascal.  The Pascal version will not be
discussed in this MDD.

Some  of   the  discussion  in  this   section,  particularly  on
allocation, is derived from MTB-219  by Steve Webber dated 8/8/75
and titled "A New Area Management System".


_6_._1_:  _G_e_n_e_r_a_l _P_u_r_p_o_s_e _A_r_e_a _M_a_n_a_g_e_m_e_n_t

General purpose  areas, implemented by alloc_, are  used for PL/I
language-supported  areas  and   controlled  variables,  combined
linkage sections, non-permanent  external variables, system calls
that  return dynamically  allocated information,  and C's  malloc
routine.  Since it is a  good general purpose mechanism suited to
Multics' virtual memory, alternative mechanisms are discouraged.


6.1.1:  LOGICAL SYSTEM AREAS

Each  non-zero ring  has five  logical perprocess  areas that are
used by the  system.  However, in practice there  is usually only
one  system area per  ring with five  classes of things  that are
allocated in it.  As long as  the allocation of each class uses a
separate  logical  area,  it  is   possible  to  make  the  areas
physically separate for debugging.   Each logical area is located
by its own pointer in the stack header.  There is no need to have
separate areas  because of size considerations,  since the single
area is a multi-segment "extensible" area (see below).


6.1.1.1:  User Free Area

The user free area is used for PL/I allocations when the allocate
statement  does not  have an   "in" clause  (i.e.  PL/I  "system"
storage),  and for   non-permanent external  variables, including
Fortran COMMON.  It is  pointed to by stack_header.user_free_ptr,
which can be changed by the set_user_storage command.


6.1.1.2:  System Free Area

In  general,  system  programs  other  than  the  linker and PL/I
runtime are required to use the  system free area.  It is pointed
to  by  stack_header.system_free_ptr,  but  programs  should  use
get_system_free_area_   to   get   the   pointer.    The  command



                                58
Runtime Environment                                       MDD-020


set_system_storage can be used to change the pointer in the stack
header.


6.1.1.3:  Combined Linkage Area

The  combined  linkage  area  is  used  by  the linker to combine
linkage sections.  It is pointed to by stack_header.clr_ptr.


6.1.1.4:  Combined Static Area

The  combined  static  area  is  used  by  the  linker to combine
separate    static    sections.     It    is    pointed   to   by
stack_header.combined_stat_ptr.


6.1.1.5:  hcs_$assign_linkage Area

hcs_$assign_linkage  uses stack_header.assign_linkage_ptr.   This
pointer is defined to point to  the original area created for the
ring and so must not be changed.  System programs use this rather
than  the system  free area  when they  want to  be sure that all
their allocations are in the same area.


6.1.1.6:  C Heap

The C  Heap (used by  malloc) is not  part of the  general system
area  since it  typically has   a shorter  lifetime.  Usually  it
occupies   a  temporary   segment.   stack_header.heap_header_ptr
points to the  heap header which is itself allocated  in the heap
area.   A process may  have several heaps  at once, one  for each
active C execution unit.  The  heap headers are threaded together
and contain level numbers.


6.1.1.7:  RNT Area

As mentioned  in a previous  section, the nodes  of the reference
name  table  are  allocated  in  a  separate  area that is itself
allocated in the user  free area.  stack_header.rnt_ptr points to
the RNT header which is allocated  in the RNT area and contains a
pointer to the area.


6.1.1.8:  Ring 0's Use of Areas

Ring 0 does not have areas  of its own, but does make allocations
in other rings'  areas.  There are generally two  ways that these
areas are located.   One way is to obtain from  the PDS a pointer
to the current stack for the ring of the validation level (or the


                                59
MDD-020                                       Runtime Environment


link target ring) and then to use one of the area pointers in the
stack header.  The other way is  for the area pointer to be given
as input during a system call.


6.1.2:  FREEING ALLOCATIONS

Generally it is the responsibility  of the program that allocates
space to  also free it.  In  the case of a  program that requests
ring  0 to make  the allocation, the  program making the  request
should free  the space.  Linkage sections  and external variables
usually  remain  allocated  for  the  rest  of  the  process.   A
segment's linkage section is freed  if the segment is terminated.
An external variable is freed if  it is explicitly deleted by the
delete_external_variables command.

Multics  does not automatically  free allocations when  a program
finishes because a  program that is executed becomes  part of the
environment until  it is explicitly removed or  the process ends.
Exceptions to this include run units and C execution units, whose
purpose  is  to  provide   separate  environments  for  executing
programs.  Both of these provide  separate areas which are simply
deleted when  the program finishes.   C execution units  free the
program's linkage section(s) by partially terminating the program
if it was invoked recursively.


6.1.3:  AREA FORMAT

Some familiarlity with the area format will help in understanding
the allocation  algorithm.  Each area consists of  a header, free
or  allocated  blocks  (if  the  area  is  not empty), and unused
storage  following the  last block.   Additionally, an extensible
area may consist of one  or more segments chained together.  Each
segment is itself a complete area.


6.1.3.1:  Area Header Format

This  subsection  will  attempt  to  describe  the  header format
without going into all the  declaration details, etc.  The header
contains the following information:

ox    a version number

ox    the  bounds of  the unused   storage, relative  to the  area
     header

ox    option flags

ox    allocation method  indicator, which can  currently represent
     either standard or no freeing


                                60
Runtime Environment                                       MDD-020


ox    the size  and location of  the last block  before the unused
     storage

ox    the  free list,  which partitions  free blocks  into classes
     according to size

ox    a counter used to detect asynchronous changes

ox    the  offset   of  the  chaining  information   for  extended
     (multi-segment) areas

ox    the number of allocated and free blocks


6.1.3.1.1:  The Free List

The area  header contains the  offsets of fourteen  separate free
lists.  Each list contains the  free blocks whose size is between
2**i and 2**(i+1)-1, where i ranges  from 3 to 16.  The blocks of
each list are threaded together  forward and backward in a circle
with  the free  list header.   When a  block becomes  free it  is
simply  added to  the end  of the  appropriate list,  so that the
blocks within a list are  not ordered according to their location
in the segment.  An empty area contains only empty free lists.

As  an optimization,  the area  header information  for each free
list also  contains the actual size  of the largest block  in the
list.  This is  only calculated when alloc is  searching the list
anyway, so it is not always set.


6.1.3.2:  Area Block Format

Each  block  in  an  area  contains  a  two  word header with the
following information:

ox    the size of the preceding block

ox    the size of the current block

ox    a  field  of  zeroes  to  distinguish  the  block  from  one
     allocated by an obsolete area mechanism

ox    a flag  indicating whether the preceding  block is allocated
     or not

ox    the free list stratum number (when the block is free)

ox    the offset of the base of the area, which is not necessarily
     the beginning of the segment




                                61
MDD-020                                       Runtime Environment


In  addition,  the  headers  of  free  blocks  have an extra word
containing:

ox    the offset of the preceding block in the free list

ox    the offset of the next block in the free list


6.1.3.3:  Area Extend Blocks

Each header  in an extensible  area is immediately  followed by a
pseudo block containing:

ox    a pointer to the first area header

ox    a pointer to the next segment, if any, in the extended area

ox    the sequence number for the component

ox    the name of the owner of the area


6.1.4:  ALGORITHMS USED

Two  basic  allocation  schemes  are  available, distinguished by
whether  or not the  blocks can be  freed.  The scheme  involving
free blocks is by far the most heavily used.


6.1.4.1:  Standard Allocation Method

The  current allocation  scheme uses  a "first  fit" strategy for
finding space, wherein the first free block of sufficient size is
used.  (First means first to be found, not necessarily physically
first.)  The  searching time is  reduced by having  separate free
lists of different  sizes, so that time is not  wasted looking at
free blocks that are too  small.  Having multiple free lists also
improves the  fit, since blocks that  are much too large  are not
used unless nothing closer is  available.  If the block chosen is
too large,  the extra space  is returned to  the appropriate free
list.

This approach  strikes a good balance between  simplicity of code
and  time/space efficiency.  Although  one can probably  devise a
better strategy  for any given application,  this general purpose
scheme has  proved to be  acceptable for most  dynamic allocation
needs on Multics.







                                62
Runtime Environment                                       MDD-020


6.1.4.1.1:  Allocation

First  an attempt  is made  to  find  an existing  free block  of
sufficient  size.  Each  of the  free lists  that contain  blocks
large enough are  searched, in order, until a  large enough block
is found.   This block is then  split into two parts.   The first
part  is returned  to the  caller;  the  second is  added to  the
appropriate free list.

If no  suitable free block  is found, a  block is created  at the
beginning of the unused storage  and the next_virgin field in the
area header is updated.

If there is  no unused storage or if there is  not enough, and if
the header's extend  flag is on, a new area  (segment) is created
and  threaded  to  both  the  current  area  and  the  first area
component through  the extend_info pseudo-block.   The allocation
request is then serviced from the new area.  The previous area is
still available for allocation requests, as long as they fit.

Of course,  any allocation involves  filling in the  block header
and turning on the prev_busy flag  in the header of the following
block.

If the extend flag is not on  or if the allocation won't even fit
in a brand  new area segment, the area (or  storage) condition is
signalled.   Usually it  is area,  but PL/I  requires storage  in
certain  cases; our implementation  indicates this case  by using
the storage_ or op_storage_ entrypoints in alloc_.

The  bad_area_format condition  is  signalled  if the  version or
allocation_method fields in the area  header are invalid, or if a
free  list thread  appears to   go off  into space  (indicated by
reaching a  limit of 40000 on  the number of blocks  "found" in a
free list).


6.1.4.1.2:  Freeing

The  main  job  of  freeing  is  to  thread  the  block  into the
appropriate   free  list.    But  first,   in  order   to  reduce
fragmentation, the block is merged with any adjacent free blocks.
If there  is a merger, the  old blocks are threaded  out of their
free lists and the merged block  threaded in.  If the block being
freed is the last block before the unused storage, it is added to
the  unused  storage.   This  involves  zeroing  the  header  and
updating the next_virgin, last_size  and last_block fields in the
area header.






                                63
MDD-020                                       Runtime Environment


6.1.4.1.3:  Initialization

There are three options for  initializing blocks:  zero on alloc,
zero on free, and no  initialization.  The user free area created
by the system uses zero on  alloc.  Zero on free is not generally
recommended because  it touches all the pages  initially, many of
which will  never be used.  It  is provided as an  aid in certain
debugging situations.


6.1.4.1.4:  Asynchronous Reinvocation Protection

Areas   are  considered   process  entities   (they  contain  ITS
pointers), so the system provides no protection from simultaneous
updates.   (A Multics process  cannot run on  two cpus at  once.)
However, it  is possible for  an asynchronous (IPS)  interrupt to
occur while the area is being  updated, and for another update to
occur  before  the  first  one  is  returned  to.   alloc_ uses a
combination of  detecting a change and  running critical sections
inhibited to keep the area consistent.

IPS  interrupts are  prevented from  occurring while instructions
with the inhibit bit on are  executing, so a section of code that
is inhibited is atomic as far  as the IPS mechanism is concerned.
alloc_ takes advantage of this  feature to do all threading while
inhibited.   Then it  can always   expect to  see a  consistently
threaded area.

However it is not recommended to  run inhibited for very long, so
alloc_ does its searching for  a suitable free block uninhibited.
Thus while the  area itself is always consistent,  it is possible
for  a thread  to change  while alloc_  is following  it.  alloc_
detects this  by saving the  value of a  counter before beginning
the search  and comparing the value  every time it thinks  it has
found a new free block.  If the value of the counter has changed,
alloc_ knows  that the thread it  was following may no  longer be
valid and  that it should begin  the search all over  again.  The
counter,   area_header.allocation_p_clock,   is   incremented  in
inhibited code  before beginning either  an allocation or  a free
operation.

Care must be taken when writing inhibited code.  One should avoid
taking  page faults in  this state.  In  particular, the code  in
alloc_ that zeroes out blocks runs uninhibited.

There is a flaw in the protection mechanism described above.  The
inhibit bit  does not prevent a process  from losing eligibility.
Then before the scheduler restarts the process, it checks for and
signals any pending IPS interrupts.





                                64
Runtime Environment                                       MDD-020


6.1.4.2:  No Freeing Method

Areas whose blocks  are never freed can be  simpler, more compact
and more efficient.  alloc_ simply grabs the requested space from
the  current beginning  of the   unused storage  and updates  the
last_usable and next_virgin fields in  the area header.  There is
no block header because there is  no need to thread blocks.  Thus
the blocks are  contiguous and in the order  of allocation, which
may be useful in certain situations.

If  there  is  no  more  room  in  the  segment  and  the area is
extensible, a new area segment is  created and threaded as in the
freeing case.   In this case, however, alloc_  is interested only
in   the  most  recent   component.   To  simplify   things,  the
next_virgin  and last_usable fields  in the first  area component
always refer to the most recent component.  The current_component
field  in  the  no-freeing  header  provides  the desired segment
number.

No-freeing  areas  with  the  zero_on_alloc  option  have  to  be
entirely  zeroed out  before any  allocations are  made in  them.
When  define_area_  acquires  the  area  segments,  such  as when
extending  an  area,  it  knows  the  segments  are already zero.
However, if a pointer to the area was passed to define_area_ (and
the system flag is off), the entire area is explicitly zeroed.

The no-freeing  method does have some  disadvantages.  No-freeing
areas cannot  be used for PL/I  declared areas nor can  the empty
builtin  function be  used on  them.  They  can very  wasteful of
space  if  used  in  situations  involving  a  lot  of  temporary
allocations.   There   is  no  protection   against  asynchronous
changes.


6.1.5:  MODULES USED

The  standard system  area management  code is  divided into  two
modules:   alloc_, written in  alm, and define_area_,  written in
PL/I.


6.1.5.1:  alloc_

alloc_ is basically an extension  of pl1_operators_.  The way the
operators  transfer to  and return  from alloc_  depend on  their
being  bound into  the same  segment.  Of  course there  are some
regular external entries as well.

alloc_  is used  for operations  that must  run partly inhibited,
including   the    block   threading   operations.     It   calls
get_next_area_ptr_  to extend  the area  to another  segment, and
calls signal_ to signal area or storage.


                                65
MDD-020                                       Runtime Environment


Most  of the  entries in  alloc_ check  the version  field in the
header  for a  version of  zero, which  indicates the  use of the
obsolete  buddy block  format.   To  process that  format, alloc_
calls  the corresponding buddy_XXX_  routine.  This was  done for
compatibility but is no longer necessary.


6.1.5.1.1:  Entrypoints in alloc_

ox    alloc_
     external entry  to allocate a  block of the  specified size;
     signals area

ox    storage_
     same as alloc_ but signals storage

ox    op_alloc_
     segdef for  operators to allocate  a block of  the specified
     size; signals area

ox    op_storage_
     same as op_alloc_ but signals storage

ox    freen_
     external entry to free a specified block

ox    op_freen_
     segdef for operators to free a specified block

ox    area_
     copies         a         header         template        from
     template_area_header$template_area_header into the specified
     segment and initializes the size

ox    extend (called as area_$extend)
     same as area but adds an extend block

ox    no_freeing (called as area_$no_freeing)
     same as area_ but sets  the allocation method to no freeing;
     optionally adds an extend block

ox    op_empty_
     same as area_ except that it is a segdef called by operators
     with  arguments  already  in  registers;  this  is  used  to
     initialize  variables  declared  as  areas,  which cannot be
     extensible

ox    redef (called as area_$redef)
     changes  the  size  of  the  area  by  changing the value of
     area_header.last_usable;  the  new  size  must  include  all
     allocated blocks



                                66
Runtime Environment                                       MDD-020


ox    area_assign_
     replaces one initialized area by copying another initialized
     area; refuses to  assign a buddy area to a  standard area or
     vice  versa  (although  an  uninitialized  buddy  target  is
     allowed).

ox    old_alloc_, old_area_, old_freen_
     identical to the  corresponding "non-old" entrypoints; these
     names were  originally on the  buddy area modules  when they
     were still supported as alternatives


6.1.5.2:  Entrypoints in define_area_

define_area_ is used  for operations that are done  once per area
and for making extension components.


6.1.5.2.1:  define_area_

define_area_  creates and  initializes an  area.  It  is passed a
control structure which can specify  any of the possible options,
and it  can be asked to  create a new segment  for the area.  The
new  area is  first initialized   by the  empty builtin  function
(which calls alloc_$op_empty_  which copies template_area_header)
and then revised with the specified control information.

define_area_'s  actions  are  partly  controlled  by  the  system
option.   If the system  flag is on  and a new  segment is to  be
created, hcs_$make_seg  rather than get_temp_segments is  used to
create the segment in the  process directory.  Also it is assumed
that any system area is already initialized to zero.


6.1.5.2.2:  get_next_area_ptr_

get_next_area_ptr_ is called by alloc_ to obtain a pointer to the
next area component.  If the next component does not exist, it is
created.  If the system flag is  on, the new component is created
in the same directory as the previous component; otherwise a temp
segment (in the  process directory) is used.  In  either case the
ring   brackets   are   copied   from   the  previous  component.
define_area_ is called to initialize the area and then the extend
blocks are threaded together.


6.1.5.2.3:  release_area_

release_area_  deletes  or  releases  all  the  components of the
specified  area that were  created by the  define_area_ interface
and  sets  any  other  components  to  empty  (which usually just



                                67
MDD-020                                       Runtime Environment


reinitializes  the header  without zeroing  pages or truncating).
IPS signals are masked during the entire operation.


6.1.6:  AREA COMMANDS

There  are two  area commands:   create_area, which  is a command
interface   to  define_area_,   and  area_status,   which  prints
information from the area header as well as the free threads.


_6_._2_:  _O_b_s_o_l_e_t_e _B_u_d_d_y _B_l_o_c_k _M_e_c_h_a_n_i_s_m

This  code exists in  the system to  allow areas already  in this
format (presumably on backup tapes)  to be processed.  The system
no longer creates such areas.  Since  this has been true for more
than ten years,  it is now safe to remove  the obsolete code from
the system.  The modules involved are buddy_alloc_, buddy_freen_,
buddy_area_, and buddy_area_assign_.

It is not appropriate to discuss this mechanism in detail in this
MDD, but some knowledge of the  pitfalls could be useful.  In the
"buddy system", block  sizes are powers of two  and an allocation
uses the  smallest size block that contains  the requested amount
of  storage.  Any  leftover space  in the  block is  left unused.
This method does make allocations and frees very fast but it also
has many disadvantages.

First, the fixed block sizes cause a lot of wasted space.

Second, the initialization required limits flexibility.  Since it
is difficult to extend an area  once it has been initialized, the
maximum  size  of  the  area  segment  must  be  specified at the
beginning.  Most of the  pages are touched during initialization,
causing  unnecessary  page  faults,  extra  pages  in the process
directory, and heavier use of large AST entries.  (The usual area
space used is MUCH smaller than the maximum would have to be.)


_6_._3_:  _F_i_l_e _S_y_s_t_e_m _A_r_e_a_s

The  file system  uses a   simple, efficient  area mechanism  for
directory  contents  that  takes   advantage  of  the  fact  that
everything to be allocated belongs to a small set of small sizes.
The header specifies  the number of sizes and the  value of each.
It also contains a free block thread for each size, the number of
words in the area, and the offset of the last word used.

Allocations work in the following way.   If there is a free block
of  the  specified  size,  it  is  used.   Otherwise the block is
allocated from the unused space and  the lu field is updated.  If



                                68
Runtime Environment                                       MDD-020


there  is  not  enough  room  for  the  block,  a null pointer is
returned.

Freeing a block simply entails  threading the block onto the free
list of the specified size.

For  both  allocating  and  freeing,  the  bad_dir_  condition is
signalled  if  the  area  does  not  define  blocks  for the size
specified.

The module that implements this  mechanism is fs_alloc with three
entrypoints:  init,  alloc and free.  The  acceptable block sizes
are specified to the init entrypoint.

This mechanism  with its fixed sizes and  more awkward interfaces
is  not suitable  for general   purpose use,  but is  well enough
suited to  directory control.  The salvager must  also know about
this format,  both to check  the directories' consistency  and to
compact  the directories.   Compaction is  desirable because  the
need for blocks of a particular  size may decrease, leaving a lot
of unused space.  It is  possible because the salvager can figure
out where all  the references to each block are  and can relocate
them.































                                69
MDD-020                                       Runtime Environment


_7_:  _C_O_N_D_I_T_I_O_N _S_I_G_N_A_L_L_I_N_G _A_N_D _H_A_N_D_L_I_N_G

The  Multics  fault/condition/exception  mechanism  is  a general
system  service used  for hardware  faults, Inter-Process  Signal
(IPS)  events,  conditions  defined  by  the  PL/I  language, and
various  software-defined conditions.  It  is based on  the model
defined for the  PL/I language in that it  uses PL/I terminology,
searches  the procedure  activation stack,  and allows restarting
the  operations  that  detected   the  errors.   However  several
extensions  have been made  to support system  requirements.  For
the  most  part  these  extensions  involve  the  ability to pass
additional information when signalling  and the ability to bypass
or alter the stack search.


_7_._1_:  _C_o_n_d_i_t_i_o_n _S_i_g_n_a_l_l_i_n_g

A  single signalling  program, signal_,  is used  for all  cases.
When  an event  is detected,  signal_ is  invoked with  arguments
containing information  about the event.   Its job in  turn is to
find  the appropriate handler  for the event  and to give  it the
information.


7.1.1:  STATIC CONDITIONS

Some events are requests for  special system services which users
cannot  or should not  provide.  Most of  these, such as  segment
faults, are handled in ring  zero directly by the fault intercept
mechanism without being signalled at all.  However those that are
handled in the user ring, such  as IPS timers and lot faults, are
funneled through signal_.  Since these events are not intended to
really  be signalled,  signal_ finds  their handlers  in a static
location  instead of  searching  the  activation stack  for them.
These are called static conditions.

signal_  calls  sct_manager_$call_handler  to  invoke  the static
condition handlers.   Since all static conditions  happen to have
machine   conditions   associated   with   them,   signal_  calls
sct_manager_$call_handler  for all  conditions that  have machine
conditions  and lets  it figure  out whether  or not  there is  a
static handler.  If there is  a static handler for the condition,
sct_manager_  invokes  it.   Static  handlers  generally do their
thing and then return,  eventually causing the associated machine
conditions to be restored (see below).   If there is not a static
handler for the condition,  sct_manager_ returns, telling signal_
to search the stack in the regular way.

A static handler is  established by calling sct_manager_$set with
a pointer to  the handler's entry point and  with the condition's
fault code.  sct_manager_ uses  the SCT (Static Condition Table),
which is indexed by fault code.   (The fault code is contained in


                                70
Runtime Environment                                       MDD-020


the machine conditions.)  The SCT lives in the stack, so there is
one for each nonempty nonzero  ring of each process.  Some static
handlers are  established during process initialization,  but the
user is then  allowed (but not encouraged) to  delete, replace or
add new static handlers.  They can  even be "stacked" in a way by
calling  sct_manager_$get to  obtain  the  old SCT  entry's value
before replacing it, and then having the new handler call the old
one when it is done.


7.1.2:  NORMAL CONDITION SIGNALLING

The  algorithm  that  signal_  uses  for  searching  the stack is
described  in section  7  of  the Multics  Programmer's Reference
Manual.  Briefly, signal_ starts with the stack frame immediately
preceding  its own,  looking for   an on  unit for  the specified
condition.  If one  is found, it is invoked.  If  the stack frame
does not have an on unit for the specific condition name but does
have one for any_other, the any_other handler is invoked.

The  handler has three  options when it  is done.  It  can simply
return, causing signal_ to return to its caller to retry/continue
the  operation; it  can turn   on its  continue flag  argument by
calling continue_to_signal_  and then returning,  causing signal_
to continue  searching the stack; or  it can do a  nonlocal goto,
causing all the  stack frames between it and  the frame specified
by the target label to be unwound.

On units  are implemented as  a linked list  of structures within
the stack frame.   A frame may not have multiple  active on units
for  the same  condition.  The   on unit  structure contains  the
condition name (as pointer and length), a pointer to the handler,
snap  and  system  flags,  and  optionally  a  pointer  to a file
descriptor.

On units  established by PL/I on statements  are initialized when
the stack frame  is created and activated (linked to  the on unit
list)  by the  on statement.   On units  can also  be created  by
calling  condition_, which  extends its  caller's stack  frame in
order to append the on unit.

signal_  must also  do some  additional work  to support  all the
semantics  of the  PL/I condition  mechanism.  Before  invoking a
handler,  signal_  checks  the  on  unit's  snap  bit and invokes
pl1_snap_  if the  bit is  on.  (pl1_snap_  in turn  calls either
trace_stack_  or  probe,  depending  on  whether  the  process is
absentee  or interactive.)  If  the on unit's  system bit is  on,
default_error_handler_$wall_ignore_pi is invoked  as the handler.
Some file-related conditions are  established only in relation to
a specific file,  so a stack frame may have  several on units for
the same condition, each with  respect to a different file (i.e.,
containing a  different file pointer).   In these cases  the file


                                71
MDD-020                                       Runtime Environment


specified  by the on  unit must match  the file specified  by the
information passed to signal_.


7.1.3:  CRAWLOUTS

If signal_ reaches  the beginning of the stack  without finding a
handler  and there  is no  thread  to  a higher  ring stack,  the
process is terminated.  If there  is another stack, signal_ makes
one last effort to get  the condition handled before resignalling
the condition  in the higher  ring.  Some (PL/I)  conditions have
well-defined  default actions  which  the  system is  supposed to
provide.   In the  user ring   they are  provided by  the general
condition  handler established  at  the  beginning of  the stack,
normally  default_error_handler_$wall.  Inner  rings do  not have
such  a  handler  established  because  their  stacks are logical
extensions of the outer  rings' stacks.  However some conditions,
such  as endpage, cannot  be handled correctly  in the user  ring
because they  require access to  inner ring objects.   To provide
the   necessary   services   in   inner   rings,   signal_  calls
crawlout_default_handler_.   If  the  condition  is  then handled
successfully, signal_ returns to  its caller as usual.  Otherwise
(the usual  case) the condition  is resignalled in  the ring that
called the inner ring.  This is called a crawlout.

The crawlout mechanism  is described in the section  on exiting a
lower ring.  During a crawlout, signal_'s arguments are copied to
the outer ring, including the  structures the arguments point to.
In addition, if ring 0 was entered via a fault rather than a gate
call, an attempt  is made to copy the machine  conditions of that
fault as well (if they are  relevant for the target ring).  These
are termed the wall crossing conditions.

Conditions that have been resignalled cannot be restarted because
the inner  ring stack has been truncated.   The copied structures
contain some  information about inner ring  activities but cannot
be used to affect the inner ring.


_7_._2_:  _H_o_w _s_i_g_n_a_l__ _I_s _I_n_v_o_k_e_d

The information  in this subsection  really belongs in  MDD021 on
Fault and Interrupt Handling, but as of this writing that MDD has
not  been  written.   This  subsection  describes  how signal_ is
invoked once  the decision has  been made to  signal a condition.
How the decision is made is beyond the scope of this MDD.

signal_ may  be called just  like any other  subroutine.  In this
case there usually are no  machine conditions, so any information
to be passed  to the handler is put into an  info structure and a
pointer to  it is passed to  signal_.  The structure must  have a
standard  header so  that the  default system  handler can obtain


                                72
Runtime Environment                                       MDD-020


basic  information   from  all  structures.   However   the  info
structure itself is not required.

PL/I  runtime routines that  signal generally invoke  signal_ via
pl1_signal_.   There usually  is  a  common, long  info structure
involved  that   is  best  filled   in  by  one   program.   Also
historically pl1_signal_ had to maintain an obsolete but parallel
mechanism for storing "ondata".

signal_  may also be  invoked in the  faulting ring from  ring 0.
This  happens for  hardware faults  and IPS  interrupts.  In this
case there are usually machine  conditions but no info structure.
The trick is  to get all the necessary arguments  into the proper
ring so that signal_ can reference them.

The  decision  to  signal  is  usually  made  by  the  fim (fault
intercept module), which then calls signaller.  signaller extends
the  most recent  stack frame  in the  faulting ring  in case the
fault occurred while  a new frame was being  constructed and sets
the signaller stack frame flag to indicate this.  It then appends
a  frame  to  that  stack,  copies  into  the  frame  the machine
conditions that the fim stored in pds$signal_data, and constructs
the  argument list  for signal_.   The location  of everything in
that    frame    is    also    known    by    restart_fault   and
trap_caller_caller_.  The frame is made  to look as if it belongs
to return_to_ring_0_, since that program  should be the target of
a return from signal_.

The signaller/return_to_ring_0_ frame also has a cleanup handler.
As long as the condition can  be restarted, a copy of the machine
conditions  is  kept  in  the  pds.   Unwinding  past  the  frame
indicates that the condition is not going to be restarted, so the
cleanup  handler deletes  the copy  in the  pds.  Obviously  this
cleanup    handler    must    run    in    ring    0;    it    is
restart_fault$cleanup_entry, which is a gate.

Transferring  to  signal_   is  not  straightforward.   signaller
obtains a pointer to the  entrypoint to be called from signal_ptr
in  the target stack,  which means that  a user can  substitute a
private program for the system  version of signal_.  As mentioned
in the section on exiting a  lower ring, only an rtcd instruction
may be used,  and the address must be in  the target ring.  Since
the  program being  transferred to  in this  case can  execute in
several  rings,  signaller  must  construct  the  address pointer
carefully  to  contain  the  correct  ring  number.   The  actual
transfer        to        signal_        is        done       via
return_to_ring_0_$return_to_ring_n, which just executes a few nop
instructions to allow time for the ring alarm to go off if it was
set.  The  ring alarm in  turn may cause  an IPS interrupt  to be
signalled.  This  is pushed on  the stack and  handled before the
original condition even gets signalled.



                                73
MDD-020                                       Runtime Environment


7.2.1:  RESTARTING A CONDITION

When a handler returns to  signal_, signal_ simply returns to its
"caller".   When signal_  was called   as a  subroutine, this  is
straightforward.  The caller should  set the cant_restart flag in
the info structure  header if it doesn't want to  be returned to.
It  is then the  responsibility of the  handler to not  return to
signal_.

On the other hand, machine conditions  can be restarted only by a
privileged instruction  in ring 0.   Since it is  not possible to
return  (rtcd) to ring  0, signal_ returns  to return_to_ring_0_,
which   calls   restart_fault$restart_entry.    The   "links"  to
restart_fault  in return_to_ring_0_  and to  return_to_ring_0_ in
signaller are snapped at  system initialization time; the correct
link targets cannot be found by the dynamic linker.

restart_fault is an unusual gate  in that it contains "real code"
and does  not use the gate  macros.  It restarts the  copy of the
machine  conditions that is  in the outer  ring stack, but  first
compares it  to the copy saved  in the PDS.  If  it is discovered
that  the  user  made  any  illegal  changes,  the illegal_return
condition  is signalled  instead.  This  allows the  user to make
some  changes  without  allowing  the  execution  of an arbitrary
instruction  in ring 0,  for example.  Before  actually restoring
the machine conditions,  restart_fault pops the return_to_ring_0_
stack frame and removes the extension and signaller flag from the
previous frame.

Conditions  that have  been signalled  after crawling  out from a
lower ring are never restartable.


_7_._3_:  _C_o_n_d_i_t_i_o_n _H_a_n_d_l_i_n_g

Condition  handling  usually  falls   into  one  of  two  general
categories:  either  the handler knows  how to fix  or ignore the
condition  and  does  so,  or  the  handler  has  to get the user
involved  by  printing  a  message  and  reentering command level
(where  user  interaction  can  take  place).   Most  conditions,
although not  necessarily the conditions signalled  the most, are
in the second category.


7.3.1:  CONDITION WALLS

Before  reentering command  level, it  is common  practice for  a
handler  to  establish  a  condition  wall.   The  condition wall
protects  the programs  with stack  frames before  the wall  from
conditions that  occur afterward.  Theoretically,  the procedures
executing afterwards are not related to those behind the wall, so
conditions raised by them have no meaning in the earlier context.


                                74
Runtime Environment                                       MDD-020


The wall  does allow the program_interrupt  and finish conditions
to  "pass through"  because they   are intended  for the  earlier
procedures.

A  condition wall  is simply   an on  unit for  "any_other".  The
system default handler  establishes one of its entries  as a wall
just  before reentering  command level.   If the  wall handler is
invoked for finish or program_interrupt it just sets the continue
flag and returns.


7.3.2:  SYSTEM DEFAULT CONDITION HANDLING

As  mentioned above, the  system establishes at  the base of  the
user  ring  stack  a  condition  handler  that  is supposed to do
something  reasonable  with  anything   it  gets.   This  section
discusses  that  handler  but  does  not  cover  many  individual
conditions.  The conditions signalled by the system are listed in
the  Multics  Programmer's  Reference   Manual,  along  with  the
structure of the information passed to them.

The default handling described here does not take place in ring 0
and has no real security implications.

Two  collections of  programs are  involved.  One  consists of  a
driver,  default_error_handler_,  and  the  subroutines  that  it
calls.  Together they try to make sense of the information passed
with the condition in the  context of the environment.  The other
collection  is a set  of routines that  try to interpret  various
aspects  of the  environment and   is heavily  used by  the first
collection.  This MDD will not attempt to describe the details of
how all the information for the message is obtained.


7.3.2.1:  default_error_handler_

default_error_handler_  is  the  "main"  program  of  the  system
default condition handler.  Although  it is passed arguments from
signal_,   it    uses   the   more   useful    information   from
find_condition_info_  instead.  default_error_handler_  calls the
other  routines as  necessary and  formats a  message (unless the
default  action is  to return   silently).  When  it is  actually
called as a handler (as opposed  to just being called to format a
message), it  prints the message,  sets itself up  as a condition
wall, and then calls cu_$cl to reenter command level.

default_error_handler_ is more or  less table-driven.  Although a
few conditions are special-cased, there is a table that specifies
for  most conditions  such things   as whether  there is  a valid
target reference  or whether there is extra  interpretation to be
done.  Since it  is impossible to have an  exhaustive list, there
are  default entries  for  both  hardware and  software generated


                                75
MDD-020                                       Runtime Environment


conditions.  The  table also contains the format  strings for the
messages.  In most  cases, default_error_handler_ accumulates the
information  for each  piece of  the message  separately and then
puts the pieces together in a single call to ioa_$rs.

Programs that  raise software conditions can  also take advantage
of  the flags defined  in the standard  info structure header  to
direct  the handler.  The  flags specify such  actions as not  to
restart or to restart without printing a message.

Extra work  is necessary after a crawlout.   The only information
that is available about the  condition itself is whatever signal_
copied out of the inner ring.  The message must include this, but
must  also include  the fact  that  there  was a  crawlout and  a
description of how the user entered the inner ring.

The main goal  of all this interpretation is to  try to present a
message that is meaningful to the user.  Usually this means tying
it into a source program statement in the most recent active user
program,  as  well  as  trying  to  use  the actual segment names
referenced by the user.

default_error_handler_  has to be  aware that the  situation that
caused the  condition may also  have affected the  environment in
other  ways.  Sometimes  a fault  occurs while  trying to  obtain
information  about the  original condition.   To deal  with this,
default_error_handler_  establishes  its  own  any_other handler.
This  handler makes  fewer assumptions   about the  state of  the
environment so it does not try  to obtain as much information for
the message.   In particular, it uses the  arguments from signal_
directly  rather  than   calling  find_condition_info_.   If  the
internal  handler is invoked  twice recursively, it  assumes that
the process is in bad shape and that the iox_$error_output switch
is probably unuseable; it then  terminates the process by calling
terminate_process_.   Some of  the conditions  that the  internal
handler might get are "process control" conditions (such as quit)
unrelated to the original condition.  In these cases the internal
handler simply  returns so that the signalling  can be continued.
(As of this writing the list  of conditions checked for is out of
date.)


7.3.2.1.1:  Entry $wall

This entry handles finish  and program_interrupt, and establishes
a  condition  wall  before   reentering  command  level.   It  is
specified  by  initialize_process_  as  well  as  other  programs
ultimately responsible for handling a process' conditions.






                                76
Runtime Environment                                       MDD-020


7.3.2.1.2:  Entry $standard_default_handler_

This is an obsolete name for default_error_handler_$wall.


7.3.2.1.3:  Entry $default_error_handler_

This  entry handles  finish  and  program_interrupt but  does not
establish a condition wall.  It used  to be the main entry before
the  condition wall  was invented   but is  now obsolete.   A few
programs still specify it.


7.3.2.1.4:  Entry $wall_ignore_pi

This entry does not handle  finish or program interrupt, and does
establish  a  wall.   It  is  specified by default_error_handler_
itself  as the  any_other handler  to implement  the wall.   This
entry is also invoked when an on unit specifies the PL/I "system"
handler.


7.3.2.1.5:  Entry $ignore_pi

This entry does not handle  finish or program_interrupt, and does
not establish a wall.  It  was the predecessor of $wall_ignore_pi
and is no longer used by the system.


7.3.2.1.6:  Entry $condition_interpreter_

This entry is a subroutine interface for other condition handlers
to  use.  It  provides all  the semantics  of the  default system
condition  handler except  that it  does not  resignal the  error
condition  (defined  for  some   PL/I  conditions),  establish  a
condition wall  or reenter command level.  The  error message may
be  either printed  by  condition_interpreter_  or returned  in a
specified  area.  In  cases when  the standard  handler would not
print a message, this entry  returns a null message.  (This entry
is  not always useful.   It would probably  be desirable to  have
another  entrypoint  that  had  a  simpler  interface  and always
returned a message.)


7.3.2.1.7:  Entry $reinterpret_condition_

This entry  is the same as condition_interpreter_  except that it
is  supposed  to  return  a  message  in  lieu  of actually doing
anything to the environment.  The assumption is that it is called
after  the condition  has actually  been handled  but the  caller
wants to see the  message again.  Since reinterpret_condition_ is
not just handling the most recent condition, it can interpret any


                                77
MDD-020                                       Runtime Environment


condition that has active information on the stack; it is given a
pointer to the most recent stack frame of interest.


7.3.2.1.8:  Entry $interpret_condition_

This  entry usually   formats messages  as reinterpret_condition_
does,  but does  not have  the option  of returning  them to  its
caller.  It  is the predecessor of  condition_interpreter_ and is
no longer used by the system.


7.3.2.1.9:  Entry $reprint_error_message_

This entry is the subroutine  interface used by the reprint_error
command.  It  figures out the  stack frames corresponding  to the
specified      condition     depths     and      then     invokes
reinterpret_condition_.


7.3.2.1.10: Entry $change_error_message_mode_

This entry is called by  the change_error_mode command to control
somewhat the verbosity of the messages.


7.3.2.1.11: Entry $add_finish_handler

This entry is now simply a writearound to add_epilogue_handler_.


7.3.2.2:  message_table_

This is a  data segment that contains the format  strings as well
as specific action indicators for each condition.  Any conditions
not named  specifically are covered  by either the  default entry
for conditions  with machine conditions or the  default entry for
conditions without machine conditions.


7.3.2.3:  find_pathname_

This procedure formats the pathname  of a specified location.  It
is  necessary  do  some  interpretation  in  order  to  obtain  a
meaningful name.   For example, if  the directory is  the process
directory,  the string  "in  the  process directory"  is inserted
instead of the  real unique-string name; also if  the location is
in a bound segment, the offset is given relative to the beginning
of the proper component.





                                78
Runtime Environment                                       MDD-020


7.3.2.4:  get_ppr_

This procedure tries to obtain  the last location executed before
the condition.  If  that location is in a  support procedure such
as a language's  runtime routines, the location used  is the most
recently  executed one in  a non-support routine.   get_ppr_ then
tries to  find the exact name  used to invoke the  procedure that
contains  the location.  Since  the procedure's segment  may have
several  names, and since  even the entry  used may have  several
names,  the  surest  way  to  find  the  name  used to invoke the
procedure is to look at the  most recently referenced link in the
procedure's caller when relevant.


7.3.2.5:  get_tpr_

This  procedure  tries  to  obtain  the  location  that was being
referenced   when  the    condition  occurred.    Generally  this
information is available only when there are machine conditions.


7.3.2.6:  interpret_info_struc_

This procedure obtains  whatever part of the message  there is to
obtain from the  info structure (if any) that is  passed with the
condition.


7.3.2.7:  interpret_oncode_

If the condition  is one defined by PL/I and  has an oncode value
associated with it, interpret_info_struc_ calls interpret_oncode_
to  find the message  associated with the  oncode in the  segment
oncode_messages_.


7.3.2.8:  linkage_error_

The  machine   conditions  for  a  link   fault  are  interpreted
differently  from  those  of  any  other  fault.   This procedure
isolates this special case.


7.3.2.9:  special_messages_

This procedure is called in cases where it not possible to obtain
a useful descriptive message from  a table.  For example, one may
need to interpret the machine conditions for disk addresses.






                                79
MDD-020                                       Runtime Environment


7.3.3:  REENTERING COMMAND LEVEL

cu_$cl reenters command level  by calling the program established
as  the  "cl  intermediary",  or  get_to_cl_$unclaimed_signal  by
default.  The main job at this point is to reset the standard I/O
switches to their default values, again because it is likely that
subsequent  procedures do  not want  to run  in exactly  the same
environment  as the  procedure  that  caused the  condition.  The
interfaces cu_$(get set  reset)_cl_intermediary are available for
those who  have to do other  things before the user  can interact
with the  process.  For example, control point  management uses a
cl intermediary that suspends the  current control point in favor
of the main control point.

To   actually   arrive   at   command   level,  get_to_cl_  calls
listen_$release_stack.






































                                80
Runtime Environment                                       MDD-020


_8_:  _S_E_G_M_E_N_T _A_N_D _P_R_O_C_E_S_S _T_E_R_M_I_N_A_T_I_O_N

This section discusses how segments  are removed from the address
space, in whole  or in part, as well as what  a process does when
it terminates itself normally.


_8_._1_:  _S_e_g_m_e_n_t _T_e_r_m_i_n_a_t_i_o_n

Segments may be removed form  the address space dynamically, just
as they  are added dynamically.   However removal is  always done
with  explicit  calls,  unlike   addition  which  is  often  done
implicitly by the linker.  Usually removal is done because either
the  segment was  only added  temporarily, or  it is  about to be
deleted,  and/or  the  segment  is  about  to  be  replaced  by a
different one (such as a different version of a program).

There are hcs_ interfaces for terminating programs, but they only
affect the  ring 0 databases such  as the KST and  the descriptor
segment.  Since there are often  pointers to the segment, such as
snapped links,  in the user  ring that need  to be cleaned  up as
well, it is recommended that segment terminations be done via the
user ring program term_.

term_ has several  entries to do various amounts  of cleaning up.
The  "complete  job"  consists  of  the  following:   The command
processor's  associative memory is  cleared.  All links  that are
snapped to the segment or to its static are unsnapped.  PL/I file
state blocks that reference the segment or its static are closed.
Any  Fortran static VLAs  the segment was  using are freed.   Any
external variable initialization pointers  to the segment are set
to null.   The segment's active  linkage and static  sections are
freed.  The supervisor is called to complete the termination.  If
the segment  is a component of  an object MSF, the  above actions
are automatically repeated for each component.

Some of  the entries in  term_ do all  of the above,  while other
entries are more selective.  For example, term_$no_clear does not
call  find_command_$clear because  it is  called by find_command_
itself.  Some  other entries only  clean up the  user environment
and do not call the hcs_ termination interfaces.

The  hcs_ segment  termination interfaces  start out  by removing
reference  names that  were associated  with the  segment by  the
calling ring.  If no reference  names remain for the segment, the
segment is removed from the address space.  Thus a segment cannot
be removed (except  by deletion) unless each ring  that was using
it terminates it explicitly.






                                81
MDD-020                                       Runtime Environment


_8_._2_:  _P_r_o_c_e_s_s _T_e_r_m_i_n_a_t_i_o_n

The answering service actually terminates the process by deleting
the   process  directory,    removing  the   process'  scheduling
information, and disconnecting the line to the terminal.  However
there often are  things that the process must do  itself, such as
closing  files.   These  things  are  coordinated  by  the logout
command.  Whenever  the process is healthy enough  to do anything
to its environment, process termination is routed through logout.

logout can  be invoked in  several ways.  The  most commonly used
are the logout and  new_proc commands.  sus_signal_handler_ calls
it if  the user does  not have the  option of having  the process
saved  when  the  line   is  suddenly  disconnected.   The  entry
logout$term_signal_handler_ is the (static)  handler for the trm_
IPS condition, which  is signalled when the process  is bumped by
the answering service.

However not all programs that decide to terminate the process use
logout.  The procedure that  actually tells the answering service
to  terminate the  process  is  terminate_process_.  Most  of the
procedures  that  call  terminate_process_  probably  have little
choice but  some, notably the  software that runs  absentee jobs,
should really have an entrypoint in logout to call.

The common code  in logout does the following:   First the finish
condition  is signalled.   Second execute_epilogue_  is called to
invoke the  established epilogue handlers  and then to  close all
the  files (by  calling iox_$close  on all  the IOCBs).   Finally
terminate_process_ is called.

There are  at least two  major deficiences in  the current logout
algorithm.  First, the stack should be unwound (i.e., any cleanup
handlers  executed)  after  the  finish  condition  is signalled.
Second,  logout only cleans  up the user  ring; execute_epilogue_
should be called in inner rings  as well.  (Of course inner rings
do not have any stack history to worry about.)



















                                          -----------------------------------------------------------


Historical Background

This edition of the Multics software materials and documentation is provided and donated
to Massachusetts Institute of Technology by Group BULL including BULL HN Information Systems Inc. 
as a contribution to computer science knowledge.  
This donation is made also to give evidence of the common contributions of Massachusetts Institute of Technology,
Bell Laboratories, General Electric, Honeywell Information Systems Inc., Honeywell BULL Inc., Groupe BULL
and BULL HN Information Systems Inc. to the development of this operating system. 
Multics development was initiated by Massachusetts Institute of Technology Project MAC (1963-1970),
renamed the MIT Laboratory for Computer Science and Artificial Intelligence in the mid 1970s, under the leadership
of Professor Fernando Jose Corbato. Users consider that Multics provided the best software architecture 
for managing computer hardware properly and for executing programs. Many subsequent operating systems 
incorporated Multics principles.
Multics was distributed in 1975 to 2000 by Group Bull in Europe , and in the U.S. by Bull HN Information Systems Inc., 
as successor in interest by change in name only to Honeywell Bull Inc. and Honeywell Information Systems Inc. .

                                          -----------------------------------------------------------

Permission to use, copy, modify, and distribute these programs and their documentation for any purpose and without
fee is hereby granted,provided that the below copyright notice and historical background appear in all copies
and that both the copyright notice and historical background and this permission notice appear in supporting
documentation, and that the names of MIT, HIS, BULL or BULL HN not be used in advertising or publicity pertaining
to distribution of the programs without specific prior written permission.
    Copyright 1972 by Massachusetts Institute of Technology and Honeywell Information Systems Inc.
    Copyright 2006 by BULL HN Information Systems Inc.
    Copyright 2006 by Bull SAS
    All Rights Reserved