12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010(*Generated by Lem from link.lem.*)openLem_basic_classesopenLem_functionopenLem_stringopenLem_tupleopenLem_boolopenLem_listopenLem_sortingopenLem_mapopenLem_set(*import Set_extra*)openLem_numopenLem_maybeopenLem_assert_extra(*import Command_line*)(*import Input_list*)openByte_sequenceopenDefault_printingopenErroropenMissing_pervasivesopenShowopenEndiannessopenElf_headeropenElf_interpreted_sectionopenElf_interpreted_segmentopenElf_section_header_tableopenElf_program_header_tableopenElf_symbol_tableopenElf_types_native_uintopenElf_relocationopenAbisopenAbi_amd64_relocation(* HACK -- remove me *)openInput_listopenLinkable_list(*import Command_line*)openMemory_imageopenMemory_image_orderingsopenElf_memory_imageopenElf_memory_image_of_elf64_fileopenLinker_scriptletall_common_symbolsimg2:(symbol_definition)list=(List.filter(fundef->Nat_big_num.equal(Uint32_wrapper.to_bigintdef.def_syment.elf64_st_shndx)shn_common)(elf_memory_image_defined_symbolsimg2))(* 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*)letdef_is_in_relocdef_item:bool=((matchdef_itemwith(RelocELF(_),_,_)->true|(ScriptAST(_),_,_)->true|_->false))letretrieve_binding_for_refdict_Basic_classes_Eq_brr_linkable_idxitembindings_by_name:('b*symbol_reference*'d)*'c=(letmaybe_found_bs=(Pmap.lookupr.ref.ref_symnamebindings_by_name)in(matchmaybe_found_bswithNone->failwith("impossible: list of bindings does not include symbol reference `"^(r.ref.ref_symname^"` (map empty)"))(* FIXME: could this actually be an "undefined symbol" link error perhaps? *)|Somebis_and_bs->(matchList.filter(fun(b_idx,((b_ref_idx,b_ref,b_ref_item),b_maybe_def))->ifdict_Basic_classes_Eq_b.isEqual_methodb_ref_idxr_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*)trueelsefalse)bis_and_bswith[]->failwith("impossible: list of bindings does not include symbol reference `"^(r.ref.ref_symname^"` (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)")))))typereloc_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)*)letmark_fate_of_relocslinkable_idxaoptionsbindings_by_nameitemimg2:(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.
*)letbuilding_executable=(Pset.mem(Command_line.OutputKind(Command_line.Executable))options)inletbuilding_shared_library=(Pset.mem(Command_line.OutputKind(Command_line.SharedLibrary))options)inletbind_functions_early=(Pset.memCommand_line.BindFunctionsEarlyoptions)inletbind_non_functions_early=(Pset.memCommand_line.BindNonFunctionsEarlyoptions)inlet(new_by_tag,rev_decisions)=(List.fold_left(fun(acc_by_tag,rev_acc_decisions)->(fun(tag,maybe_range)->letpass_through=(Pset.add(tag,maybe_range)acc_by_tag,rev_acc_decisions)in(matchtagwithSymbolRef(r)->(matchr.maybe_relocwithSomereloc1->(* 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_optionPset.set->binding->bool)=(funoptions->(fun((ref_idx,ref1,ref_item),maybe_def)->(matchmaybe_defwith(* 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_bindingdef.def_syment)stb_local||((* Bindings to hidden- or protected- or internal-visibility globals
* are final. *)Pset.mem(get_symbol_visibilitydef.def_syment.elf64_st_info)(Pset.from_listNat_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_relocdef_item)||(* 2 and 3. *)(building_shared_library&&(def_is_in_relocdef_item&&((Nat_big_num.equal(get_elf64_symbol_typedef.def_syment)stt_func&&bind_functions_early)||(not(Nat_big_num.equal(get_elf64_symbol_typedef.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? *))))inlet(reloc_is_absolute:reloc_site->bool)=(funrs->let(kind,_)=(a.parse_reloc_infors.ref_relent.elf64_ra_info)inlet(is_abs,_)=(a.relockind)inis_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.
*)letb=(retrieve_binding_for_refinstance_Basic_classes_Eq_Num_natural_dictrlinkable_idxitembindings_by_name)inlet((ref_idx,_,ref_item),maybe_def)=binletdefined_in_shared_lib=((matchmaybe_defwithSome(def_idx,def,def_item)->not(def_is_in_relocdef_item)|None->false(* i.e. the "definition", 0, can be "linked in" *)))inletdecide=(fundecision->((*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,(matchmaybe_defwithSome(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)))inif(building_executable&&defined_in_shared_lib)||(building_shared_library&&(reloc_is_absolutereloc1))thendecideLeaveRelocelse(* In what circumstances do we apply the reloc? If it's a final binding. *)ifbinding_is_finaloptionsbthendecideApplyReloc(* 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).
*)elseifbuilding_shared_librarythendecide(* MakePIC *)(ChangeRelocTo((Nat_big_num.of_int0),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). *)elsefailwith"didn't know what to do with relocation"|None->(* symbol ref with no reloc *)pass_through)|_->pass_through)))((Pset.from_list(pairComparecompare(maybeCompare(pairComparecompare(pairCompareNat_big_num.compareNat_big_num.compare))))[]),[])(Pset.elementsimg2.by_tag))in(List.revrev_decisions,{elements=(img2.elements);by_tag=new_by_tag;by_range=(by_range_from_by_taginstance_Basic_classes_SetType_var_dict(instance_Basic_classes_SetType_Maybe_maybe_dict(instance_Basic_classes_SetType_tup2_dictinstance_Basic_classes_SetType_var_dict(instance_Basic_classes_SetType_tup2_dictinstance_Basic_classes_SetType_Num_natural_dictinstance_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*)letstrip_metadata_sectionsreloc_decisionsaimg2:(any_abi_feature)annotated_memory_image=(let(section_tags,section_ranges)=(elf_memory_image_section_rangesimg2)inletrel_sections=(Lem_list.mapMaybe(fun(range_tag1,(el_name,el_range))->(matchrange_tag1withFileFeature(ElfSection(idx1,isec1))->ifPset.memisec1.elf64_section_type(Pset.from_listNat_big_num.compare[sht_rel;sht_rela])thenSome(idx1,isec1,el_name)elseNone|_->None))(list_combinesection_tagssection_ranges))inletdiscarded_sections_with_element_name=(Lem_list.mapMaybe(fun(range_tag1,(el_name,el_range))->(matchrange_tag1withFileFeature(ElfSection(idx1,isec1))->ifa.section_is_specialisec1img2(* discard reloc sections, and we'll re-add them *)thenSome(el_name,range_tag1)elseNone))(list_combinesection_tagssection_ranges))inletdiscarded_elements_map=(List.fold_left(funm->(fun(el_name,range_tag1)->(*let _ = errln ("Discarding a metadata element named `" ^ el_name ^ "'") in*)Pmap.addel_namerange_tag1m))(Pmap.emptycompare)discarded_sections_with_element_name)inletfiltered_image=(Memory_image.filter_elements(fun(el_name,el)->not(Pmap.memel_namediscarded_elements_map))img2)inletnew_reloc_section_length=(funidx1->(funisec1->letretained_relocs_from_this_section=(letx2=([])inList.fold_right(fun(reloc1,b,decision)x2->ifNat_big_num.equal(* is it from this section? *)reloc1.ref_rel_scnidx1(* are we retaining it? *)&&(decision=LeaveReloc)then(reloc1,b,decision)::x2elsex2)reloc_decisionsx2)inNat_big_num.mul(lengthretained_relocs_from_this_section)isec1.elf64_section_entsize))inlet(new_reloc_elements,new_reloc_tags_and_ranges)=(List.split(letx2=([])inList.fold_right(fun(idx1,isec1,el_name)x2->ifNat_big_num.greater(new_reloc_section_lengthidx1isec1)((Nat_big_num.of_int0))then(letnew_len=(new_reloc_section_lengthidx1isec1)inletnew_el=({startpos=None;length1=(Somenew_len);contents=([])})inletnew_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_int0))(* should be 0 anyway *);elf64_section_offset=((Nat_big_num.of_int0))(* 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)})inletnew_meta=(FileFeature(ElfSection(idx1,new_isec)))in((el_name,new_el),(new_meta,Some(el_name,((Nat_big_num.of_int0),new_len)))))::x2elsex2)rel_sectionsx2))inletnew_by_tag=(Pset.bigunion(pairComparecompare(maybeCompare(pairComparecompare(pairCompareNat_big_num.compareNat_big_num.compare))))(Pset.from_list(Pset.compare_by(pairComparecompare(maybeCompare(pairComparecompare(pairCompareNat_big_num.compareNat_big_num.compare)))))[filtered_image.by_tag;(Pset.from_list(pairComparecompare(maybeCompare(pairComparecompare(pairCompareNat_big_num.compareNat_big_num.compare))))new_reloc_tags_and_ranges)]))in{elements=(List.fold_rightPmap.union[filtered_image.elements;Lem_map.fromList(instance_Map_MapKeyType_var_dictinstance_Basic_classes_SetType_var_dict)new_reloc_elements](Pmap.emptycompare));by_tag=new_by_tag;by_range=(by_range_from_by_taginstance_Basic_classes_SetType_var_dict(instance_Basic_classes_SetType_Maybe_maybe_dict(instance_Basic_classes_SetType_tup2_dictinstance_Basic_classes_SetType_var_dict(instance_Basic_classes_SetType_tup2_dictinstance_Basic_classes_SetType_Num_natural_dictinstance_Basic_classes_SetType_Num_natural_dict)))new_by_tag)})letexpand_sections_for_one_imageaoptionsbindings_by_namelinkable_idxitemstrip_relocs:(reloc_site*binding*reloc_decision)list*(any_abi_feature)annotated_memory_image*(input_spec)list=((matchitemwith(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_relocslinkable_idxaoptionsbindings_by_nameitemimg2)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.
*)letstripped_img_with_reloc_sections=(ifstrip_relocsthen(*let _ = errln ("Discarding metadata sections from image of `" ^ fname ^ "'") in*)strip_metadata_sectionsreloc_decisionsamarked_imgelsemarked_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.. *)letinputs=(List.rev_append(List.rev(letx2=([])(* not (a.section_is_special isec img *)inList.fold_right(fun(isec1,shndx1)x2->iftruethen(letshort_name=(short_string_of_linkable_itemitem)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}))::x2elsex2)(elf_memory_image_sections_with_indicesstripped_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. *)letcommon_symbols=(all_common_symbolsstripped_img_with_reloc_sections)in(*let _ = errln ("Expanding " ^ (show (length common_symbols)) ^ " common symbols")
in*)letx2=([])inList.fold_right(fundefx2->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*)truethenCommon(linkable_idx,fname1,stripped_img_with_reloc_sections,def)::x2elsex2)common_symbolsx2))in(reloc_decisions,stripped_img_with_reloc_sections,inputs)|_->failwith"non-reloc linkable not supported yet"))typereloc_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)*)letdefault_merge_generatedagenerated_imginput_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*)letdummy_input_item=("(no file)",Input_list.Reloc(Byte_sequence.empty),((Command_line.File(Command_line.Filename("(no file)"),Command_line.null_input_file_options)),[InCommandLine((Nat_big_num.of_int0))]))inletdummy_linkable_item=(RelocELF(generated_img),dummy_input_item,Input_list.null_input_options)inlet(_,_,generated_inputs)=(expand_sections_for_one_imagea(Pset.from_listcompare[])(Pmap.emptycompare)((Nat_big_num.of_int0))dummy_linkable_itemfalse)in(*let _ = errln ("Generated image yielded " ^ (show (length generated_inputs)) ^ " input items")
in*)(* okay, hang them off the first one *)(matchinput_spec_listswith[]->failwith"link job empty"|first_input_list::more_input_lists->(List.rev_append(List.revfirst_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)*)letexpand_sections_for_all_inputsaoptionsbindings_by_namemerge_generatedidx_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_imageaoptionsbindings_by_nameidx1linkabletrue)idx_and_linkables))inletfnames=(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.
*)letgenerated_img=(a.generate_support(* expanded_relocs *)(list_combinefnamesexpanded_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_speclist)list)=(merge_generatedagenerated_imglinker_script_input_lists)inzip3expanded_reloc_listsexpanded_imgsfinal_input_spec_lists)(*val relocate_output_image : abi any_abi_feature -> map string (list (natural * binding)) -> elf_memory_image -> elf_memory_image*)letrelocate_output_imageabindings_by_nameimg2:(any_abi_feature)annotated_memory_image=(letrelocs=(Multimap.lookupBy0(instance_Basic_classes_Ord_Memory_image_range_tag_dictinstance_Basic_classes_Ord_Abis_any_abi_feature_dict)(instance_Basic_classes_Ord_Maybe_maybe_dict(instance_Basic_classes_Ord_tup2_dictLem_string_extra.instance_Basic_classes_Ord_string_dict(instance_Basic_classes_Ord_tup2_dictinstance_Basic_classes_Ord_Num_natural_dictinstance_Basic_classes_Ord_Num_natural_dict)))instance_Basic_classes_SetType_var_dict(instance_Basic_classes_SetType_Maybe_maybe_dict(instance_Basic_classes_SetType_tup2_dictinstance_Basic_classes_SetType_var_dict(instance_Basic_classes_SetType_tup2_dictinstance_Basic_classes_SetType_Num_natural_dictinstance_Basic_classes_SetType_Num_natural_dict)))(Memory_image_orderings.tagEquivinstance_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*)letapply_reloc=(funimg2->fun(el_name,start,len)->funsymref_and_reloc_site->funsymaddr->(letreloc_site1=((matchsymref_and_reloc_site.maybe_relocwithNone->failwith"impossible: no reloc site during relocation"|Somer->r))inlet(rel_type1,_)=(a.parse_reloc_inforeloc_site1.ref_relent.elf64_ra_info)inlet(field_is_absolute_addr,applyfn)=(a.relocrel_type1)inletelement1=((matchPmap.lookupel_nameimg2.elementswithNone->failwith"impossible: reloc site in nonexistent section"|Somee->e))inletsite_address=((matchelement1.startposwithSomeaddr->Nat_big_num.addaddrstart|None->failwith"error: relocation in section with no address"))inlet(width,calculate)=(applyfnimg2site_addresssymref_and_reloc_site)inletexisting_field=(extract_natural_fieldwidthelement1start)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*)letaddend=(Nat_big_num.of_int64reloc_site1.ref_relent.elf64_ra_addend)inletnew_field_value=(calculatesymaddraddendexisting_field)in(*let _ = errln ("Calculated new field value 0x" ^ (hex_string_of_natural new_field_value))
in*)letnew_element=(write_natural_fieldnew_field_valuewidthelement1start)in{elements=(Pmap.addel_namenew_element(Pmap.removeel_nameimg2.elements));by_tag=(Pset.diffimg2.by_tag(Pset.from_list(pairComparecompare(maybeCompare(pairComparecompare(pairCompareNat_big_num.compareNat_big_num.compare))))[(SymbolRef(symref_and_reloc_site),Some(el_name,(start,len)))]));by_range=(Pset.diffimg2.by_range(Pset.from_list(pairCompare(maybeCompare(pairComparecompare(pairCompareNat_big_num.compareNat_big_num.compare)))compare)[(Some(el_name,(start,len)),SymbolRef(symref_and_reloc_site))]))}))inlet(ranges_and_defs:(element_rangeoption*symbol_definition)list)=(Memory_image_orderings.defined_symbols_and_rangesinstance_Basic_classes_Ord_Abis_any_abi_feature_dictinstance_Abi_classes_AbiFeatureTagEquiv_Abis_any_abi_feature_dictimg2)inletrelocated_img=(List.fold_left(funacc_img->(fun(tag,maybe_range)->(matchtagwithSymbolRef(x)->(matchx.maybe_relocwithSomers->(matchmaybe_rangewithNone->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*)letsymaddr=((matchx.maybe_def_bound_towithSome(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_symaddrbound_defimg2ranges_and_defsx.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? *)ifnot(Nat_big_num.equal(get_elf64_symbol_bindingx.ref.ref_syment)stb_weak)then(*let _ = errln "Actually not weak! bailing"
in*)failwith"not a weak reference, but no binding"else(* Weak symbol. *)(Nat_big_num.of_int0)|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_relocacc_img(el_name,start,len)xsymaddr)|None->(* okay, do nothing *)acc_img)|_->failwith"impossible: not a symbol ref")))img2relocs)inrelocated_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*)letlinkalloc_mapscript1aoptionslinkables:(any_abi_feature)annotated_memory_image=(letinitial_included_indices=(mapMaybei(funi->(fun(obj,inp,(opts:input_options))->ifopts.item_force_outputthenSomeielseNone))linkables)inletlinker_script_linkable_idx=(lengthlinkables)inletdefmap=(all_definitions_by_namelinkables)inlet(accumulated_bindings:bindinglist)=((* accumulate_bindings_bf a linkables defmap {} initial_included_indices [] *)accumulate_bindings_objectwise_dfalinkablesdefmap[](Pset.from_listNat_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. *)letreferenced_object_indices_and_reasons=(List.fold_left(funacc_m->(fun((ref_idx,ref_sym,ref_linkable),maybe_def_idx_and_sym_and_linkable)->(matchmaybe_def_idx_and_sym_and_linkablewithNone->acc_m|Some(def_idx,def_sym,def_linkable)->(* Make sure the map contains this key. *)if(Lem.option_equal(Lem.pair_equal(=)(tripleEqualinstance_Basic_classes_Eq_var_dict(instance_Basic_classes_Eq_tup3_dictinstance_Basic_classes_Eq_string_dictinstance_Basic_classes_Eq_var_dict(instance_Basic_classes_Eq_tup2_dictinstance_Basic_classes_Eq_var_dict(instance_Basic_classes_Eq_list_dictinstance_Basic_classes_Eq_var_dict)))instance_Basic_classes_Eq_var_dict))(Pmap.lookupdef_idxacc_m)None)thenPmap.adddef_idx(ref_sym,ref_linkable)acc_melseacc_m)))((Pmap.emptyNat_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*)letlinkables_not_discarded=(mapMaybei(funi->(fun(obj,inp,opts)->letreferenced_object_map_entry=(Pmap.lookupireferenced_object_indices_and_reasons)inletreferenced=(not((Lem.option_equal(Lem.pair_equal(=)(tripleEqualinstance_Basic_classes_Eq_var_dict(instance_Basic_classes_Eq_tup3_dictinstance_Basic_classes_Eq_string_dictinstance_Basic_classes_Eq_var_dict(instance_Basic_classes_Eq_tup2_dictinstance_Basic_classes_Eq_var_dict(instance_Basic_classes_Eq_list_dictinstance_Basic_classes_Eq_var_dict)))instance_Basic_classes_Eq_var_dict))referenced_object_map_entryNone)))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*)ifreferenced||opts.item_force_outputthenSome(i,(obj,inp,opts))elseNone))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(funm->fun(b_idx,((ref_idx,ref1,ref_item),maybe_def))->(matchPmap.lookupref1.ref_symnamemwithNone->Pmap.addref1.ref_symname[(b_idx,((ref_idx,ref1,ref_item),maybe_def))]m|Some((bi,b)::more)->Pmap.addref1.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.emptycompare)(Lem_list.mapi(funi->funb->(Nat_big_num.of_inti,b))accumulated_bindings))inlet(expanded_triples:(reloc_resolutionlist*elf_memory_image*Linker_script.input_speclist)list)=(expand_sections_for_all_inputsaoptionsinitial_bindings_by_namedefault_merge_generatedlinkables_not_discarded)inlet(reloc_resolutions,imgs,input_lists)=(unzip3expanded_triples)inletinput_sections=(list_concatinput_lists)inletseen_ordering=(funis1->(funis2->(lettoNaturalList=(funis->((* 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)=((matchiswithCommon(idx1,fname1,img2,def)->(idx1,Nat_big_num.add(let(_,l)=(elf_memory_image_section_rangesimg2)inlengthl)def.def_sym_idx)|InputSection(isrec)->(isrec.idx,isrec.shndx)))in(matchLem_list.list_indexlinkables(Nat_big_num.to_intlinkable_idx)withNone->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)=((matchcoordswithInArchive(aid,aidx,_,asize)::InGroup(gid1,gidx)::[InCommandLine(cid)]->(cid,gid1,aid,Someasize)|InArchive(aid,aidx,_,asize)::[InCommandLine(cid)]->(cid,(Nat_big_num.of_int0),aid,Someasize)|InGroup(gid1,gidx)::[InCommandLine(cid)]->(cid,gid1,(Nat_big_num.of_int0),None)|[InCommandLine(cid)]->(cid,(Nat_big_num.of_int0),(Nat_big_num.of_int0),None)|_->failwith"internal error: impossible coordinates"))inletaid_to_use=(ifoptions.item_force_outputthenour_aidelse(* how many elements does the archive have? *)letarchive_size=((matchmaybe_archive_sizewithNone->failwith"impossible: archive with no size"|Somea->a))inNat_big_num.addarchive_size(* search the bindings: we want the index of the first binding
that refers to this object.
*)(matchLem_list.find_index(fun((b_ref_idx,b_ref,b_ref_item),b_maybe_def)->(matchb_maybe_defwithSome(b_def_idx,b_def,b_def_item)->Nat_big_num.equalb_def_idxlinkable_idx|_->false))accumulated_bindingswithSomen->Nat_big_num.of_intn|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_compareNat_big_num.compare(toNaturalListis1)(toNaturalListis2)))))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_scriptalloc_mapscript1linkableslinker_script_linkable_idxainput_sectionsseen_orderingdefault_place_orphansinitial_bindings_by_name)in(* also copy over ABS (range-less) symbols from all included input items *)letall_abs_range_tags_in_included_inputs=(List.concat(Lem_list.map(fun(img2,(idx1,linkable))->letabslist=(Lem_list.mapMaybe(fun(tag,maybeRange)->(matchtagwithSymbolDef(ent)->if(Lem.option_equal(Lem.pair_equal(=)(Lem.pair_equalNat_big_num.equalNat_big_num.equal))maybeRangeNone)&&Nat_big_num.equal(Uint32_wrapper.to_bigintent.def_syment.elf64_st_shndx)shn_absthenSome(maybeRange,ent)elseNone|_->None))(tagged_ranges_matching_taginstance_Basic_classes_Ord_Abis_any_abi_feature_dictinstance_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*)letx2=([])inList.fold_right(fun(maybe_range,ent)x2->iftruethen(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}))::x2elsex2)abslistx2)(list_combineimgslinkables_not_discarded)))inletby_range_including_abs_symbols=(Pset.(union)unrelocated_output_image_lacking_abs_symbols.by_range((Pset.from_list(pairCompare(maybeCompare(pairComparecompare(pairCompareNat_big_num.compareNat_big_num.compare)))compare)all_abs_range_tags_in_included_inputs)))inletunrelocated_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_dictinstance_Basic_classes_SetType_var_dict(instance_Basic_classes_SetType_tup2_dictinstance_Basic_classes_SetType_Num_natural_dictinstance_Basic_classes_SetType_Num_natural_dict)))instance_Basic_classes_SetType_var_dictby_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.
*)inletremaining_relocs=(Multimap.lookupBy0(instance_Basic_classes_Ord_Memory_image_range_tag_dictinstance_Basic_classes_Ord_Abis_any_abi_feature_dict)(instance_Basic_classes_Ord_Maybe_maybe_dict(instance_Basic_classes_Ord_tup2_dictLem_string_extra.instance_Basic_classes_Ord_string_dict(instance_Basic_classes_Ord_tup2_dictinstance_Basic_classes_Ord_Num_natural_dictinstance_Basic_classes_Ord_Num_natural_dict)))instance_Basic_classes_SetType_var_dict(instance_Basic_classes_SetType_Maybe_maybe_dict(instance_Basic_classes_SetType_tup2_dictinstance_Basic_classes_SetType_var_dict(instance_Basic_classes_SetType_tup2_dictinstance_Basic_classes_SetType_Num_natural_dictinstance_Basic_classes_SetType_Num_natural_dict)))(Memory_image_orderings.tagEquivinstance_Abi_classes_AbiFeatureTagEquiv_Abis_any_abi_feature_dict)(SymbolRef(null_symbol_reference_and_reloc_site))unrelocated_output_image.by_tag)inlet_=(List.fold_left(fun_->(fun(tag,maybe_range)->let_=((matchtagwithSymbolRef(x)->(matchx.maybe_relocwithSomers->(matchmaybe_rangewithNone->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*)letunrelocated_concrete_output_image=(a.concretise_supportunrelocated_output_image)inletoutput_image=(relocate_output_imageabindings_by_nameunrelocated_concrete_output_image)inlet(maybe_entry_point_address:Nat_big_num.numoption)=((matchCommand_line.find_option_matching_tag(Command_line.EntryAddress((Nat_big_num.of_int0)))optionswithNone->a.guess_entry_pointoutput_image|Some(Command_line.EntryAddress(x))->Somex))in(matchmaybe_entry_point_addresswithSomeaddr->(matchaddress_to_element_and_offsetaddroutput_imagewithSome(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_nameel_offset((Nat_big_num.of_int0))output_image|None->(* HMM. entry point symbol has no address at present. *)failwith("error: entry point address 0x"^((hex_string_of_naturaladdr)^" does not correspond to any element position")))|None->let_=(prerr_endline"Warning: not tagging entry point in output image")inoutput_image))