?? RIGHT := 110 ??
?? TITLE := 'NOS/VE : Loader : Load library modules' ??
MODULE lom$load_library_modules;

{  PURPOSE:
{    This module contains procedures responsible for loading of modules from object_library files.
{    Knowledge of the structure of object_library files is localized in this module.

  ?VAR
    inline_procs: boolean := TRUE?;

?? NEWTITLE := '  Global declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc llt$deferred_entry_points
*copyc llt$entry_point_dictionary
*copyc llt$load_module_header
*copyc llt$module_dictionary
*copyc llt$object_library_header
*copyc loc$deferred_entry_pt_library
*copyc loc$task_services_library_name
*copyc lot$loader_options
*copyc lot$loader_type_definitions
?? POP ??
*copyc bap$inhibit_implicit_detach
*copyc lop$define_entry_point
*copyc lop$load_module
*copyc lop$open_library
*copyc lop$report_error
*copyc lop$satisfy_task_services_refs
*copyc lov$deferred_entry_points
*copyc lov$head_of_unsat_ref_list
*copyc lov$library_list
*copyc lov$unsatisfied_reference
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] lop$load_library_file', EJECT ??

  PROCEDURE [XDCL] lop$load_library_file
    (    file_descriptor: lot$file_descriptor;
         module_ring_attributes: lot$module_ring_attributes;
         control_options {control} : lot$control_options;
     VAR transfer_descriptor: lot$external_descriptor);

{  PURPOSE:
{    This procedure initiates the loading of every load_module contained on a library file.

    VAR
      library_file: lot$load_file,
      object_text_descriptor: ^llt$object_text_descriptor,
      library_header: ^llt$object_library_header,
      library_hdr: ^llt$object_library_header_v1_0,
      module_dictionary: ^llt$module_dictionary,
      number_of_modules: 0 .. llc$max_modules_in_library,
      library_dictionary: ^llt$object_library_dictionaries,
      j: 0 .. llc$max_dictionaries_on_library,
      i: 0 .. llc$max_modules_in_library,
      module_header: ^llt$load_module_header,
      valid_file_position: boolean,
      module_structure_error: boolean,
      ignore_symbol_table_present: boolean;

    library_file := file_descriptor.segment;
    RESET library_file;
    NEXT library_header IN library_file;
    IF library_header = NIL THEN
      lop$report_error (lle$library_header_missing, file_descriptor.attributes.name, '', 0);
      RETURN
    IFEND;

    IF library_header^.version = llc$object_library_version THEN

      NEXT library_dictionary: [1 .. library_header^.number_of_dictionaries] IN library_file;
      IF library_dictionary = NIL THEN
        lop$report_error (lle$library_header_missing, file_descriptor.attributes.name, '', 0);
        RETURN;
      IFEND;

      number_of_modules := 0;

      FOR j := LOWERBOUND (library_dictionary^) TO UPPERBOUND (library_dictionary^) DO
        CASE library_dictionary^ [j].kind OF
        = llc$module_dictionary =
          module_dictionary := #PTR (library_dictionary^ [j].module_dictionary, library_file^);
          number_of_modules := UPPERBOUND (module_dictionary^);
        ELSE
        CASEND;
      FOREND;

    ELSEIF library_header^.version = 'V1.0' THEN

      RESET library_file;

      NEXT library_hdr IN library_file;
      IF library_hdr = NIL THEN
        lop$report_error (lle$library_header_missing, file_descriptor.attributes.name, '', 0);
        RETURN;
      IFEND;

      number_of_modules := library_hdr^.number_of_modules;
      module_dictionary := #PTR (library_hdr^.module_dictionary, library_file^);

    ELSE
      lop$report_error (lle$wrong_library_version, file_descriptor.attributes.name, '', 0);
      RETURN;
    IFEND;

    IF number_of_modules <> 0 THEN
      IF module_dictionary = NIL THEN
        lop$report_error (lle$bad_module_dictionary_ptr, file_descriptor.attributes.name, '', 0);
        RETURN;
      IFEND;
    ELSE
      lop$report_error (lle$empty_module_dictionary, file_descriptor.attributes.name, '', 0);
      RETURN;
    IFEND;

    FOR i := 1 TO number_of_modules DO
      IF module_dictionary^ [i].kind = llc$load_module THEN
        module_header := #PTR (module_dictionary^ [i].module_header, library_file^);
        IF module_header = NIL THEN
          lop$report_error (lle$bad_module_header_ptr, file_descriptor.attributes.name, 'module', i);
          RETURN
        IFEND;
        object_text_descriptor := #PTR (module_header^.interpretive_element, library_file^);
        IF object_text_descriptor = NIL THEN
          lop$report_error (lle$bad_interpretive_elem_ptr, file_descriptor.attributes.name, '',
                #OFFSET (module_header));
          RETURN
        IFEND;

        RESET library_file TO object_text_descriptor;
        lop$load_module (module_ring_attributes, file_descriptor.attributes, control_options, library_file,
              transfer_descriptor, ignore_symbol_table_present, module_structure_error);
      IFEND;
    FOREND;
  PROCEND lop$load_library_file;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] lop$load_module_list', EJECT ??

  PROCEDURE [XDCL] lop$load_module_list
    (    module_list: {input} ^pmt$module_list;
         initial_ring: ost$ring;
         control_options {control} : lot$control_options;
     VAR transfer_descriptor: lot$external_descriptor);

{  PURPOSE:
{    This procedure initiates loading of every module specified in the module_list of a program
{    load request.  Modules are loaded in the order they appear in the module_list.  For each
{    module, the library_list is scanned in an attempt to locate a library containing the
{    module.  This procedure determines the ring into which module will be loaded.

    VAR
      i: pmt$number_of_modules,
      j: 1 .. llc$max_modules_in_library,
      file_descriptor: lot$file_descriptor,
      library_valid: boolean,
      current_library: ^lot$library_descriptor,
      library_file: lot$load_file,
      library_header: ^llt$object_library_header,
      library_hdr: ^llt$object_library_header_v1_0,
      object_text_descriptor: ^llt$object_text_descriptor,
      module_dictionary: ^llt$module_dictionary,
      number_of_modules: 0 .. llc$max_modules_in_library,
      library_dictionary: ^llt$object_library_dictionaries,
      k: 0 .. llc$max_dictionaries_on_library,
      module_found: boolean,
      module_ring_attributes: lot$module_ring_attributes,
      module_header: ^llt$load_module_header,
      valid_file_position: boolean,
      module_structure_error: boolean,
      ignore_symbol_table_present: boolean;

  /load_a_module/
    FOR i := 1 TO UPPERBOUND (module_list^) DO
      current_library := lov$library_list.first;

    /search_library_list/
      WHILE current_library <> NIL DO
        IF (NOT current_library^.library_valid) OR (current_library^.phantom_library AND
              (NOT current_library^.phantom_library_active)) THEN
          current_library := current_library^.nnext;
          CYCLE /search_library_list/;
        IFEND;

        IF (current_library^.attributes.name <> loc$task_services_library_name) AND
              (current_library^.attributes.name (1, loc$deferred_entry_pt_lib_size) <>
              loc$deferred_entry_pt_library) THEN
          IF NOT current_library^.library_open THEN
            lop$open_library (current_library^.attributes.name, file_descriptor, library_valid);
            IF NOT library_valid THEN
              current_library^.library_valid := FALSE;
              current_library := current_library^.nnext;
              CYCLE /search_library_list/
            ELSE
              IF current_library^.text_embedded_library THEN
                bap$inhibit_implicit_detach (file_descriptor.file_identifier);
              IFEND;
              current_library^.library_open := TRUE;
              current_library^.segment := file_descriptor.segment;
              current_library^.ring_brackets := file_descriptor.ring_brackets;
              current_library^.attributes := file_descriptor.attributes;
            IFEND;
          IFEND;

          IF (initial_ring >= current_library^.ring_brackets.r1) AND
                (initial_ring <= current_library^.ring_brackets.r3) THEN
            library_file := current_library^.segment;
            RESET library_file;
            NEXT library_header IN library_file;

            IF library_header^.version = llc$object_library_version THEN

              NEXT library_dictionary: [1 .. library_header^.number_of_dictionaries] IN library_file;

            /find_module_dictionary/
              FOR k := LOWERBOUND (library_dictionary^) TO UPPERBOUND (library_dictionary^) DO
                CASE library_dictionary^ [k].kind OF
                = llc$module_dictionary =
                  module_dictionary := #PTR (library_dictionary^ [k].module_dictionary, library_file^);
                  number_of_modules := UPPERBOUND (module_dictionary^);
                  EXIT /find_module_dictionary/;
                ELSE
                CASEND;
              FOREND /find_module_dictionary/;

            ELSEIF library_header^.version = 'V1.0' THEN

              RESET library_file;
              NEXT library_hdr IN library_file;
              module_dictionary := #PTR (library_hdr^.module_dictionary, library_file^);
            ELSE
            IFEND;

            module_found := FALSE;

          /search_module_dictionary/
            FOR j := LOWERBOUND (module_dictionary^) TO UPPERBOUND (module_dictionary^) DO
              IF (module_list^ [i] = module_dictionary^ [j].name) AND
                    (module_dictionary^ [j].kind = llc$load_module) THEN
                module_found := TRUE;
                EXIT /search_module_dictionary/
              IFEND;
            FOREND /search_module_dictionary/;
            IF module_found THEN
              IF initial_ring >= current_library^.ring_brackets.r2 THEN
                module_ring_attributes.loaded_ring := current_library^.ring_brackets.r2;
                module_ring_attributes.call_bracket := current_library^.ring_brackets.r3;
              ELSE
                module_ring_attributes.loaded_ring := initial_ring;
                module_ring_attributes.call_bracket := initial_ring;
              IFEND;
              module_header := #PTR (module_dictionary^ [j].module_header, library_file^);
              IF module_header = NIL THEN
                lop$report_error (lle$bad_module_header_ptr, current_library^.attributes.name, 'module', j);
                CYCLE /load_a_module/
              IFEND;
              object_text_descriptor := #PTR (module_header^.interpretive_element, library_file^);
              IF object_text_descriptor = NIL THEN
                lop$report_error (lle$bad_interpretive_elem_ptr, current_library^.attributes.name, '',
                      #OFFSET (module_header));
                CYCLE /load_a_module/
              IFEND;

              RESET library_file TO object_text_descriptor;

              lop$load_module (module_ring_attributes, current_library^.attributes, control_options,
                    library_file, transfer_descriptor, ignore_symbol_table_present, module_structure_error);
              CYCLE /load_a_module/
            IFEND;
          IFEND;
        IFEND;
        current_library := current_library^.nnext;
      WHILEND /search_library_list/;
      lop$report_error (lle$module_not_found, module_list^ [i], '', 0);
    FOREND /load_a_module/;

  PROCEND lop$load_module_list;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] lop$search_entry_pt_dictionary', EJECT ??

  PROCEDURE [XDCL] lop$search_entry_pt_dictionary
    (    external_name: {input} ^pmt$program_name;
         entry_point_dictionary: {input} ^llt$entry_point_dictionary;
     VAR entry_point_found {control} : boolean;
     VAR entry_point_gated {control} : boolean;
     VAR dictionary_index: 1 .. llc$max_entry_points_in_library);

{!  A search which took advantage of the fact that external names are received in lexical order
{!  might prove faster.  Also, selecting either a binary search or an ordered search, depending on
{!  the number of entries to be scanned, looks promising.  Remembering the value of lower between
{!  successive calls (for the same library) would improve performance.  Note that successive
{!  calls may be for the same external name.

    VAR
      temp: integer,
      lower: 1 .. llc$max_entry_points_in_library,
      upper: 0 .. llc$max_entry_points_in_library;

    IF entry_point_dictionary <> NIL THEN
      lower := 1;
      upper := UPPERBOUND (entry_point_dictionary^);

    /binary_search/
      WHILE lower <= upper DO
        temp  := lower + upper;
        dictionary_index := temp DIV 2;
        IF external_name^ = entry_point_dictionary^ [dictionary_index].name THEN
          IF entry_point_dictionary^ [dictionary_index].module_kind = llc$load_module THEN
            entry_point_found := TRUE;
            entry_point_gated := (entry_point_dictionary^ [dictionary_index].kind = llc$gate);
            RETURN
          ELSE
            EXIT /binary_search/
          IFEND;
        ELSE
          IF external_name^ > entry_point_dictionary^ [dictionary_index].name THEN
            lower := dictionary_index + 1;
          ELSE
            upper := dictionary_index - 1;
          IFEND;
        IFEND;
      WHILEND /binary_search/;
    IFEND;

    entry_point_found := FALSE;

  PROCEND lop$search_entry_pt_dictionary;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] lop$satisfy_externals', EJECT ??

  PROCEDURE [XDCL] lop$satisfy_externals
    (    control_options {control} : lot$control_options);

{  PURPOSE:
{    This procedure initiates loading of modules (from libraries) in order to satisfy outstanding
{    external references.  As modules are loaded, new libraries may be added to the library_list
{    and unsatisfied external references may be created, so the list of unsatisfied references is scanned
{    repetitively until no more references can be satisfied.  This procedure determines the ring into
{    which each module will be loaded.
{  NOTE:
{    Due to locality considerations, the outermost loop of this procedure scans the library_list
{    rather than the unsatisfied references list.  For each library in the list, each unsatisfied
{    reference is examined to determine if it can be satisfied by loading a module from the library.
{    The procedure terminates when a scan of the entire library_list is completed without loading
{    any modules.
{    This procedure utilizes an external procedure (which understands the data structure used to store
{    unsatisfied references) to locate all unsatisfied references.  The external procedure is passed
{    a pointer to an internal procedure which is called to process individual unsatisfied references
{    as they are located by the external procedure.
{    When this procedure initiates the loading of a module which should satisfy an external
{    reference, the reference is marked as 'logically_satisfied' and will be ignored on subsequent
{    passes.  This prevents a loop in the case where the external reference is not satisfied
{    (and thereby removed from the unsatisfied list) due to some recoverable error in loading
{    the module.

?? NEWTITLE := 'satisfy_deferred_references', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to determine if any unsatisfied
{   external references can be satisfied by deferred entry points.  If a
{   deferred entry point can satisfy some external reference, then the
{   deferred entry point will be defined in the tasks current symbol table.

    PROCEDURE satisfy_deferred_references
      (    control_options: lot$control_options;
       VAR unsatisfied_reference: ^lot$unsatisfied_reference_list;
       VAR another_scan_required: boolean);

?? NEWTITLE := '[INLINE] find_deferred_entry_point', EJECT ??

{ PURPOSE:
{   This procedure does a binary search looking for an entry point name in
{   a list of deferred entry points.

      PROCEDURE [INLINE] find_deferred_entry_point
        (    deferred_entry_points: {input} ^llt$deferred_entry_points;
             linkage_name: {input} ^pmt$program_name;
         VAR entry_point_found {control} : boolean;
         VAR entry_point_index: 0 .. llc$max_deferred_entry_points);

        VAR
          temp: integer,
          lower: integer,
          upper: integer;

        entry_point_found := FALSE;
        lower := LOWERBOUND (deferred_entry_points^);
        upper := UPPERBOUND (deferred_entry_points^);

        WHILE (NOT entry_point_found) AND (lower <= upper) DO
          temp  := lower + upper;
          entry_point_index := temp  DIV 2;
          IF linkage_name^ = deferred_entry_points^ [entry_point_index].name THEN
            entry_point_found := TRUE;
          ELSEIF linkage_name^ > deferred_entry_points^ [entry_point_index].name THEN
            lower := entry_point_index + 1;
          ELSE
            upper := entry_point_index - 1;
          IFEND;
        WHILEND;
      PROCEND find_deferred_entry_point;
?? OLDTITLE ??
?? NEWTITLE := '[INLINE] search_deferred_entry_defs', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to satisfy any unsatisfied references from a
{   deferred entry point library.

      PROCEDURE [INLINE] search_deferred_entry_defs
        (    deferred_entry_points: ^llt$deferred_entry_points);

        VAR
          duplicate_entry_point: boolean,
          entry_point_found: boolean,
          entry_point_index: 0 .. llc$max_deferred_entry_points,
          library_lock: ost$key_lock_value,
          linkage: ^lot$linkage_name_lists,
          reference_group: ^lot$unsatisfied_reference_group,
          reference_group_chain: ^lot$unsatisfied_reference_group,
          references_list_head: ^^lot$unsatisfied_reference_group,
          pseudo_allocated_sections: array [0 .. 1] of lot$section_allocation,
          pseudo_control_options: lot$control_options,
          pseudo_entry_definition: llt$entry_definition,
          pseudo_module_descriptor: lot$module_descriptor;

        linkage := unsatisfied_reference^.linkage_info;

        references_list_head := ^linkage^.unsat_references_list^.references;
        reference_group := references_list_head^;

        IF current_library^.attributes.key_lock.global THEN
          library_lock := current_library^.attributes.key_lock.value;
        ELSE
          library_lock := loc$no_lock;
        IFEND;

        find_deferred_entry_point (deferred_entry_points, ^linkage^.name, entry_point_found,
              entry_point_index);

        IF entry_point_found THEN
          pseudo_entry_definition.section_ordinal := 0;
          pseudo_entry_definition.offset := 0;
          pseudo_entry_definition.attributes := deferred_entry_points^ [entry_point_index].attributes;
          pseudo_entry_definition.name := deferred_entry_points^ [entry_point_index].name;
          pseudo_entry_definition.language := deferred_entry_points^ [entry_point_index].language;
          pseudo_entry_definition.declaration_matching_required :=
                deferred_entry_points^ [entry_point_index].declaration_matching_required;
          pseudo_entry_definition.declaration_matching := deferred_entry_points^ [entry_point_index].
                declaration_matching_value;

          pseudo_module_descriptor.name := 'DEFERRED_ENTRY_POINT';
          pseudo_module_descriptor.attributes.global_key_lock := current_library^.attributes.key_lock.value;
          pseudo_module_descriptor.attributes.binding_section_address.ring :=
                deferred_entry_points^ [entry_point_index].binding_section_address.ring;
          pseudo_module_descriptor.attributes.binding_section_address.segment :=
                deferred_entry_points^ [entry_point_index].binding_section_address.segment;
          pseudo_module_descriptor.attributes.binding_section_address.offset :=
                deferred_entry_points^ [entry_point_index].binding_section_address.offset;
          pseudo_module_descriptor.attributes.vmid := osc$cyber_180_mode;
          pseudo_module_descriptor.attributes.source_declaration_matching :=
                deferred_entry_points^ [entry_point_index].source_type_checking;

          pseudo_allocated_sections [0].kind := llc$code_section;
          pseudo_allocated_sections [0].address.ring := deferred_entry_points^ [entry_point_index].address.
                ring;
          pseudo_allocated_sections [0].address.segment := deferred_entry_points^ [entry_point_index].address.
                segment;
          pseudo_allocated_sections [0].address.offset := deferred_entry_points^ [entry_point_index].address.
                offset;
          pseudo_allocated_sections [0].binding_section_offset := 0;
          pseudo_allocated_sections [0].length := osc$maximum_offset;
          pseudo_allocated_sections [1].length := osc$maximum_offset;

          pseudo_control_options.map := control_options.map * $pmt$load_map_options [pmc$entry_point_xref];
          pseudo_control_options.debug_ring := control_options.debug_ring;
        IFEND;

      /scan_reference_group_list/
        WHILE reference_group <> NIL DO
          IF reference_group^.logically_satisfied THEN
            reference_group := reference_group^.nnext;
            CYCLE /scan_reference_group_list/
          IFEND;
          IF reference_group^.newly_created THEN
            reference_group^.newly_created := FALSE;
            reference_group_chain := reference_group^.nnext;
            WHILE reference_group_chain <> NIL DO
              reference_group_chain^.newly_created := FALSE;
              reference_group_chain := reference_group_chain^.nnext;
            WHILEND;
            reference_group := reference_group^.nnext;
            unsatisfied_reference^.library_searched := 0;
            another_scan_required := TRUE;
            CYCLE /scan_reference_group_list/
          IFEND;
          IF NOT entry_point_found THEN
            reference_group := reference_group^.nnext;
            CYCLE /scan_reference_group_list/
          IFEND;
          IF (reference_group^.ring >= current_library^.ring_brackets.r1) AND
                (reference_group^.ring <= current_library^.ring_brackets.r3) AND
                ((reference_group^.global_key = library_lock) OR (library_lock = loc$no_lock) OR
                (reference_group^.global_key = loc$master_key)) AND
                ((llc$gated_entry_point IN deferred_entry_points^ [entry_point_index].attributes) OR
                (reference_group^.ring <= current_library^.ring_brackets.r2)) THEN
            IF reference_group^.ring >= current_library^.ring_brackets.r2 THEN
              pseudo_module_descriptor.attributes.loaded_ring := current_library^.ring_brackets.r2;
              pseudo_module_descriptor.attributes.call_bracket := current_library^.ring_brackets.r3;
            ELSE
              pseudo_module_descriptor.attributes.loaded_ring := reference_group^.ring;
              pseudo_module_descriptor.attributes.call_bracket := reference_group^.ring;
            IFEND;

            unsatisfied_reference := unsatisfied_reference^.b_link;

            lop$define_entry_point (^pseudo_entry_definition, ^pseudo_module_descriptor,
                  ^pseudo_allocated_sections, pseudo_control_options,
                  current_library^.attributes.load_file_number, duplicate_entry_point);

            IF linkage^.unsat_references_list = NIL THEN
              RETURN;
            ELSEIF duplicate_entry_point THEN
              unsatisfied_reference := unsatisfied_reference^.f_link;
              RETURN;
            ELSE
              unsatisfied_reference := unsatisfied_reference^.f_link;
              reference_group := linkage^.unsat_references_list^.references;
            IFEND;

          ELSE
            reference_group := reference_group^.nnext;
          IFEND;
        WHILEND /scan_reference_group_list/;
      PROCEND search_deferred_entry_defs;
?? OLDTITLE, EJECT ??

      VAR
        deferred_entry_points: ^lot$deferred_entry_points,
        entry_found: boolean;

      entry_found := FALSE;
      deferred_entry_points := lov$deferred_entry_points;
      WHILE (NOT entry_found) AND (deferred_entry_points <> NIL) DO
        IF #SEGMENT (deferred_entry_points^.deferred_entry_points) = #SEGMENT (current_library^.segment) THEN
          entry_found := TRUE;
        ELSE
          deferred_entry_points := deferred_entry_points^.link;
        IFEND;
      WHILEND;

      IF entry_found THEN
        WHILE unsatisfied_reference <> lov$head_of_unsat_ref_list DO
          search_deferred_entry_defs (deferred_entry_points^.deferred_entry_points);
          unsatisfied_reference := unsatisfied_reference^.f_link;
        WHILEND;
      IFEND;
    PROCEND satisfy_deferred_references;
?? OLDTITLE ??
?? NEWTITLE := 'satisfy_from_current_library', EJECT ??

    ?IF inline_procs = TRUE THEN

      PROCEDURE [INLINE] satisfy_from_current_library;

    ?ELSE

      PROCEDURE satisfy_from_current_library;

    ?IFEND

{  PURPOSE:
{    For a particular (library, external name) pair, this procedure determines if any unsatisfied
{    references to the external name can be satisfied by loading a module from the library.  If so,
{    then loading of the module is initiated.
{  NOTE:
{    The identity of the library to be examined is communicated thru the static chain.

    VAR
      references_list_head: ^^lot$unsatisfied_reference_group,
      reference_group: ^lot$unsatisfied_reference_group,
      reference_group_chain: ^lot$unsatisfied_reference_group,
      library_lock: ost$key_lock_value,
      library_file: lot$load_file,
      library_header: ^llt$object_library_header,
      library_hdr: ^llt$object_library_header_v1_0,
      linkage: ^lot$linkage_name_lists,
      object_text_descriptor: ^llt$object_text_descriptor,
      entry_point_dictionary: ^llt$entry_point_dictionary,
      number_of_entry_points: 0 .. llc$max_entry_points_in_library,
      library_dictionary: ^llt$object_library_dictionaries,
      i: 0 .. llc$max_dictionaries_on_library,
      entry_point_found: boolean,
      entry_point_gated: boolean,
      dictionary_index: 1 .. llc$max_entry_points_in_library,
      module_ring_attributes: lot$module_ring_attributes,
      module_header: ^llt$load_module_header,
      pseudo_transfer_descriptor: lot$external_descriptor,
      valid_file_position: boolean,
      module_structure_error: boolean,
      ignore_symbol_table_present: boolean;

    linkage := lov$unsatisfied_reference^.linkage_info;
    IF current_library^.attributes.key_lock.global THEN
      library_lock := current_library^.attributes.key_lock.value;
    ELSE
      library_lock := loc$no_lock;
    IFEND;
    library_file := current_library^.segment;
    RESET library_file;
    NEXT library_header IN library_file;

    IF library_header^.version = llc$object_library_version THEN
      NEXT library_dictionary: [1 .. library_header^.number_of_dictionaries] IN library_file;
      number_of_entry_points := 0;

    /find_entry_point_dictionary/
      FOR i := LOWERBOUND (library_dictionary^) TO UPPERBOUND (library_dictionary^) DO
        CASE library_dictionary^ [i].kind OF
        = llc$entry_point_dictionary =
          entry_point_dictionary := #PTR (library_dictionary^ [i].entry_point_dictionary, library_file^);
          number_of_entry_points := UPPERBOUND (entry_point_dictionary^);
          EXIT /find_entry_point_dictionary/;
        ELSE
        CASEND;
      FOREND /find_entry_point_dictionary/;

    ELSEIF library_header^.version = 'V1.0' THEN

      RESET library_file;
      NEXT library_hdr IN library_file;
      entry_point_dictionary := #PTR (library_hdr^.entry_point_dictionary, library_file^);
      number_of_entry_points := library_hdr^.number_of_entry_points;

    ELSE
    IFEND;

    IF number_of_entry_points = 0 THEN
      entry_point_dictionary := NIL;
    IFEND;

    references_list_head := ^linkage^.unsat_references_list^.references;
    reference_group := references_list_head^;

  /satisfy_each_reference_group/
    WHILE reference_group <> NIL DO
      IF reference_group^.logically_satisfied THEN
        reference_group := reference_group^.nnext;
        CYCLE /satisfy_each_reference_group/
      IFEND;
      IF reference_group^.newly_created THEN
        reference_group^.newly_created := FALSE;

{ Make sure there are no more newly created references on this reference group chain.

        reference_group_chain := reference_group^.nnext;
        WHILE reference_group_chain <> NIL DO
          reference_group_chain^.newly_created := FALSE;
          reference_group_chain := reference_group_chain^.nnext;
        WHILEND;
        reference_group := reference_group^.nnext;
        lov$unsatisfied_reference^.library_searched := 0;
        another_scan_required := TRUE;
        CYCLE /satisfy_each_reference_group/
      IFEND;
      IF (reference_group^.ring >= current_library^.ring_brackets.r1) AND
            (reference_group^.ring <= current_library^.ring_brackets.r3) AND
            ((reference_group^.global_key = library_lock) OR (library_lock = loc$no_lock) OR
            (reference_group^.global_key = loc$master_key)) THEN
        lop$search_entry_pt_dictionary (^linkage^.name, entry_point_dictionary, entry_point_found,
              entry_point_gated, dictionary_index);
        IF NOT entry_point_found THEN
          RETURN
        IFEND;
        IF entry_point_gated OR (reference_group^.ring <= current_library^.ring_brackets.r2) THEN
          reference_group^.logically_satisfied := TRUE;
          IF reference_group^.ring >= current_library^.ring_brackets.r2 THEN
            module_ring_attributes.loaded_ring := current_library^.ring_brackets.r2;
            module_ring_attributes.call_bracket := current_library^.ring_brackets.r3;
          ELSE
            module_ring_attributes.loaded_ring := reference_group^.ring;
            module_ring_attributes.call_bracket := reference_group^.ring;
          IFEND;
          module_header := #PTR (entry_point_dictionary^ [dictionary_index].module_header, library_file^);
          IF module_header = NIL THEN
            lop$report_error (lle$bad_module_header_ptr, current_library^.attributes.name, 'entry',
                  dictionary_index);
            reference_group := reference_group^.nnext;
            CYCLE /satisfy_each_reference_group/
          IFEND;
          object_text_descriptor := #PTR (module_header^.interpretive_element, library_file^);
          IF object_text_descriptor = NIL THEN
            lop$report_error (lle$bad_interpretive_elem_ptr, current_library^.attributes.name, '',
                  #OFFSET (module_header));
            reference_group := reference_group^.nnext;
            CYCLE /satisfy_each_reference_group/
          IFEND;

          RESET library_file TO object_text_descriptor;

          lov$unsatisfied_reference := lov$unsatisfied_reference^.b_link;

          lop$load_module (module_ring_attributes, current_library^.attributes, control_options, library_file,
                pseudo_transfer_descriptor, ignore_symbol_table_present, module_structure_error);

          IF linkage^.unsat_references_list = NIL THEN
            RETURN;
          ELSE
            lov$unsatisfied_reference := lov$unsatisfied_reference^.f_link;

            reference_group := linkage^.unsat_references_list^.references;
          IFEND;

          CYCLE /satisfy_each_reference_group/;
        IFEND;
      IFEND;
      reference_group := reference_group^.nnext;
    WHILEND /satisfy_each_reference_group/;
  PROCEND satisfy_from_current_library;
?? OLDTITLE, EJECT ??

  VAR
    another_scan_required: boolean,
    current_library: ^lot$library_descriptor,
    file_descriptor: lot$file_descriptor,
    first_library_on_list: boolean,
    library_number: integer,
    library_valid: boolean;

  IF lov$head_of_unsat_ref_list = NIL THEN
    RETURN;
  IFEND;

  IF lov$head_of_unsat_ref_list^.f_link <> lov$head_of_unsat_ref_list THEN
    REPEAT
      another_scan_required := FALSE;
      IF lov$library_list.first = NIL THEN
        lov$unsatisfied_reference := lov$head_of_unsat_ref_list^.f_link;
        lop$satisfy_task_services_refs (control_options, lov$unsatisfied_reference, another_scan_required);
      ELSE;

        current_library := lov$library_list.first;
        first_library_on_list := TRUE;
        library_number := 1;

      /library_scan/
        WHILE current_library <> NIL DO
          IF NOT current_library^.library_valid THEN
            current_library := current_library^.nnext;
            CYCLE /library_scan/;
          IFEND;

          lov$unsatisfied_reference := lov$head_of_unsat_ref_list^.f_link;

        /reference_scan/
          WHILE lov$unsatisfied_reference <> lov$head_of_unsat_ref_list DO
            IF NOT current_library^.library_open THEN
              lop$open_library (current_library^.attributes.name, file_descriptor, library_valid);
              IF NOT library_valid THEN
                current_library^.library_valid := FALSE;
                current_library := current_library^.nnext;
                CYCLE /library_scan/;
              ELSE
                IF current_library^.text_embedded_library THEN
                  bap$inhibit_implicit_detach (file_descriptor.file_identifier);
                IFEND;
                current_library^.library_open := TRUE;
                current_library^.segment := file_descriptor.segment;
                current_library^.ring_brackets := file_descriptor.ring_brackets;
                current_library^.attributes := file_descriptor.attributes;
              IFEND;
            IFEND;

            IF library_number <= lov$unsatisfied_reference^.library_searched THEN
              lov$unsatisfied_reference := lov$unsatisfied_reference^.f_link;
              CYCLE /reference_scan/;
            IFEND;

            lov$unsatisfied_reference^.library_searched := library_number;

            IF current_library^.attributes.name = loc$task_services_library_name THEN
              lop$satisfy_task_services_refs (control_options, lov$unsatisfied_reference,
                    another_scan_required);
            ELSEIF (current_library^.attributes.name (1, loc$deferred_entry_pt_lib_size) =
                  loc$deferred_entry_pt_library) THEN
              satisfy_deferred_references (control_options, lov$unsatisfied_reference, another_scan_required);
            ELSE
              satisfy_from_current_library;
              lov$unsatisfied_reference := lov$unsatisfied_reference^.f_link;
            IFEND;
          WHILEND /reference_scan/;

          IF another_scan_required THEN
            EXIT /library_scan/
          IFEND;

          current_library := current_library^.nnext;
          library_number := library_number + 1;
          first_library_on_list := FALSE;
        WHILEND /library_scan/;
      IFEND;

    UNTIL (NOT another_scan_required) OR (lov$head_of_unsat_ref_list^.f_link = lov$head_of_unsat_ref_list);
  IFEND;

PROCEND lop$satisfy_externals;
?? OLDTITLE ??
MODEND lom$load_library_modules;
