summaryrefslogtreecommitdiff
path: root/lib/ocaml_rts/linksem/link.ml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ocaml_rts/linksem/link.ml')
-rw-r--r--lib/ocaml_rts/linksem/link.ml1005
1 files changed, 1005 insertions, 0 deletions
diff --git a/lib/ocaml_rts/linksem/link.ml b/lib/ocaml_rts/linksem/link.ml
new file mode 100644
index 00000000..1265de61
--- /dev/null
+++ b/lib/ocaml_rts/linksem/link.ml
@@ -0,0 +1,1005 @@
+(*Generated by Lem from link.lem.*)
+open Lem_basic_classes
+open Lem_function
+open Lem_string
+open Lem_tuple
+open Lem_bool
+open Lem_list
+open Lem_sorting
+open Lem_map
+open Lem_set
+(*import Set_extra*)
+open Lem_num
+open Lem_maybe
+open Lem_assert_extra
+(*import Command_line*)
+(*import Input_list*)
+
+open Byte_sequence
+open Default_printing
+open Error
+open Missing_pervasives
+open Show
+open Endianness
+
+open Elf_header
+open Elf_interpreted_section
+open Elf_interpreted_segment
+open Elf_section_header_table
+open Elf_program_header_table
+open Elf_symbol_table
+open Elf_types_native_uint
+open Elf_relocation
+
+open Abis
+open Abi_amd64_relocation (* HACK -- remove me *)
+
+open Input_list
+open Linkable_list
+(*import Command_line*)
+
+open Memory_image
+open Memory_image_orderings
+open Elf_memory_image
+open Elf_memory_image_of_elf64_file
+open Linker_script
+
+let all_common_symbols img2:(symbol_definition)list= (List.filter (fun def -> Nat_big_num.equal
+ (Nat_big_num.of_string (Uint32.to_string def.def_syment.elf64_st_shndx)) shn_common
+) (elf_memory_image_defined_symbols img2))
+
+(* Q. On what does the decision about a reloc depend? definitely on
+ *
+ * -- command-line options applying to the referenc*ed* object;
+ * (CHECK: I'm inferring that -Bsymbolic, like -Bstatic, applies to the
+ * *referenced* object, not the referring -- need experimental conf.)
+ * ACTUALLY, it seems to be global: if a definition goes in the library,
+ * bind to it; doesn't matter where it comes from. So
+ *
+ * -- command-line options applying to the output object / whole link (-Bsymbolic);
+ *
+ * -- command-line options applying to the referencing object?
+ *
+ * What decision can we make?
+ * Given a reloc, it might be
+ * - not bound (weak symbols) -- THIS MEANS it *is* bound but to the value 0!
+ * - bound to a definition
+ *
+ * ... perhaps our distinction is between "firm binding or provisional binding"?
+ * "final binding or overridable binding"?
+ *
+ * Can we also hit cases where the binding is final but can't be relocated til load time?
+ * YES, e.g. any final R_*_64_64 reference in a shared library's data segment.
+ * WHAT do we do in these cases? Apply what we can and generate a R_*_RELATIVE?
+ * Yes, that's where R_*_RELATIVE come from, since they don't appear in .o inputs.
+ *)
+
+(*val def_is_in_reloc : linkable_item -> bool*)
+let def_is_in_reloc def_item:bool= ((match def_item with
+ (RelocELF(_), _, _) -> true
+ | (ScriptAST(_), _, _) -> true
+ | _ -> false
+))
+
+let retrieve_binding_for_ref dict_Basic_classes_Eq_b r r_linkable_idx item bindings_by_name:('b*symbol_reference*'d)*'c=
+ (let maybe_found_bs = (Pmap.lookup r.ref.ref_symname bindings_by_name)
+ in
+ (match maybe_found_bs with
+ None -> failwith "impossible: list of bindings does not include symbol reference (map empty)"
+ (* FIXME: could this actually be an "undefined symbol" link error perhaps? *)
+ | Some bis_and_bs -> (match List.filter (fun (b_idx, ((b_ref_idx, b_ref, b_ref_item), b_maybe_def)) ->
+ if dict_Basic_classes_Eq_b.isEqual_method b_ref_idx r_linkable_idx && (b_ref = r.ref) then
+ (*let _ = Missing_pervasives.errln ("saw ref from linkable idx " ^ (show r_linkable_idx)
+ ^ ", ref sym scn " ^ (show r.ref.ref_sym_scn) ^ ", ref sym idx "^ (show r.ref.ref_sym_idx)
+ ^ ", item " ^ (show item) ^ "; binding to " ^ (
+ match b_maybe_def with
+ Just (def_idx, def, def_item) -> "linkable idx " ^ (show def_idx) ^
+ ", def sym scn " ^ (show def.def_sym_scn) ^ ", def sym idx " ^
+ (show def.def_sym_idx)
+ | Nothing -> "no definition"
+ end
+ )
+ )
+ in*) true
+ else false) bis_and_bs with
+ [] -> failwith "impossible: list of bindings does not include symbol reference (filtered list empty)"
+ | [(bi, b)] -> b
+ | _ -> failwith ("impossible: list of bindings binds reference to symbol `"
+ ^ (r.ref.ref_symname ^ "' more than one way (filtered list has >1 element)"))
+ )
+ ))
+
+type reloc_site_resolution = reloc_site * binding * reloc_decision
+
+
+(*val mark_fate_of_relocs : natural -> abi any_abi_feature -> set Command_line.link_option ->
+ binding_map -> linkable_item -> elf_memory_image -> ((list reloc_site_resolution) * elf_memory_image)*)
+let mark_fate_of_relocs linkable_idx a options bindings_by_name item img2:(reloc_site*((Nat_big_num.num*symbol_reference*(linkable_object*input_item*input_options))*(Nat_big_num.num*symbol_definition*(linkable_object*input_item*input_options))option)*reloc_decision)list*(any_abi_feature)annotated_memory_image=
+(
+ (* Our image already models relocation sites. For each relocation *record*,
+ * we use our bindings to make a decision about whether to apply it or not.
+ *
+ * Q1. How do we get the .rela.dyn made? Synthesise a fake reloc section?
+ * Or pass them through to the linker script separately?
+ * AHA. Note that the script already has an entry for .rela.dyn.
+ * And it matches the ordinary rel sections, e.g. .rela.text and so on.
+ * So if "-q" is active, the applied relocs need to be injected back in *after* the script
+ * has run.
+ * So we need both to materialize some relocs into the script inputs, *and* save some for later.
+ *
+ * Can we just use memory image metadata as the "saved for later" case? YES, I think so.
+ * What do we do with metadata that is now being materialized?
+ * I think we should only remove the metadata when we apply the relocation.
+ * Q. When do we do that?
+ * A. *After* address assignment has happened, i.e. all sections are allocated.
+ *)let building_executable = (Pset.mem (Command_line.OutputKind(Command_line.Executable)) options) in
+ let building_shared_library = (Pset.mem (Command_line.OutputKind(Command_line.SharedLibrary)) options) in
+ let bind_functions_early = (Pset.mem Command_line.BindFunctionsEarly options) in
+ let bind_non_functions_early = (Pset.mem Command_line.BindNonFunctionsEarly options) in
+ let (new_by_tag, rev_decisions) = (List.fold_left (fun (acc_by_tag, rev_acc_decisions) -> (fun (tag, maybe_range) ->
+ let pass_through = (Pset.add (tag, maybe_range) acc_by_tag, rev_acc_decisions)
+ in
+ (match tag with
+ SymbolRef(r) ->
+ (match r.maybe_reloc with
+ Some reloc1 ->
+ (* decision: do we want to
+ * - apply it? if so, do we need a consequent relocation (e.g. R_*_RELATIVE) in the output?
+ * - PICify it, but leave it interposable?
+ * - is "PICified, non-interposable" a thing? I don't think so, because non-interposable bindings are
+ either intra-object *or* necessarily need load-time relocation to account for load addresses.
+ In fact ELF can't express "non-interposable inter-object bindings" because we can't name
+ specific objects when binding symbols.
+ * - leave it alone, i.e. "relocate at load time"?
+ *
+ * Some useful questions: is the binding final?
+ * The GNU linker *never* leaves text relocs alone when generating shared libs;
+ * it always PICifies them.
+ * It can leave them alone when generating executables, though.
+ * This is an approximation; load-time text relocation can make sense for shared libs.
+ * (but it's dangerous because PC32 relocs might overflow)
+ *)
+ let (binding_is_final : Command_line.link_option Pset.set -> binding -> bool)
+ = (fun options -> (fun ((ref_idx, ref1, ref_item), maybe_def) ->
+ (match maybe_def with
+ (* Weak bindings to 0 are final (though libcrunch wishes they weren't!). *)
+ None -> true
+ | Some (def_idx, def, def_item) -> Nat_big_num.equal
+(
+ (* Bindings to non-global symbols are final. *)get_elf64_symbol_binding def.def_syment) stb_local
+ ||
+(
+ (* Bindings to hidden- or protected- or internal-visibility globals
+ * are final. *)Pset.mem (get_symbol_visibility def.def_syment.elf64_st_info)(Pset.from_list Nat_big_num.compare [ stv_hidden; stv_protected; stv_internal ])
+ ||
+(
+ (* Bindings to global symbols are non-final
+ * *unless*
+ * 1. the symbol definition is [going to end up] in the executable
+ * 2. we're -Bsymbolic, outputting a shared object,
+ * and the symbol definition is [going to end up] within the same shared object
+ * 3. we're -Bsymbolic-functions, outputting a shared object,
+ * and the symbol definition has STT_FUNC and is [going to end up] within the same shared object
+ *
+ * ... where "going to end up in an X" means "we're building an X and def is in a RelocELF rather than a SharedELF".
+ *)
+ (* 1. *)(building_executable && def_is_in_reloc def_item) ||
+ (* 2 and 3. *)
+ (building_shared_library && (def_is_in_reloc def_item &&
+ ( ( Nat_big_num.equal(get_elf64_symbol_type def.def_syment) stt_func && bind_functions_early)
+ || ( not (Nat_big_num.equal (get_elf64_symbol_type def.def_syment) stt_func) && bind_non_functions_early)
+ ))
+ )))
+ (* FIXME: does it matter if the binding is intra-object or inter-object?
+ * We don't get inter-object bindings much to non-{default global}s. How much? *)
+ )))
+ in
+ let (reloc_is_absolute : reloc_site -> bool) = (fun rs ->
+ let kind = (get_elf64_relocation_a_type rs.ref_relent) in
+ let (is_abs, _) = (a.reloc kind) in
+ is_abs)
+ in
+ (* What's our decision for this reloc? leave, apply, MakePIC?
+ * In fact we return both a decision and a maybe-function to create
+ * the consequent reloc.
+ * In what circumstances do we leave the reloc? If we're making an executable
+ and the definition is not in a relocatable input file or archive or script.
+ Or if we're making a shared library and the reference is "from data".
+ What does "from data" mean? I think it means it's a PC-relative reloc.
+ If we compile our code to do movabs $addr, even from a *local* address,
+ it's not PIC because that address needs load-time fixup.
+ So actually it's "is absolute address" again.
+ *)
+ let b = (retrieve_binding_for_ref
+ instance_Basic_classes_Eq_Num_natural_dict r linkable_idx item bindings_by_name)
+ in
+ let ((ref_idx, _, ref_item), maybe_def) = b
+ in
+ let defined_in_shared_lib = ((match maybe_def with
+ Some (def_idx, def, def_item) -> not (def_is_in_reloc def_item)
+ | None -> false (* i.e. the "definition", 0, can be "linked in" *)
+ ))
+ in
+ let decide = (fun decision -> (
+ (*let _ = errln ("Decided to " ^ match decision with
+ LeaveReloc -> "leave"
+ | ApplyReloc -> "apply"
+ end ^ " relocation in linkable " ^ (show ref_item) ^ "'s image, bound to " ^
+ match maybe_def with
+ Just(def_idx, def, def_item) -> "a definition called `" ^ def.def_symname ^ "' in linkable " ^
+ (show def_item)
+ | Nothing -> "no definition"
+ end
+ )
+ in*)
+ Pset.add (SymbolRef({
+ ref = (r.ref)
+ ; maybe_reloc = (r.maybe_reloc)
+ ; maybe_def_bound_to = (Some (decision,
+ (match maybe_def with
+ Some(def_idx, def, def_item) ->
+ Some { def_symname = (def.def_symname)
+ ; def_syment = (def.def_syment)
+ ; def_sym_scn = (def.def_sym_scn)
+ ; def_sym_idx = (def.def_sym_idx)
+ ; def_linkable_idx = def_idx
+ }
+ | None -> None
+ )
+ ))
+ }
+ ), maybe_range) acc_by_tag,
+((reloc1, b, decision) :: rev_acc_decisions)))
+ in
+ if (building_executable && defined_in_shared_lib)
+ || (building_shared_library && (reloc_is_absolute reloc1))
+ then decide LeaveReloc
+ else
+ (* In what circumstances do we apply the reloc? If it's a final binding. *)
+ if binding_is_final options b then decide ApplyReloc
+ (* In what circumstances do we MakePIC? If it's a non-absolute relocatable field
+ * and we're building a shared library.
+ *
+ * PIC is a kind of "consequent relocation", so let's think through it.
+ * A call site that calls <printf> will usually be non-final (overridable).
+ * Output needs to call <printf@plt>. BUT the trick is as follows:
+ * the reloc is swizzled so that it binds to the PLT slot <printf@plt>;
+ * the PLT slot is locally generated, so no reloc is needed.
+ * So the point is that
+ * a *non*-applied reloc
+ * might still need "applying" after a fashion (swizzling).
+ * The initial reloc is removed! Since PLT means removing relocs from code
+ * and reproducing their effect using a PLT.
+ * That's why we need this special MakePIC behaviour.
+ * Actually, generalise to a ChangeRelocTo.
+ *
+ * What about data?
+ * Suppose I have a shared library containing a read-only pointer to <environ>.
+ * The binding is final because <environ> is defined in the executable, say.
+ * PIC doesn't handle this case -- we still need load-time relocation.
+ * It's PIC, not PID: data can't be made position-independent.
+ *
+ * So, at least for simple cases of PIC, we don't need consequent relocation if
+ * we don't apply the reloc. We'll be removing the reloc. But we *do* need to create
+ * extra stuff later (PLT, GOT).
+ *)
+ else if building_shared_library then decide (* MakePIC *) (ChangeRelocTo(Nat_big_num.of_int 0, r.ref, reloc1)) (* FIXME *)
+ (* The above are non-exclusive and non-exhaustive. Often, more than one option is available,
+ * ABIs / practice makes an arbitrary choice. For example, final bindings
+ * within a library could be realised the PIC way, but aren't (it'd create a
+ * pointless indirection). *)
+ else failwith "didn't know what to do with relocation"
+ | None ->
+ (* symbol ref with no reloc *)
+ pass_through
+ )
+ | _ -> pass_through
+ )
+ )) ((Pset.from_list (pairCompare compare (maybeCompare (pairCompare compare (pairCompare Nat_big_num.compare Nat_big_num.compare)))) []), []) (Pset.elements img2.by_tag))
+ in
+ (List.rev rev_decisions, { elements = (img2.elements)
+ ; by_tag = new_by_tag
+ ; by_range = (by_range_from_by_tag
+ 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))) new_by_tag)
+ }))
+
+(*val strip_metadata_sections : list (reloc_site * binding * reloc_decision) -> abi any_abi_feature -> elf_memory_image -> elf_memory_image*)
+let strip_metadata_sections reloc_decisions a img2:(any_abi_feature)annotated_memory_image=
+ (let (section_tags, section_ranges) = (elf_memory_image_section_ranges img2)
+ in
+ let rel_sections = (Lem_list.mapMaybe (fun (range_tag1, (el_name, el_range)) ->
+ (match range_tag1 with
+ FileFeature(ElfSection(idx1, isec1)) ->
+ if Pset.mem isec1.elf64_section_type(Pset.from_list Nat_big_num.compare [ sht_rel; sht_rela ])
+ then Some (idx1, isec1, el_name)
+ else None
+ | _ -> None
+ )
+ ) (list_combine section_tags section_ranges))
+ in
+ let discarded_sections_with_element_name = (Lem_list.mapMaybe (fun (range_tag1, (el_name, el_range)) ->
+ (match range_tag1 with
+ FileFeature(ElfSection(idx1, isec1)) ->
+ if a.section_is_special isec1 img2 (* discard reloc sections, and we'll re-add them *)
+ then Some (el_name, range_tag1) else None
+ )
+ ) (list_combine section_tags section_ranges))
+ in
+ let discarded_elements_map = (List.fold_left (fun m -> (fun (el_name, range_tag1) ->
+ (*let _ = errln ("Discarding a metadata element named `" ^ el_name ^ "'") in*)
+ Pmap.add el_name range_tag1 m
+ )) (Pmap.empty compare) discarded_sections_with_element_name)
+ in
+ let filtered_image = (Memory_image.filter_elements (fun (el_name, el) -> not (Pmap.mem el_name discarded_elements_map)) img2)
+ in
+ let new_reloc_section_length = (fun idx1 -> (fun isec1 ->
+ let retained_relocs_from_this_section = (let x2 =
+ ([]) in List.fold_right
+ (fun(reloc1, b, decision) x2 ->
+ if Nat_big_num.equal (* is it from this section? *) reloc1.ref_rel_scn
+ idx1 (* are we retaining it? *) && (decision = LeaveReloc) then
+ (reloc1, b, decision) :: x2 else x2) reloc_decisions x2)
+ in Nat_big_num.mul (length retained_relocs_from_this_section) isec1.elf64_section_entsize
+ ))
+ in
+ let (new_reloc_elements, new_reloc_tags_and_ranges) = (List.split (let x2 =
+ ([]) in List.fold_right
+ (fun(idx1, isec1, el_name) x2 ->
+ if Nat_big_num.greater (new_reloc_section_length idx1 isec1)
+ (Nat_big_num.of_int 0) then
+ (let new_len = (new_reloc_section_length idx1 isec1) in
+ let new_el = ({ startpos = None ; length1 = (Some new_len); contents =
+ ([]) }) in
+ let new_isec = ({ elf64_section_name = (isec1.elf64_section_name)
+ ; elf64_section_type = (isec1.elf64_section_type)
+ ; elf64_section_flags = (isec1.elf64_section_flags)
+ ; elf64_section_addr =(Nat_big_num.of_int 0) (* should be 0 anyway *)
+ ; elf64_section_offset =(Nat_big_num.of_int 0) (* ignored *)
+ ; elf64_section_size = new_len
+ ; elf64_section_link = (isec1.elf64_section_link)
+ ; elf64_section_info = (isec1.elf64_section_info)
+ ; elf64_section_align = (isec1.elf64_section_align)
+ ; elf64_section_entsize = (isec1.elf64_section_entsize)
+ ; elf64_section_body = Byte_sequence.empty (* ignored *)
+ ; elf64_section_name_as_string = (isec1.elf64_section_name_as_string)
+ }) in
+ let new_meta = (FileFeature (ElfSection (idx1, new_isec))) in
+ ((el_name, new_el), (new_meta, Some
+ (el_name, (Nat_big_num.of_int 0, new_len)))))
+ :: x2 else x2) rel_sections x2))
+ in
+ let new_by_tag = (Pset.bigunion (pairCompare compare (maybeCompare (pairCompare compare (pairCompare Nat_big_num.compare Nat_big_num.compare))))(Pset.from_list (Pset.compare_by (pairCompare compare (maybeCompare (pairCompare compare (pairCompare Nat_big_num.compare Nat_big_num.compare))))) [ filtered_image.by_tag; (Pset.from_list (pairCompare compare (maybeCompare (pairCompare compare (pairCompare Nat_big_num.compare Nat_big_num.compare)))) new_reloc_tags_and_ranges) ]))
+ in
+ {
+ elements = (List.fold_right Pmap.union [filtered_image.elements; Lem_map.fromList
+ (instance_Map_MapKeyType_var_dict instance_Basic_classes_SetType_var_dict) new_reloc_elements] (Pmap.empty compare))
+ ; by_tag = new_by_tag
+ ; by_range = (by_range_from_by_tag
+ 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))) new_by_tag)
+ })
+
+
+let expand_sections_for_one_image a options bindings_by_name linkable_idx item strip_relocs:(reloc_site*binding*reloc_decision)list*(any_abi_feature)annotated_memory_image*(input_spec)list=
+ ((match item with
+ (RelocELF(img2), (fname1, blob, origin), input_opts) ->
+ (*let _ = List.foldl (fun _ -> fun (isec, shndx) ->
+ let _ = errln ("For file " ^ fname ^ " before stripping, saw section idx " ^ (show shndx) ^
+ " with name " ^ isec.elf64_section_name_as_string ^ ", first 20 bytes: " ^ (show (take 20 (
+ (let maybe_elname = elf_memory_image_element_coextensive_with_section shndx img
+ in
+ match maybe_elname with
+ Nothing -> failwith ("impossible: no such section (" ^ (show shndx) ^ ") in image of " ^ fname)
+ | Just idstr ->
+ match Map.lookup idstr img.elements with
+ Just el -> el.contents
+ | Nothing -> failwith "no such element"
+ end
+ end
+ )))))
+ in
+ ()
+ ) () (elf_memory_image_sections_with_indices img)
+ in*)
+ let ((reloc_decisions : (reloc_site * binding * reloc_decision) list), marked_img) = (mark_fate_of_relocs linkable_idx a options bindings_by_name item img2)
+ in
+ (* Now we have a decision for each reloc: Leave, Apply, MakePIC. Which ones
+ * do we materialize? Only the Leave ones, for now. To support -q we'll
+ * have to support tweaking this.
+ *
+ * For each relocation that we Leave, we figure out its originating section
+ * and re-create a lookalike in the memory image.
+ *
+ * We also get called for the "generated" memory image that contains .plt,
+ * .rela.plt and so on. We don't strip these, since they actually contain relocs
+ * that need to go directly into the output file. That's what the strip_relocs
+ * argument is for. FIXME: refactor this into two functions.
+ *)
+ let stripped_img_with_reloc_sections = (if strip_relocs
+ then (*let _ = errln ("Discarding metadata sections from image of `" ^ fname ^ "'") in*)
+ strip_metadata_sections reloc_decisions a marked_img
+ else marked_img)
+ in
+ (* Now we have a whole new image! It differs from the old one in that
+ * - non-special sections have been stripped
+ * - the relocs we want to participate in linking have been materialized.
+ *)
+ (* The "-q" option is tricky. It causes all incoming relocs to be retained, but
+ * they *don't* participate in linking -- notice that the default linker script
+ * pulls all .rela.* sections into .rela.dyn, whereas these ones *don't* go in there.
+ * So FIXME: to support this, we need a way to re-add them, probably when we
+ * generate meta-output like .symtab etc.. *)
+ let inputs =
+
+ (List.rev_append (List.rev (let x2 =
+ ([]) (* not (a.section_is_special isec img *)in
+ List.fold_right
+ (fun(isec1, shndx1) x2 ->
+ if true then
+ (let short_name = (short_string_of_linkable_item item) in
+ (*let _ = errln ("For file " ^ short_name ^ " after stripping, saw section idx " ^ (show shndx) ^
+ " with name " ^ isec.elf64_section_name_as_string ^ ", first 20 bytes: " ^ (show (take 20 (
+ (let maybe_elname = elf_memory_image_element_coextensive_with_section shndx stripped_img_with_reloc_sections
+ in
+ match maybe_elname with
+ Nothing -> failwith ("impossible: no such section (matching " ^ (show shndx) ^ ")")
+ | Just idstr ->
+ match Map.lookup idstr stripped_img_with_reloc_sections.elements with
+ Just el -> el.contents
+ | Nothing -> failwith "no such element"
+ end
+ end
+ )))))
+ in*)
+ InputSection
+ ({ idx = linkable_idx ; fname = short_name
+ ; img = stripped_img_with_reloc_sections ; shndx = shndx1
+ ; secname = (isec1.elf64_section_name_as_string) ; isec = isec1
+ })) :: x2 else x2)
+ (elf_memory_image_sections_with_indices stripped_img_with_reloc_sections)
+ x2)) (
+ (* One item per common symbol. FIXME: what about common symbols that have the same name?
+ * We need to explicitly instantiate common symbols somewhere, probably here.
+ * This means dropping any that are unreferenced (does it?) and merging any multiply-defined.
+ * Actually, we deal with section merging at the same time as section concatenation, so during
+ * linker script processing. For discarding unused common symbols, I *think* that this has already
+ * been done by discarding unreferenced inputs. *)
+ let common_symbols = (all_common_symbols stripped_img_with_reloc_sections)
+ in
+ (*let _ = errln ("Expanding " ^ (show (length common_symbols)) ^ " common symbols")
+ in*)
+ let x2 = ([]) in List.fold_right
+ (fun def x2 ->
+ if
+ (*let _ = Missing_pervasives.outln ((space_padded_and_maybe_newline 20 def.def_symname)
+ ^ (let hexstr = "0x" ^ (hex_string_of_natural (natural_of_elf64_xword def.def_syment.elf64_st_size))
+ in
+ space_padded_and_maybe_newline 20 hexstr
+ )
+ ^
+ fname)
+ in*)
+ true then
+ Common (linkable_idx, fname1, stripped_img_with_reloc_sections, def) ::
+ x2 else x2) common_symbols x2
+ ))
+ in (reloc_decisions, stripped_img_with_reloc_sections, inputs)
+ | _ -> failwith "non-reloc linkable not supported yet"
+))
+
+type reloc_resolution = reloc_site * binding * reloc_decision
+
+(*val default_merge_generated : abi any_abi_feature -> elf_memory_image -> list (list Linker_script.input_spec) -> list (list Linker_script.input_spec)*)
+let default_merge_generated a generated_img input_spec_lists:((input_spec)list)list=
+(
+ (* We expand the sections in the generated image and hang them off
+ * the first linkable item. *)
+ (*let _ = errln ("Generated image has " ^ (show (Map.size generated_img.elements)) ^ " elements and " ^ (show (Set.size (generated_img.by_tag))) ^
+ " metadata elements (sanity: " ^ (show (Set.size (generated_img.by_range))) ^ ")")
+ in*)let dummy_input_item = ("(no file)", Input_list.Reloc(Sequence([])), ((Command_line.File(Command_line.Filename("(no file)"), Command_line.null_input_file_options)), [InCommandLine(Nat_big_num.of_int 0)]))
+ in
+ let dummy_linkable_item = (RelocELF(generated_img), dummy_input_item, Input_list.null_input_options)
+ in
+ let (_, _, generated_inputs) = (expand_sections_for_one_image a(Pset.from_list compare []) (Pmap.empty compare)(Nat_big_num.of_int 0) dummy_linkable_item false)
+ in
+ (*let _ = errln ("Generated image yielded " ^ (show (length generated_inputs)) ^ " input items")
+ in*)
+ (* okay, hang them off the first one *)
+ (match input_spec_lists with
+ [] -> failwith "link job empty"
+ | first_input_list :: more_input_lists -> ( List.rev_append (List.rev first_input_list) generated_inputs) :: more_input_lists
+ ))
+ (* input_spec_lists *)
+
+(*val expand_sections_for_all_inputs : abi any_abi_feature -> set Command_line.link_option -> binding_map ->
+ (abi any_abi_feature -> elf_memory_image -> list (list Linker_script.input_spec) -> list (list Linker_script.input_spec)) (* merge_generated *) ->
+ list (natural * Linkable_list.linkable_item) ->
+ list (list reloc_resolution * elf_memory_image * list Linker_script.input_spec)*)
+let expand_sections_for_all_inputs a options bindings_by_name merge_generated idx_and_linkables:((reloc_site*binding*reloc_decision)list*(any_abi_feature)annotated_memory_image*(input_spec)list)list=
+ (let (expanded_reloc_lists, expanded_imgs, linker_script_input_lists) = (unzip3 (Lem_list.map (fun (idx1, linkable) ->
+ expand_sections_for_one_image a options bindings_by_name idx1 linkable true) idx_and_linkables))
+ in
+ let fnames = (Lem_list.map (fun (idx1, (_, (fname1, _, _), _)) -> fname1) idx_and_linkables)
+ in
+ (* We pass the collection of linkable images and reloc decision lists
+ * to an ABI tap function.
+ *
+ * This returns us a new *image* containing all the elements. Logically
+ * this is another participant in the link, which we could expand separately.
+ * A personality function takes care of actually merging it back into the
+ * linker script inputs... in the case of the GNU linker, this means pretending
+ * the generated stuff came from the first input object.
+ *)
+ let generated_img = (a.generate_support (* expanded_relocs *) (list_combine fnames expanded_imgs))
+ in
+ (* We need to return a
+ *
+ * list (list reloc_decision * elf_memory_image * list Linker_script.input_spec)
+ *
+ * i.e. one item for every input image. *)
+ let (final_input_spec_lists : ( Linker_script.input_spec list) list) = (merge_generated a generated_img linker_script_input_lists)
+ in
+ zip3 expanded_reloc_lists expanded_imgs final_input_spec_lists)
+
+(*val relocate_output_image : abi any_abi_feature -> map string (list (natural * binding)) -> elf_memory_image -> elf_memory_image*)
+let relocate_output_image a bindings_by_name img2:(any_abi_feature)annotated_memory_image=
+ (let relocs = (Multimap.lookupBy0
+ (instance_Basic_classes_Ord_Memory_image_range_tag_dict
+ instance_Basic_classes_Ord_Abis_any_abi_feature_dict) (instance_Basic_classes_Ord_Maybe_maybe_dict
+ (instance_Basic_classes_Ord_tup2_dict
+ Lem_string_extra.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
+ instance_Abi_classes_AbiFeatureTagEquiv_Abis_any_abi_feature_dict) (SymbolRef(null_symbol_reference_and_reloc_site))
+ img2.by_tag)
+ in
+
+ (*let _ = errln ("For __libc_multiple_threads (in relocate_output_image), we have " ^
+ (let all_bs = match Map.lookup "__libc_multiple_threads" bindings_by_name with
+ Just l -> l
+ | Nothing -> []
+ end
+ in
+ ((show (length all_bs)) ^
+ " bindings, of which " ^
+ (show (length (List.filter (fun (bi, ((ref_idx, ref, ref_item), maybe_def)) ->
+ match maybe_def with
+ Just _ -> true
+ | _ -> false
+ end
+ ) all_bs))) ^ " have defs")))
+ in*)
+ let apply_reloc = (fun img2 -> fun (el_name, start, len) -> fun symref_and_reloc_site -> fun symaddr -> (
+ let reloc_site1 = ((match symref_and_reloc_site.maybe_reloc with
+ None -> failwith "impossible: no reloc site during relocation"
+ | Some r -> r
+ ))
+ in
+ let (field_is_absolute_addr, applyfn) = (a.reloc (get_elf64_relocation_a_type reloc_site1.ref_relent))
+ in
+ let element1 = ((match Pmap.lookup el_name img2.elements with
+ None -> failwith "impossible: reloc site in nonexistent section"
+ | Some e -> e
+ ))
+ in
+ let site_address = ((match element1.startpos with
+ Some addr -> Nat_big_num.add addr start
+ | None -> failwith "error: relocation in section with no address"
+ ))
+ in
+ let (width, calculate) = (applyfn img2 site_address symref_and_reloc_site)
+ in
+ let existing_field = (extract_natural_field width element1 start)
+ in
+ (*let _ = errln ("Existing field has value 0x" ^ (hex_string_of_natural existing_field))
+ in*)
+ (*let _ = errln ("Symaddr has value 0x" ^ (hex_string_of_natural symaddr))
+ in*)
+ let addend = (Nat_big_num.of_int64 reloc_site1.ref_relent.elf64_ra_addend)
+ in
+ let new_field_value = (calculate symaddr addend existing_field)
+ in
+ (*let _ = errln ("Calculated new field value 0x" ^ (hex_string_of_natural new_field_value))
+ in*)
+ let new_element = (write_natural_field new_field_value width element1 start)
+ in
+ {
+ elements = (Pmap.add el_name new_element (Pmap.remove el_name img2.elements))
+ ; by_tag = (Pset.diff img2.by_tag(Pset.from_list (pairCompare compare (maybeCompare (pairCompare compare (pairCompare Nat_big_num.compare Nat_big_num.compare)))) [(SymbolRef(symref_and_reloc_site), Some(el_name, (start, len)))]))
+ ; by_range = (Pset.diff img2.by_range(Pset.from_list (pairCompare (maybeCompare (pairCompare compare (pairCompare Nat_big_num.compare Nat_big_num.compare))) compare) [(Some(el_name, (start, len)), SymbolRef(symref_and_reloc_site))]))
+ }
+ ))
+ in
+ let relocated_img = (List.fold_left (fun acc_img -> (fun (tag, maybe_range) ->
+ (match tag with
+ SymbolRef(x) -> (match x.maybe_reloc with
+ Some rs ->
+ (match maybe_range with
+ None -> failwith "impossible: reloc site with no range"
+ | Some (el_name, (start, len)) ->
+ (*let _ = errln ("During relocation, saw a reloc site in element " ^ el_name ^ ", offset 0x" ^
+ (hex_string_of_natural start) ^ ", length 0x" ^ (hex_string_of_natural len) ^
+ ", reloc type " ^ (* a. *) Abi_amd64_relocation.string_of_amd64_relocation_type (get_elf64_relocation_a_type rs.ref_relent) ^
+ ", symbol name `" ^ x.ref.ref_symname ^ "'")
+ in*)
+ let symaddr = ((match x.maybe_def_bound_to with
+ Some(ApplyReloc, Some(bound_def)) ->
+ (* Here we are mapping
+ * *from* the definition found in an input object during resolution (bound_def)
+ * *to* the corresponding symbol in the output image, now that we've built it.
+ *
+ * Q. What about ABI-specific interventions, e.g.
+ * redirecting a symbol reference to its GOT or PLT slot?
+ * A. Indeed, we need to ask the ABI to give us the target
+ * address. The default implementation is just to look for
+ * a matching symbol and use its address. But ABIs can do
+ * wacky things if they like.
+ *)
+ a.get_reloc_symaddr bound_def img2 x.maybe_reloc
+ | None -> failwith "no def found for bound-to symbol"
+ | Some(ApplyReloc, None) ->
+ (*let _ = errln "No definition, so we think this is a weak reference; giving it value 0."
+ in*)
+ (* CHECK: does the syment say it's weak? *)
+ if not (Nat_big_num.equal (get_elf64_symbol_binding x.ref.ref_syment) stb_weak) then
+ (*let _ = errln "Actually not weak! bailing"
+ in*)
+ failwith "not a weak reference, but no binding"
+ else Nat_big_num.of_int
+ (* Weak symbol. *)0
+ | Some(LeaveReloc, _) ->
+ (* We shouldn't be seeing this, given that we're applying the reloc Right Now. *)
+ failwith "internal error: applying reloc that is not to be applied"
+ ))
+ in
+ (*let _ = errln ("Got symaddr: 0x" ^ (hex_string_of_natural symaddr))
+ in*)
+ apply_reloc acc_img (el_name, start, len) x symaddr
+ )
+ | None -> (* okay, do nothing *) acc_img
+ )
+ | _ -> failwith "impossible: not a symbol ref"
+ )
+ )) img2 relocs)
+ in
+ relocated_img)
+
+(*val link : address_expr_fn_map allocated_sections_map -> linker_control_script -> abi any_abi_feature -> set Command_line.link_option -> linkable_list -> elf_memory_image*)
+let link alloc_map script1 a options linkables:(any_abi_feature)annotated_memory_image=
+ (let initial_included_indices = (mapMaybei (fun i -> (fun (obj, inp, (opts : input_options)) ->
+ if opts.item_force_output
+ then Some i
+ else None
+ )) linkables)
+ in
+ let linker_script_linkable_idx = (length linkables)
+ in
+ let defmap = (all_definitions_by_name linkables)
+ in
+ let (accumulated_bindings : binding list)
+ =
+( (* accumulate_bindings_bf a linkables defmap {} initial_included_indices [] *)accumulate_bindings_objectwise_df a linkables defmap [](Pset.from_list Nat_big_num.compare []) initial_included_indices)
+ in
+ (* Keep a map whose keys are referenced objects, and whose values are
+ * *some* (diagnostic purposes only) reference to that linkable. *)
+ let referenced_object_indices_and_reasons = (List.fold_left (fun acc_m -> (fun ((ref_idx, ref_sym, ref_linkable), maybe_def_idx_and_sym_and_linkable) ->
+ (match maybe_def_idx_and_sym_and_linkable with
+ None -> acc_m
+ | Some (def_idx, def_sym, def_linkable) ->
+ (* Make sure the map contains this key. *)
+ if (Lem.option_equal (Lem.pair_equal (=)
+ (tripleEqual instance_Basic_classes_Eq_var_dict
+ (instance_Basic_classes_Eq_tup3_dict
+ instance_Basic_classes_Eq_string_dict
+ instance_Basic_classes_Eq_var_dict
+ (instance_Basic_classes_Eq_tup2_dict
+ instance_Basic_classes_Eq_var_dict
+ (instance_Basic_classes_Eq_list_dict
+ instance_Basic_classes_Eq_var_dict)))
+ instance_Basic_classes_Eq_var_dict)) (Pmap.lookup def_idx acc_m) None)
+ then Pmap.add def_idx (ref_sym, ref_linkable) acc_m
+ else acc_m
+ )
+ )) ((Pmap.empty Nat_big_num.compare) : (Nat_big_num.num, (symbol_reference * linkable_item)) Pmap.map) accumulated_bindings)
+ in
+ (* Print something similar to GNU ld's linker map output, about included archive members. *)
+ (*let _ = Missing_pervasives.outln "Archive member included to satisfy reference by file (symbol)\n" in*)
+ let linkables_not_discarded = (mapMaybei (fun i -> (fun (obj, inp, opts) ->
+ let referenced_object_map_entry = (Pmap.lookup i referenced_object_indices_and_reasons)
+ in
+ let referenced = ( not ((Lem.option_equal (Lem.pair_equal (=)
+ (tripleEqual instance_Basic_classes_Eq_var_dict
+ (instance_Basic_classes_Eq_tup3_dict
+ instance_Basic_classes_Eq_string_dict
+ instance_Basic_classes_Eq_var_dict
+ (instance_Basic_classes_Eq_tup2_dict
+ instance_Basic_classes_Eq_var_dict
+ (instance_Basic_classes_Eq_list_dict
+ instance_Basic_classes_Eq_var_dict)))
+ instance_Basic_classes_Eq_var_dict)) referenced_object_map_entry None)))
+ in
+ (* Print our link map thing *)
+ (*let _ = (
+ if (not referenced) then () else
+ (* Did it come from an archive? *)
+ let (name, _, (inp_unit, coordlist)) = inp in
+ match coordlist with
+ InArchive(aid, aidx, aname, _) :: _ ->
+ (* yes, from an archive, so print a line *)
+ let (ref_sym, (ref_obj, (ref_name, ref_blob, ref_origin), ref_opts)) = match referenced_object_map_entry with
+ Just(x, y) -> (x, y)
+ | Nothing -> failwith "impossible: referenced item has no definition"
+ end
+ in
+ let lhs_name = aname ^ "(" ^ name ^ ")"
+ in
+ let lhs_name_len = stringLength lhs_name
+ in
+ let spacing = if lhs_name_len >= 29
+ then ("\n" ^ (makeString 30 #' '))
+ else makeString (30 - lhs_name_len) #' '
+ in
+ Missing_pervasives.outln (
+ lhs_name ^ spacing ^
+ (match ref_origin with
+ (_, InArchive(bid, bidx, bname, _) :: _) -> bname ^ "(" ^ ref_name ^ ")"
+ | _ -> ref_name
+ end)
+ ^ " (" ^ ref_sym.ref_symname ^ ")"
+ )
+ | _ (* not from an archive *) -> ()
+ end
+ )
+ in*)
+ if referenced || opts.item_force_output
+ then Some (i, (obj, inp, opts))
+ else None
+ )) linkables)
+ in
+ (*let _ = Missing_pervasives.outln "\nAllocating common symbols\nCommon symbol size file\n"
+ in*)
+ (* We have to do a pass over relocations quite early. This is because relocs *do* participate
+ * in linking. For each reloc, we need to decide whether to apply it or not. For those not applied,
+ * we include it in a synthesised section that participates in linking.
+ *
+ * Similarly, the GOT needs to participate in linking, so that it gets assigned an address
+ * at the appropriate place (as determined by the script). So we have to generate the GOT
+ * *before* running the linker script. The GNU linker hangs the whole GOT and PLT content
+ * off the first input object (usually crt1.o). In general, expand_sections calls an ABI tap
+ * which synthesises all the necessary things, like (in the GNU case) the .got and .plt sections
+ * hanging off the first input object. *)
+ let (initial_bindings_by_name : (string, ( (Nat_big_num.num * binding)list)) Pmap.map) =
+ (List.fold_left (fun m -> fun (b_idx, ((ref_idx, ref1, ref_item), maybe_def)) -> (match Pmap.lookup ref1.ref_symname m with
+ None -> Pmap.add ref1.ref_symname [ (b_idx, ((ref_idx, ref1, ref_item), maybe_def)) ] m
+ | Some ((bi, b) :: more) -> Pmap.add ref1.ref_symname ((b_idx, ((ref_idx, ref1, ref_item), maybe_def)) :: ((bi, b) :: more)) m
+ | _ -> failwith "impossible: found empty list in map lacking empties by construction"
+ )) (Pmap.empty compare) (Lem_list.mapi (fun i -> fun b -> (Nat_big_num.of_int i, b)) accumulated_bindings))
+ in
+ let (expanded_triples : ( reloc_resolution list * elf_memory_image * Linker_script.input_spec list) list)
+ = (expand_sections_for_all_inputs a options initial_bindings_by_name default_merge_generated linkables_not_discarded)
+ in
+ let (reloc_resolutions, imgs, input_lists) = (unzip3 expanded_triples)
+ in
+ let input_sections = (list_concat input_lists)
+ in
+ let seen_ordering = (fun is1 -> (fun is2 -> (
+ let toNaturalList = (fun is -> (
+ (* We're mapping the item to a list of naturals that determine a
+ * lexicographic order. The list has a fixed depth:
+ *
+ * [within-commandline, within-group, within-archive, section-or-symbol]
+ *
+ * For .o files on the command line, we use the command line order. This
+ * is the first level in the hierarchy.
+ *
+ * For .a files with --whole-archive, we want to do the same. Do this
+ * by using archive position as the second level of the hierarchy, *if*
+ * the item is marked as force_output.
+ *
+ * For other archives, "order seen" means something different: it's
+ * the order in which they were "pulled in" during input enumeration. Another
+ * way to say this is that they're ordered by the first binding that was
+ * made to them. We map these to numbers starting from the size of the archive,
+ * i.e. so that "force_output" makes an element appear sooner. In practice
+ * we won't get a mixture of force_output and non- in the same archive,
+ * so each archive will use only one of the two orderings.
+ *
+ * How do sections order relative to common symbols? Again, in practice it
+ * doesn't matter because no input query will get a mixture of the two.
+ * For symbols, we start the numbering from the number of sections in the file,
+ * so symbols always appear later in the sortd order.
+ *)
+ let (linkable_idx, section_or_symbol_idx) = ((match is with
+ Common(idx1, fname1, img2, def) -> (idx1, Nat_big_num.add
+ (let (_, l) = (elf_memory_image_section_ranges img2) in length l) def.def_sym_idx)
+ | InputSection(isrec) -> (isrec.idx, isrec.shndx)
+ ))
+ in
+ (match Lem_list.list_index linkables (Nat_big_num.to_int linkable_idx) with
+ None -> failwith "impossible: linker input not in linkables list"
+ | Some (obj, (fname1, blob, (inp_unit, coords)), options) ->
+ let (our_cid, our_gid, our_aid, maybe_archive_size) = ((match coords with
+ InArchive(aid, aidx, _, asize) :: InGroup(gid1, gidx) :: [InCommandLine(cid)] -> (cid, gid1, aid, Some asize)
+ | InArchive(aid, aidx, _, asize) :: [InCommandLine(cid)] -> (cid,Nat_big_num.of_int 0, aid, Some asize)
+ | InGroup(gid1, gidx) :: [InCommandLine(cid)] -> (cid, gid1,Nat_big_num.of_int 0, None)
+ | [InCommandLine(cid)] -> (cid,Nat_big_num.of_int 0,Nat_big_num.of_int 0, None)
+ | _ -> failwith "internal error: impossible coordinates"
+ ))
+ in
+ let aid_to_use = (if options.item_force_output then our_aid
+ else (* how many elements does the archive have? *)
+ let archive_size = ((match maybe_archive_size with
+ None -> failwith "impossible: archive with no size"
+ | Some a -> a
+ ))
+ in Nat_big_num.add archive_size
+ (* search the bindings: we want the index of the first binding
+ that refers to this object.
+ *)
+ (match Lem_list.find_index (fun ((b_ref_idx, b_ref, b_ref_item), b_maybe_def) -> (match b_maybe_def with
+ Some (b_def_idx, b_def, b_def_item) -> Nat_big_num.equal b_def_idx linkable_idx
+ | _ -> false
+ )) accumulated_bindings with
+ Some n -> Nat_big_num.of_int n
+ | None -> failwith "impossible: non-force-output object does not contain any bound-to defs"
+ ))
+ in
+ (* do we care about group idx? probably not. *)
+ [our_cid; aid_to_use; section_or_symbol_idx]
+ )
+ ))
+ in
+(lexicographic_compare Nat_big_num.compare (toNaturalList is1) (toNaturalList is2))
+ )))
+ in
+ (*
+ let get_binding_for_ref = (fun symref -> (fun linkable_idx -> (fun fname ->
+ let name_matches = match Map.lookup symref.ref_symname bindings_by_name with Just x -> x | Nothing -> [] end
+ in
+ match List.filter (fun (bi, ((r_idx, r, r_item), m_d)) -> r_idx = linkable_idx && r = symref) name_matches with
+ [(b_idx, b)] -> (b_idx, b)
+ | [] -> failwith "no binding found"
+ | _ -> failwith ("ambiguous binding found for symbol `" ^ symref.ref_symname ^ "' in file " ^ fname)
+ end
+ )))
+ in
+ *)
+ let (unrelocated_output_image_lacking_abs_symbols, bindings_by_name)
+ = (interpret_linker_control_script alloc_map script1 linkables linker_script_linkable_idx a input_sections seen_ordering default_place_orphans initial_bindings_by_name)
+ in
+ (* also copy over ABS (range-less) symbols from all included input items *)
+ let all_abs_range_tags_in_included_inputs = (List.concat (
+ Lem_list.map (fun (img2, (idx1, linkable)) ->
+ let abslist = (Lem_list.mapMaybe (fun (tag, maybeRange) ->
+ (match tag with
+ SymbolDef(ent) -> if (Lem.option_equal (Lem.pair_equal (=) (Lem.pair_equal Nat_big_num.equal Nat_big_num.equal)) maybeRange None) && Nat_big_num.equal (Nat_big_num.of_string (Uint32.to_string ent.def_syment.elf64_st_shndx)) shn_abs
+ then Some (maybeRange, ent)
+ else None
+ | _ -> None
+ )
+ ) (tagged_ranges_matching_tag
+ instance_Basic_classes_Ord_Abis_any_abi_feature_dict instance_Abi_classes_AbiFeatureTagEquiv_Abis_any_abi_feature_dict (SymbolDef(null_symbol_definition)) img2))
+ in
+ (*let _ = errln ("Copying " ^ (show (length abslist)) ^ " ABS symbols (names: " ^
+ List.foldl (fun acc -> fun str -> if stringLength acc = 0 then str else acc ^ ", " ^ str) ""
+ (List.map (fun (_, x) -> x.def_symname) abslist)
+ ^ ") from not-discarded linkable item " ^
+ (short_string_of_linkable_item linkable))
+ in*)
+ let x2 = ([]) in List.fold_right
+ (fun(maybe_range, ent) x2 ->
+ if true then
+ (maybe_range, SymbolDef
+ ({ def_symname = (ent.def_symname)
+ ; def_syment = (ent.def_syment)
+ ; def_sym_scn = (ent.def_sym_scn)
+ ; def_sym_idx = (ent.def_sym_idx)
+ ; def_linkable_idx = idx1 })) :: x2 else x2)
+ abslist x2
+ ) (list_combine imgs linkables_not_discarded)
+ ))
+ in
+ let by_range_including_abs_symbols =
+
+ (Pset.(union) unrelocated_output_image_lacking_abs_symbols.by_range
+ ((Pset.from_list (pairCompare (maybeCompare (pairCompare compare (pairCompare Nat_big_num.compare Nat_big_num.compare))) compare) all_abs_range_tags_in_included_inputs)))
+ in
+ let unrelocated_output_image = ({
+ elements = (unrelocated_output_image_lacking_abs_symbols.elements)
+ ; by_range = by_range_including_abs_symbols
+ ; by_tag = (by_tag_from_by_range
+ (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))) instance_Basic_classes_SetType_var_dict by_range_including_abs_symbols)
+ })
+ (* This image has
+ * - addresses assigned
+ * - relocations *not* applied
+ * - no entry point
+ * - some ABI features not generated? GOT, certainly. HMM.
+ -- don't consider output features, like symtabs, yet;
+ -- other ABI features have to be generated before the linker script runs (dyn relocs, GOT, PLT?)
+ -- ... so we might be okay for now.
+ *)
+ in
+ let remaining_relocs = (Multimap.lookupBy0
+ (instance_Basic_classes_Ord_Memory_image_range_tag_dict
+ instance_Basic_classes_Ord_Abis_any_abi_feature_dict) (instance_Basic_classes_Ord_Maybe_maybe_dict
+ (instance_Basic_classes_Ord_tup2_dict
+ Lem_string_extra.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
+ instance_Abi_classes_AbiFeatureTagEquiv_Abis_any_abi_feature_dict) (SymbolRef(null_symbol_reference_and_reloc_site))
+ unrelocated_output_image.by_tag)
+ in
+ let _ = (List.fold_left (fun _ -> (fun (tag, maybe_range) ->
+ let _ = ((match tag with
+ SymbolRef(x) -> (match x.maybe_reloc with
+ Some rs ->
+ (match maybe_range with
+ None -> failwith "impossible: reloc site with no range"
+ | Some (el_name, (start, len)) ->
+ () (* errln ("After linking, saw a reloc site in element " ^ el_name ^ ", offset 0x" ^
+ (hex_string_of_natural start) ^ ", length 0x" ^ (hex_string_of_natural len) ^
+ ", reloc type " ^ Abi_amd64_relocation.string_of_amd64_relocation_type (get_elf64_relocation_a_type rs.ref_relent)) *)
+ )
+ | None -> (* okay, do nothing *) ()
+ )
+ | _ -> failwith "impossible: not a symbol ref"
+ ))
+ in
+ ()
+ )) () remaining_relocs)
+ in
+ (* Before we relocate, we concretise any ABI features that we've linked in. *)
+ (*let _ = errln "Asking ABI to concretise support structures" in*)
+ let unrelocated_concrete_output_image = (a.concretise_support unrelocated_output_image)
+ in
+ let output_image = (relocate_output_image a bindings_by_name unrelocated_concrete_output_image)
+ in
+ let (maybe_entry_point_address : Nat_big_num.num option) =
+ ((match Command_line.find_option_matching_tag (Command_line.EntryAddress(Nat_big_num.of_int 0)) options with
+ None -> a.guess_entry_point output_image
+ | Some(Command_line.EntryAddress(x)) -> Some x
+ ))
+ in
+ (match maybe_entry_point_address with
+ Some addr ->
+ (match address_to_element_and_offset addr output_image with
+ Some (el_name, el_offset) ->
+ (*let _ = errln ("Tagging element " ^ el_name ^ " as containing entry point at offset 0x" ^ (hex_string_of_natural el_offset))
+ in*)
+ tag_image (EntryPoint) el_name el_offset(Nat_big_num.of_int 0) output_image
+ | None ->
+ (* HMM. entry point symbol has no address at present. *)
+ failwith ("error: entry point address 0x" ^ ((hex_string_of_natural addr) ^ " does not correspond to any element position"))
+ )
+ | None ->
+ (*let _ = errln "Warning: not tagging entry point in output image"
+ in*)
+ output_image
+ ))