diff options
| author | Alasdair Armstrong | 2017-09-07 16:54:20 +0100 |
|---|---|---|
| committer | Alasdair Armstrong | 2017-09-07 16:54:20 +0100 |
| commit | 842165c1171fde332bd42e7520338c59a797f76b (patch) | |
| tree | 75b61297b6d9b6e4810542390eb1371afc2f183f /lib/ocaml_rts/linksem/linkable_list.ml | |
| parent | 8124c487b576661dfa7a0833415d07d0978bc43e (diff) | |
Add ocaml run-time and updates to sail for ocaml backend
Diffstat (limited to 'lib/ocaml_rts/linksem/linkable_list.ml')
| -rw-r--r-- | lib/ocaml_rts/linksem/linkable_list.ml | 568 |
1 files changed, 568 insertions, 0 deletions
diff --git a/lib/ocaml_rts/linksem/linkable_list.ml b/lib/ocaml_rts/linksem/linkable_list.ml new file mode 100644 index 00000000..c128563c --- /dev/null +++ b/lib/ocaml_rts/linksem/linkable_list.ml @@ -0,0 +1,568 @@ +(*Generated by Lem from linkable_list.lem.*) +open Lem_basic_classes +open Lem_function +open Lem_string +open Lem_string_extra +open Lem_tuple +open Lem_bool +open Lem_list +open Lem_list_extra +open Lem_set +open Lem_set_extra +(*import Map*) +open Lem_sorting +open Lem_num +open Lem_maybe +open Lem_assert_extra + +open Byte_sequence +open Default_printing +open Error +open Missing_pervasives +open Show + +open Elf_types_native_uint +open Elf_memory_image +open Elf_header +open Elf_file +open Memory_image +open Elf_memory_image +open Elf_section_header_table +open Elf_symbol_table +open String_table +open Input_list + +open Elf_memory_image +open Elf_memory_image_of_elf64_file + +type script = byte_sequence (* FIXME *) + +type linkable_object = RelocELF of elf_memory_image (* memory image without address assignments *) + | SharedELF of elf_memory_image (* memory image with address assignments *) + | ScriptAST of script (* FIXME: should be elaborated away *) + | ControlScriptDefs + +(*val string_of_linkable_object : linkable_object -> string*) +let string_of_linkable_object l:string= ((match l with + RelocELF(_) -> "a relocatable file (...)" + | SharedELF(_) -> "a shared library (...)" + | ScriptAST(_) -> "a linker script (...)" + | ControlScriptDefs -> "the control script" +)) + +(* We keep the original input item around, hence the filename and byte sequence + * and options. *) +type linkable_item = linkable_object * input_item * input_options + +(*val short_string_of_linkable_item : linkable_item -> string*) +let short_string_of_linkable_item item:string= + (let (obj, inp, opts) = item + in + short_string_of_input_item inp) + +let instance_Show_Show_Linkable_list_linkable_object_dict:(linkable_object)show_class= ({ + + show_method = string_of_linkable_object}) + +type linkable_list = linkable_item list + +type symbol_resolution_oracle = linkable_list -> int -> string -> int list +type binding = (Nat_big_num.num * symbol_reference * linkable_item) * (Nat_big_num.num * symbol_definition * linkable_item)option +type binding_list = binding list +type binding_map = (string, ( (Nat_big_num.num * binding)list)) Pmap.map + + +let image_of_linkable_item item:(Abis.any_abi_feature)annotated_memory_image= ((match item with + (RelocELF(image), _, _) -> image + | (SharedELF(image), _, _) -> image + | _ -> failwith "no image" +)) + +(*val linkable_item_of_input_item_and_options : forall 'abifeature. abi 'abifeature -> input_item -> input_options -> linkable_item*) +let linkable_item_of_input_item_and_options a it opts:linkable_object*(string*input_blob*(Command_line.input_unit*(origin_coord)list))*input_options= + ((match ((match it with + (fname1, Reloc(seq), origin) -> + (*let _ = Missing_pervasives.errln ("Considering relocatable file " ^ fname) in*) + Elf_file.read_elf64_file seq >>= (fun e -> + return (RelocELF(elf_memory_image_of_elf64_file a fname1 e), it, opts)) + | (fname1, Shared(seq), origin) -> + (*let _ = Missing_pervasives.errln ("Skipping shared object " ^ fname) in *) + fail "unsupported input item" + | (fname1, Script(seq), origin) -> + (*let _ = Missing_pervasives.errln ("Skipping linker script " ^ fname) in*) + fail "unsupported input item" + )) + with + Success(item) -> item + | Fail(str) -> failwith (str ^ ": non-ELF or non-relocatable input file") + )) + +(*val string_of_linkable : linkable_item -> string*) +let string_of_linkable l:string= ((match l with + (_, item, _) -> string_of_triple + instance_Show_Show_string_dict instance_Show_Show_Input_list_input_blob_dict (instance_Show_Show_tup2_dict + Command_line.instance_Show_Show_Command_line_input_unit_dict + (instance_Show_Show_list_dict + instance_Show_Show_Input_list_origin_coord_dict)) item +)) + +(* How do we signal "multiple definitions"? + * This is part of the policy baked into the particular oracle: + * are multiple definitions okay, or do we fail? + * + * NOTE that multiple definitions *globally* is not the same as + * multiple definitions as candidates for a given binding. We + * can get the former even if we don't have the latter, in some + * weird group/archive arrangements. The right place to detect + * this condition is probably when generating the output symtab. + *) + +(*val add_definition_to_map : (natural * symbol_definition * linkable_item) -> Map.map string (list (natural * symbol_definition * linkable_item)) + -> Map.map string (list (natural * symbol_definition * linkable_item))*) +let add_definition_to_map def_idx_and_def_and_linkable m:((string),((Nat_big_num.num*symbol_definition*(linkable_object*input_item*input_options))list))Pmap.map= + (let (def_idx, def, def_linkable) = def_idx_and_def_and_linkable + in + (match Pmap.lookup def.def_symname m with + Some curlist -> Pmap.add def.def_symname ((def_idx, def, def_linkable) :: curlist) m + | None -> Pmap.add def.def_symname [(def_idx, def, def_linkable)] m + )) + +(*val all_definitions_by_name : linkable_list -> Map.map string (list (natural * symbol_definition * linkable_item))*) +let all_definitions_by_name linkables:((string),((Nat_big_num.num*symbol_definition*linkable_item)list))Pmap.map= +( + (* Now that linkables are ELF memory images, we can make the + * list of definitions much more easily. *)let list_of_deflists = (Lem_list.mapi (fun (idx1 : int) -> (fun (item : linkable_item) -> + let img2 = (image_of_linkable_item item) + in + let (all_def_tags, all_def_ranges) + = (List.split (Multimap.lookupBy0 + (Memory_image_orderings.instance_Basic_classes_Ord_Memory_image_range_tag_dict + Abis.instance_Basic_classes_Ord_Abis_any_abi_feature_dict) (instance_Basic_classes_Ord_Maybe_maybe_dict + (instance_Basic_classes_Ord_tup2_dict + instance_Basic_classes_Ord_string_dict + (instance_Basic_classes_Ord_tup2_dict + instance_Basic_classes_Ord_Num_natural_dict + instance_Basic_classes_Ord_Num_natural_dict))) instance_Basic_classes_SetType_var_dict (instance_Basic_classes_SetType_Maybe_maybe_dict + (instance_Basic_classes_SetType_tup2_dict + instance_Basic_classes_SetType_var_dict + (instance_Basic_classes_SetType_tup2_dict + instance_Basic_classes_SetType_Num_natural_dict + instance_Basic_classes_SetType_Num_natural_dict))) (Memory_image_orderings.tagEquiv + Abis.instance_Abi_classes_AbiFeatureTagEquiv_Abis_any_abi_feature_dict) (SymbolDef(null_symbol_definition)) img2.by_tag)) + in + let all_defs = (Lem_list.map (fun tag -> (match tag with + SymbolDef(def) -> (def, item) + | _ -> failwith "matched tag not a symbol definition" + )) all_def_tags) + in + let x2 = ([]) in List.fold_right + (fun(def, def_linkable) x2 -> + if true then (Nat_big_num.of_int idx1, def, def_linkable) :: x2 else x2) + all_defs x2 + )) linkables) + in + List.fold_left (fun accum -> (fun deflist -> + List.fold_left (fun m -> (fun (def_idx, def, def_linkable) -> add_definition_to_map (def_idx, def, def_linkable) m)) accum deflist + )) (Pmap.empty compare) list_of_deflists) + +type binding_oracle = + linkable_list + -> (string, ( (Nat_big_num.num * symbol_definition * linkable_item)list)) Pmap.map + -> (Nat_big_num.num * symbol_reference * linkable_item) + -> (Nat_big_num.num * symbol_definition * linkable_item)option + +(*val resolve_one_reference_default : forall 'abifeature. abi 'abifeature -> binding_oracle*) +let resolve_one_reference_default a linkables defmap ref_idx_and_ref_and_linkable:(Nat_big_num.num*symbol_definition*(linkable_object*(string*input_blob*(Command_line.input_unit*(origin_coord)list))*input_options))option= + (let (ref_idx, ref1, ref_linkable) = ref_idx_and_ref_and_linkable + in + (* Get the list of all definitions whose name matches. + * Don't match empty names. + * How should we handle common symbols here? + * A common symbol is a potential definition, so it goes in the def list. + *) + let (defs_and_linkables_with_matching_name : (Nat_big_num.num * symbol_definition * linkable_item) list) + = ((match Pmap.lookup ref1.ref_symname defmap with + Some (l : ( (Nat_big_num.num * symbol_definition * linkable_item)list)) -> l + | None -> [] + )) + in + (* Filter the list by eligibility rules. + * Normally, + * + * - any .o file can supply any other .o file on the command line + * - any .a file supplies only files appearing to its left + * i.e. "it is searched once for definitions" + * - does a .o file supply a .a file? to both its right and left? Experimentally, YES. + * + * So the restrictions are + * - archives may not supply weak references + * - archives may only supply to the left, or to themselves, or to objects in the same group + *) + let (ref_obj, (ref_fname, ref_blob, (ref_u, ref_coords)), ref_options) = ref_linkable + in + let ref_is_weak = (Nat_big_num.equal (get_elf64_symbol_binding ref1.ref_syment) stb_weak) + in + let def_is_eligible = (fun (def_idx, def, def_linkable) -> + let ref_is_unnamed = (ref1.ref_symname = "") + in + let ref_is_to_defined_or_common_symbol = ( not (Nat_big_num.equal (Nat_big_num.of_string (Uint32.to_string ref1.ref_syment.elf64_st_shndx)) stn_undef)) + in + let def_sym_is_ref_sym = ( Nat_big_num.equal ref_idx def_idx && (Nat_big_num.equal ref1.ref_sym_scn def.def_sym_scn + && Nat_big_num.equal ref1.ref_sym_idx def.def_sym_idx)) + in + let (def_obj, (def_fname, def_blob, def_origin), def_options) = def_linkable + in + let (def_u, def_coords) = def_origin + in + let (def_in_group, def_in_archive) = ((match def_coords with + InArchive(aid, aidx, _, _) :: InGroup(gid1, gidx) :: [_] -> (Some gid1, Some aid) + | InArchive(aid, aidx, _, _) :: [_] -> (None, Some aid) + | InGroup(gid1, gidx) :: [_] -> (Some gid1, None) + | [_] -> (None, None) + | _ -> failwith "internal error: didn't understand origin coordinates of definition" + )) + in + let ref_is_leftmore = (Nat_big_num.less_equal ref_idx def_idx) + in + (* For simplicity we include the case of "same archive" in "in group with". *) + let ref_is_in_group_with_def = ((match def_in_group with + None -> false + | Some def_gid -> + (match ref_coords with + InArchive(_, _, _, _) :: InGroup(gid1, _) :: [_] -> Nat_big_num.equal gid1 def_gid + | InGroup(gid1, _) :: [_] -> Nat_big_num.equal gid1 def_gid + | _ -> false + ) + )) + in + (* but maybe same archive? *) + (* DEBUGGING: print some stuff out if we care about this symbol. *)let _ = + (if (ref_fname = "backtrace.o") && (def.def_symname = "_Unwind_GetCFA") then + (*Missing_pervasives.errln ("saw backtrace.o referencing _Unwind_GetCFA; coords are " + ^ "def: " ^ (show def_coords) ^ ", ref: " ^ (show ref_coords) ^ "; ref_is_in_group_with_def: " + ^ (show ref_is_in_group_with_def) ^ "; def_in_group: " ^ (show def_in_group))*) + () + else ()) + in + let ref_and_def_are_in_same_archive = ((match (def_coords, ref_coords) with + (InArchive(x1, _, _, _) :: _, InArchive(x2, _, _, _) :: _) -> Nat_big_num.equal x1 x2 + | _ -> false + )) + in + let def_is_in_archive = ((match def_in_archive with + Some _ -> true + | None -> false + )) + in + if ref_is_to_defined_or_common_symbol then def_sym_is_ref_sym + else + if ref_is_unnamed then false + else + if def_is_in_archive + then + (* Weak references *can* be resolved to archive members... + * if the reference itself is also in the archive. *) + ((not ref_is_weak) || ref_and_def_are_in_same_archive) + && ( + ref_is_leftmore + || (ref_and_def_are_in_same_archive + || ref_is_in_group_with_def) + ) + else + true + ) + in + let eligible_defs = (List.filter def_is_eligible defs_and_linkables_with_matching_name) + in + let (maybe_target_def_idx, maybe_target_def, maybe_target_def_linkable) = ((match eligible_defs with + [] -> (None, None, None) + | [(def_idx, def, def_linkable)] -> (Some def_idx, Some def, Some def_linkable) + | (d_idx, d, d_l) :: more_pairs -> + (* Break ties by + * - putting defs in relocs (or --defsym or linker script, a.k.a. command line) ahead of defs in archives; + * - else whichever definition appeared first in the left-to-right order. + *) + let sorted = (insertSortBy (fun (d_idx1, d1, (_, (_, _, (_, d_l1_coords)), _)) -> (fun (d_idx2, d2, (_, (_, _, (_, d_l2_coords)), _)) -> + (match (d_l1_coords, d_l2_coords) with + (InCommandLine(_) :: _, InCommandLine(_) :: _) -> Nat_big_num.less d_idx1 d_idx2 + | (InCommandLine(_) :: _, _) -> (* command-line wins *) true + | (_, InCommandLine(_) :: _) -> (* command-line wins *) false + | (_, _) -> Nat_big_num.less d_idx1 d_idx2 + ))) eligible_defs) + in + (match sorted with + (first_d_idx, first_d, first_d_l) :: _ -> (Some first_d_idx, Some first_d, Some first_d_l) + | _ -> failwith "impossible: sorted list is shorter than original" + ) + )) + in + let refstr = ("`" + ^ (ref1.ref_symname ^ ("' (" ^ + ((if Nat_big_num.equal (Nat_big_num.of_string (Uint32.to_string ref1.ref_syment.elf64_st_shndx)) shn_undef then "UND" else "defined") ^ + (" symbol at index " ^ ((Nat_big_num.to_string ref1.ref_sym_idx) ^ (" in symtab " + ^ ((Nat_big_num.to_string ref1.ref_sym_scn) ^ (" in " ^ (ref_fname + ^ ")")))))))))) + in + (*let _ = Missing_pervasives.errs ("Bound a reference from " ^ refstr ^ " to ") + in*) + (match (maybe_target_def_idx, maybe_target_def, maybe_target_def_linkable) with + (Some target_def_idx, Some target_def, Some target_def_linkable) -> + (*let _ = Missing_pervasives.errln (" a definition in "^ (show (target_def_linkable))) + in*) + Some(target_def_idx, target_def, target_def_linkable) + | (None, None, None) -> + (*let _ = Missing_pervasives.errln " no definition" + in*) + if ref_is_weak (* || a.symbol_is_generated_by_linker ref.ref_symname *) then None + else (* failwith ("undefined symbol: " ^ refstr) *) None + (* FIXME: do a check, *after* the linker script has been interpreted, + * that all remaining undefined symbols are permitted by the ABI/policy. *) + | _ -> failwith "impossible: non-matching maybes for target_def_idx and target_def" + )) + +(*val resolve_all : + linkable_list + -> Map.map string (list (natural * symbol_definition * linkable_item)) (* all definitions *) + -> binding_oracle + -> list (natural * symbol_reference * linkable_item) + -> list ((natural * symbol_reference * linkable_item) * maybe (natural * symbol_definition * linkable_item))*) +let resolve_all linkables all_defs oracle refs:((Nat_big_num.num*symbol_reference*(linkable_object*input_item*input_options))*(Nat_big_num.num*symbol_definition*linkable_item)option)list= + (Lem_list.map (fun (ref_idx, ref1, ref_linkable) -> ((ref_idx, ref1, ref_linkable), (oracle linkables all_defs (ref_idx, ref1, ref_linkable)))) refs) + +(* To accumulate which inputs are needed, we work with a list of undefineds, starting with those + * in the forced-output objects. We then iteratively build a list of all needed symbol definitions, + * pulling in the objects that contain them, until we reach a fixed point. *) +(*val resolve_undefs_in_one_object : + linkable_list + -> Map.map string (list (natural * symbol_definition * linkable_item)) (* all definitions *) + -> binding_oracle + -> natural + -> list ((natural * symbol_reference * linkable_item) * maybe (natural * symbol_definition * linkable_item))*) +let resolve_undefs_in_one_object linkables all_defs oracle idx1:((Nat_big_num.num*symbol_reference*linkable_item)*(Nat_big_num.num*symbol_definition*linkable_item)option)list= +( + (* Get this object's list of references *)let item = ((match Lem_list.list_index linkables (Nat_big_num.to_int idx1) with + Some it -> it + | None -> failwith "impossible: linkable not in list of linkables" + )) + in + let img2 = (image_of_linkable_item item) + in + let (all_ref_tags, all_ref_ranges) + = (List.split (Multimap.lookupBy0 + (Memory_image_orderings.instance_Basic_classes_Ord_Memory_image_range_tag_dict + Abis.instance_Basic_classes_Ord_Abis_any_abi_feature_dict) (instance_Basic_classes_Ord_Maybe_maybe_dict + (instance_Basic_classes_Ord_tup2_dict + instance_Basic_classes_Ord_string_dict + (instance_Basic_classes_Ord_tup2_dict + instance_Basic_classes_Ord_Num_natural_dict + instance_Basic_classes_Ord_Num_natural_dict))) instance_Basic_classes_SetType_var_dict (instance_Basic_classes_SetType_Maybe_maybe_dict + (instance_Basic_classes_SetType_tup2_dict + instance_Basic_classes_SetType_var_dict + (instance_Basic_classes_SetType_tup2_dict + instance_Basic_classes_SetType_Num_natural_dict + instance_Basic_classes_SetType_Num_natural_dict))) (Memory_image_orderings.tagEquiv + Abis.instance_Abi_classes_AbiFeatureTagEquiv_Abis_any_abi_feature_dict) (SymbolRef(null_symbol_reference_and_reloc_site)) img2.by_tag)) + in + (* By using SymbolRef, we are extracting and binding each relocation site individually. + * since there might be more than one relocation site referencing the same symbol name, + * in a given object. + * + * We are also binding SymbolRefs that have no relocation, which occur when there's + * an UND symbol which is not actually used by a relocation site, but is nevertheless + * in need of being resolved. + * + * We don't (for the moment) want to make different decisions for different reloc sites + * in the same object referencing the same symbol. So we dedup from a list to a set. + *) + let all_refs = (Pset.from_list compare (Lem_list.map (fun tag -> (match tag with + SymbolRef(r) -> r.ref + | _ -> failwith "matched tag not a relocation site" + )) all_ref_tags)) + in + let ref_triples = (let x2 =(Pset.from_list (tripleCompare Nat_big_num.compare compare (tripleCompare compare (tripleCompare compare compare (pairCompare compare (lexicographic_compare compare))) compare)) + []) in Pset.fold + (fun ref1 x2 -> if true then Pset.add (idx1, ref1, item) x2 else x2) + all_refs x2) + in + (*let _ = Missing_pervasives.errln ("object " ^ (show item) ^ " has " ^ + (show (Set.size ref_triples)) ^ " reloc references (symname, sym_scn, sym_idx, st_shndx) (" ^ + (show (List.map (fun x -> ("\"" ^ x.ref_symname ^ "\"", x.ref_sym_scn, x.ref_sym_idx, natural_of_elf64_half x.ref_syment.elf64_st_shndx)) (Set_extra.toList all_refs))) ^ ")") + in*) + let und_ref_triples = (let x2 =(Pset.from_list (tripleCompare Nat_big_num.compare compare (tripleCompare compare (tripleCompare compare compare (pairCompare compare (lexicographic_compare compare))) compare)) + []) in Pset.fold + (fun(idx1, ref1, ref_item) x2 -> + if Nat_big_num.equal + (Nat_big_num.of_string + (Uint32.to_string ref1.ref_syment.elf64_st_shndx)) shn_undef then + Pset.add (idx1, ref1, ref_item) x2 else x2) ref_triples x2) + in + (*let _ = Missing_pervasives.errln ("... of which " ^ + (show (Set.size und_ref_triples)) ^ " are to undefined symbols: (symname, sym_scn, sym_idx, st_shndx) (" ^ + (show (List.map (fun (idx, x, _) -> ("\"" ^ x.ref_symname ^ "\"", x.ref_sym_scn, x.ref_sym_idx, natural_of_elf64_half x.ref_syment.elf64_st_shndx)) (Set_extra.toList und_ref_triples))) ^ ")") + in*) + resolve_all linkables all_defs oracle (Pset.elements ref_triples)) + +(*val accumulate_bindings_bf : forall 'abifeature. + abi 'abifeature + -> linkable_list + -> Map.map string (list (natural * symbol_definition * linkable_item)) (* all definitions *) + -> set natural (* inputs fully-bound so far *) + -> list natural (* ordered list of inputs to bind next *) + -> list ((natural * symbol_reference * linkable_item) * maybe (natural * symbol_definition * linkable_item)) (* bindings made so far *) + -> list ((natural * symbol_reference * linkable_item) * maybe (natural * symbol_definition * linkable_item))*) (* all accumulated bindings bindings *) +let rec accumulate_bindings_bf a linkables all_defs fully_bound to_bind bindings_accum:((Nat_big_num.num*symbol_reference*linkable_item)*(Nat_big_num.num*symbol_definition*linkable_item)option)list= +( + (* This is like foldl, except that each stage + * can add stuff to the work list *)(match to_bind with + [] -> bindings_accum (* termination *) + | l_idx :: more_idx -> + (* Get the new bindings for this object *) + let new_bindings = (resolve_undefs_in_one_object + linkables + all_defs + (resolve_one_reference_default a) + l_idx) + in + let new_fully_bound = (Pset.add l_idx fully_bound) + in + (* Which of the new bindings are to objects + * not yet fully bound or not yet in the to-bind list? *) + let new_bindings_def_idx = (list_concat_map (fun (ref1, maybe_def_and_idx_and_linkable) -> + (match maybe_def_and_idx_and_linkable with + Some (def_idx, def, def_linkable) -> [def_idx] + | None -> [] + ) + ) new_bindings) + in + let new_bindings_def_idx_set = (Pset.from_list Nat_big_num.compare new_bindings_def_idx) + in + let included_linkables_idx = (Pset.(union) fully_bound ((Pset.from_list Nat_big_num.compare to_bind))) + in + let new_l_idx = (Pset.diff new_bindings_def_idx_set included_linkables_idx) + in + let new_l_idx_list = (Pset.elements new_l_idx) + in + (*let _ = Missing_pervasives.errln ( + if List.null new_l_idx_list + then + "Fully bound references in " ^ (show (List.index linkables (natFromNatural l_idx))) + ^ " using only already-included linkables (" + ^ (show (List.map (fun i -> List.index linkables (natFromNatural i)) (Set_extra.toList included_linkables_idx))) + else + "Including additional linkables " + ^ (show (List.mapMaybe (fun i -> List.index linkables (natFromNatural i)) new_l_idx_list)) + ) + in*) + accumulate_bindings_bf + a + linkables + all_defs + new_fully_bound + ( List.rev_append (List.rev more_idx) new_l_idx_list) + ( List.rev_append (List.rev bindings_accum) new_bindings) + )) + +(* We need a generalised kind of depth-first search in which there are multiple start points. + * Also, we always work one object at a time, not one edge at a time; when we pull in an object, + * we resolve *all* the references therein. + *) +(*val accumulate_bindings_objectwise_df : forall 'abifeature. + abi 'abifeature + -> linkable_list + -> Map.map string (list (natural * symbol_definition * linkable_item)) (* all definitions *) + + -> list ((natural * symbol_reference * linkable_item) * maybe (natural * symbol_definition * linkable_item)) (* bindings made so far *) + -> set natural (* inputs fully-bound so far -- these are "black" *) + -> list natural (* inputs scheduled for binding -- these include + any "grey" (in-progress) nodes *and* + any nodes that we have committed to exploring + (the "start nodes"). + Because we're depth-first, we prepend our adjacent + nodes to this list, making them grey, then we + recurse by taking from the head. We must always + filter out the prepended nodes from the existing list, + to ensure we don't recurse infinitely. *) + -> list ((natural * symbol_reference * linkable_item) * maybe (natural * symbol_definition * linkable_item))*) (* all accumulated bindings bindings *) +let rec accumulate_bindings_objectwise_df a linkables all_defs bindings_accum blacks greys:((Nat_big_num.num*symbol_reference*linkable_item)*(Nat_big_num.num*symbol_definition*linkable_item)option)list= + ((match greys with + [] -> bindings_accum (* termination *) + | l_idx :: more_idx -> + (* Get the new bindings for this object *) + let new_bindings = (resolve_undefs_in_one_object + linkables + all_defs + (resolve_one_reference_default a) + l_idx) + in + (* We pull in the whole object at a time ("objectwise"), so by definition, + * we have created bindings for everything in this object; it's now black. *) + let new_fully_bound = (Pset.add l_idx blacks) + in + (* Which of the new bindings are to objects + * not yet fully bound or not yet in the to-bind list? *) + let new_bindings_def_idx = (list_concat_map (fun (ref1, maybe_def_and_idx_and_linkable) -> + (match maybe_def_and_idx_and_linkable with + Some (def_idx, def, def_linkable) -> [def_idx] + | None -> [] + ) + ) new_bindings) + in + let new_bindings_def_idx_set = (Pset.from_list Nat_big_num.compare new_bindings_def_idx) + in + (* this is the "black or grey" set. *) + let included_linkables_idx = (Pset.(union) blacks ((Pset.from_list Nat_big_num.compare greys))) + in + (* these are the white ones that we're adjacent to *) + let new_l_idx = (Pset.diff new_bindings_def_idx_set included_linkables_idx) + in + let new_l_idx_list = (Pset.elements new_l_idx) + in + (* What is the new grey-alike list? (This is the list we tail-recurse down.) + * It's + * - the existing grey-alike list + * - with any new (were-white) objects prepended + * - ... and filtered to *remove* these from the existing list (avoid duplication). + *) + let new_grey_list = (List.rev_append (List.rev new_l_idx_list) (List.filter (fun x -> not ( Pset.mem x new_l_idx)) more_idx)) + in + (* whether or not we've not uncovered any new white nodes, we tail-recurse *) + (*let _ = (if List.null new_l_idx_list then + Missing_pervasives.errln ("Fully bound references in " ^ (show (List.index linkables (natFromNatural l_idx))) + ^ " using only already-included linkables (" + ^ (show (List.map (fun i -> List.index linkables (natFromNatural i)) (Set_extra.toList included_linkables_idx))) + ) else Missing_pervasives.errln ("Including additional linkables " + ^ (show (List.mapMaybe (fun i -> List.index linkables (natFromNatural i)) new_l_idx_list)))) + in*) + accumulate_bindings_objectwise_df + a + linkables + all_defs + ( List.rev_append (List.rev bindings_accum) new_bindings) + (new_fully_bound : Nat_big_num.num Pset.set) + (new_grey_list : Nat_big_num.num list) + )) + +(* Rather than recursively expanding the link by searching for definitions of undefs, + * the GNU linker works by recursing/looping along the list of *linkables*, testing whether + * any of the defs satisfies a currently-undef'd thing. On adding a new undef'd thing, + * we re-search only from the current archive, not from the beginning (i.e. the + * "def_is_leftmore or def_in_same_archive" logic). + * + * Why is this not the same as depth-first? One example is if we pull in a new object + * which happens to have two undefs: one satisfied by the *first* element in the current archive, + * and one satisfied by the last. + * + * In the GNU algorithm, we'll pull in the first archive element immediately afterwards, because + * we'll re-traverse the archive and find it's needed. + * + * In the depth-first algorithm, it depends entirely on the ordering of the new bindings, i.e. + * the symtab ordering of the two undefs. If the later-in-archive def was bound *first*, we'll + * recurse down *that* object's dependencies first. + * + * So if we sort the new grey list + * so that bindings formed in order of *current archive def pos*, + * will we get the same behaviour? + * We can't really do this, because we have no "current archive". + * + * Need to rewrite the algorithm to fold along the list of linkables. + *) |
