123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317(*Generated by Lem from input_list.lem.*)openLem_basic_classesopenLem_functionopenLem_stringopenLem_string_extraopenLem_tupleopenLem_boolopenLem_listopenLem_list_extraopenLem_sortingopenLem_numopenLem_maybeopenLem_assert_extraopenByte_sequenceopenDefault_printingopenErroropenMissing_pervasivesopenShowopenArchiveopenCommand_lineopenElf_types_native_uintopenElf_fileopenElf_header(* Here we elaborate away various properties of the command line:
* archives, groups, library paths, -l, --as-needed, --whole-archive,
* and which inputs can be used to resolve symbols undefined in which other inputs.
*
* What we get out is a list of input files and the options applying to them.
* Input files are either relocatable files, shared objects or linker scripts.
*)typeinput_blob=Relocofbyte_sequence0|Sharedofbyte_sequence0|Scriptofbyte_sequence0|ControlScript(* We remember where the input item came from on the command line,
* using "coordinates" identifying the index in the higher-up list
* followed by the index within that item. *)typeorigin_coord=InArchiveof(Nat_big_num.num*Nat_big_num.num*string*Nat_big_num.num)(* archive-id, pos-within-archive, archive-name, archive-member-count *)|InGroupof(Nat_big_num.num*Nat_big_num.num)(* group-id, pos-within-group *)|InCommandLineofNat_big_num.num|Builtin(*val string_of_origin_coord : origin_coord -> string*)letstring_of_origin_coordc:string=((matchcwithInArchive(aid,aidx,aname,_)->"at position "^((Nat_big_num.to_stringaidx)^(" within archive "^(aname^(" (at position "^((Nat_big_num.to_stringaid)^")")))))|InGroup(gid1,gidx)->"at position "^((Nat_big_num.to_stringgidx)^(" within group at position "^(Nat_big_num.to_stringgid1)))|InCommandLine(cid)->"(command line)"|Builtin->"(built-in)"))letinstance_Show_Show_Input_list_origin_coord_dict:(origin_coord)show_class=({show_method=string_of_origin_coord})typeinput_origin=input_unit*origin_coordlisttypeinput_item=string*input_blob*input_origin(*val string_of_input_blob : input_blob -> string*)letstring_of_input_blobitem:string=((matchitemwithReloc(seq)->"relocatable file ("^((Nat_big_num.to_string(Byte_sequence.length0seq))^" bytes)")|Shared(seq)->"shared object ("^((Nat_big_num.to_string(Byte_sequence.length0seq))^" bytes)")|Script(seq)->"script ("^((Nat_big_num.to_string(Byte_sequence.length0seq))^" bytes)")|ControlScript->"the linker control script"))letinstance_Show_Show_Input_list_input_blob_dict:(input_blob)show_class=({show_method=string_of_input_blob})(*val short_string_of_input_item : input_item -> string*)letshort_string_of_input_itemitem:string=(let(fname1,blob,(u,origin))=itemin(matchoriginwithInArchive(aid,aidx,aname,_)::_->aname^("("^(fname1^")"))|_->fname1))(* About symbol resolution and "suppliers".
*
* Groups change this.
*
* When we expand a .a file into a list of .o files, what is the supplier
* relation among them? I *THINK* that within the archive, each can supply any other,
* but outside the archive, each can only supply leftmore.
*)typecan_supply_function=input_itemlist->int->boollisttypeinput_options={item_fmt:string;item_check_sections:bool;item_copy_dt_needed:bool;item_force_output:bool(* true for .o, false for .a unless --whole-archive,
true for .so with --no-as-needed,
false for .so with --as-needed *)}(*val null_input_options : input_options*)letnull_input_options:input_options=({item_fmt="";item_check_sections=false;item_copy_dt_needed=false;item_force_output=true})(*val string_of_input_options : input_options -> string*)letstring_of_input_optionsopts:string="(some options)"letinstance_Show_Show_Input_list_input_options_dict:(input_options)show_class=({show_method=string_of_input_options})typeinput_list=(input_item*input_options)list(*val toplevel_dot_o_can_supply : list input_item -> nat -> list bool*)lettoplevel_dot_o_can_supplyinputspos:(bool)list=(Lem_list.genlist(fun_->true)(List.lengthinputs))(*val toplevel_shared_can_supply : list input_item -> nat -> list bool*)lettoplevel_shared_can_supplyinputspos:(bool)list=(Lem_list.genlist(funndx->ndx<=pos)(List.lengthinputs))(*val toplevel_archive_can_supply : list input_item -> nat -> list bool*)lettoplevel_archive_can_supplyinputspos:(bool)list=(Lem_list.genlist(funndx->ndx<=pos)(List.lengthinputs))(*val lib_filename_from_spec : string -> string -> string*)letlib_filename_from_specspecext:string=((match(Xstring.explodespec)with':'::more->(Xstring.implodemore)|_->"lib"^(spec^("."^ext))))(*val find_library_in : string -> list string -> list string -> maybe string*)letfind_library_inspecextensionspathlist:(string)option=((* Recall the GNU libc's "libc.so is a linker script" hack.
* This tells us that we should only look at file extensions, not contents. *)letfile_existsname1=((matchByte_sequence.acquirename1with(* FIXME: use cheaper call *)Success_->true|Fail_->false))inletexpand_candidate_libname=(funpath->funext->(path^("/"^(lib_filename_from_specspecext))))inletget_expansions_existing=(funpath->letx2=([])inList.fold_right(funcandx2->iffile_existscandthencand::x2elsex2)(Lem_list.map(expand_candidate_libnamepath)extensions)x2)inletfound_by_path=(Lem_list.map(funpath->(path,get_expansions_existingpath))pathlist)in(* Do we take the first path for which some extension is found?
* Or do we keep going if we prefer shared libraries, say?
* I think it's the former. *)(matchLem_list.list_find_opt(fun(path,exps)->(List.lengthexps)>0)found_by_pathwithSome(path,exps)->Some(List.hdexps)|None->None))(*val find_one_library_filename : input_file_options -> string -> string*)letfind_one_library_filenameoptionsstr:string=(letextensions=(ifoptions.input_link_sharedlibsthen["so";"a"]else["a"])inletfound=(find_library_instrextensionsoptions.input_libpath)in(matchfoundwithNone->failwith("couldn't find library matching '"^(str^"'"))|Someresult->result))(*val is_elf64_with_type : elf64_half -> byte_sequence -> bool*)letis_elf64_with_typetypseq:bool=((*let _ = Missing_pervasives.errs ("elf64? " ^
(match seq with Sequence(bs) -> show (List.take 16 bs) end))
in*)(matchElf_file.read_elf64_fileseqwithSuccess(e)->(* let _ = Missing_pervasives.errln ": yes" in *)(e.elf64_file_header.elf64_type=typ)|Fail_->(* let _ = Missing_pervasives.errln ": no" in *)false))(*val is_archive : byte_sequence -> bool*)letis_archiveseq:bool=((matchread_archive_global_headerseqwithSuccess_->true|Fail_->false))(*val open_file_and_expand : string -> input_unit -> natural -> list input_item*)letopen_file_and_expandtoplevel_fnameufpos:(string*input_blob*(input_unit*(origin_coord)list))list=((matchByte_sequence.acquiretoplevel_fnamewithFail_->failwith("could not open file "^toplevel_fname)|Successseq->ifis_elf64_with_type(Uint32_wrapper.of_bigintelf_ft_rel)seqthen[(toplevel_fname,Reloc(seq),(u,[]))]elseifis_elf64_with_type(Uint32_wrapper.of_bigintelf_ft_dyn)seqthen[(toplevel_fname,Shared(seq),(u,[]))]elseifis_archiveseqthen(matchread_archiveseqwithFail_->failwith("could not read archive "^toplevel_fname)|Success(pairs:(string*byte_sequence0)list)->(*let _ = Missing_pervasives.errln (toplevel_fname ^ " is an archive with " ^ (show (List.length pairs)) ^ " members")
in*)letnot_elf=(List.filter(fun(inner_fname,seq)->not(is_elf64_with_type(Uint32_wrapper.of_bigintelf_ft_rel)seq))pairs)inifList.lengthnot_elf=0thenmapMaybei(fun(i:Nat_big_num.num)->(fun((inner_fname:string),seq)->let(trimmed_inner_fname:string)=((match((Ml_bindings.string_index_of'/'inner_fname):Nat_big_num.numoption)withNone->inner_fname|Some(ind:Nat_big_num.num)->(matchMl_bindings.string_prefixindinner_fnamewithSomes->s|None->failwith"impossible: string has character index >= its length")))inSome(trimmed_inner_fname,Reloc(seq),(u,[InArchive(fpos,i,toplevel_fname,lengthpairs)]))))pairselselet(names,seqs)=(List.splitnot_elf)infailwith("archive with unsupported contents"(*(" ^ (show names) ^ ")*)))else[(toplevel_fname,Script(seq),(u,[]))]))(*val make_input_items_and_options : list input_item -> Command_line.input_file_options -> list origin_coord -> list (input_item * input_options)*)letmake_input_items_and_optionsfile_listcmdoptscoords_to_append:((string*input_blob*(input_unit*(origin_coord)list))*input_options)list=((matchfile_listwith[]->failwith"impossible: empty list of files"|[(fname1,Reloc(seq),(u,coords))]->[((fname1,Reloc(seq),(u,List.rev_append(List.revcoords)coords_to_append)),{item_fmt=(cmdopts.input_fmt);item_check_sections=(cmdopts.input_check_sections);item_copy_dt_needed=(cmdopts.input_copy_dt_needed);item_force_output=true})]|[(fname1,Shared(seq),(u,coords))]->[((fname1,Shared(seq),(u,List.rev_append(List.revcoords)coords_to_append)),{item_fmt=(cmdopts.input_fmt);item_check_sections=(cmdopts.input_check_sections);item_copy_dt_needed=(cmdopts.input_copy_dt_needed);item_force_output=(ifcmdopts.input_as_neededthenfalseelsetrue)})]|[(fname1,Script(seq),(u,coords))]->[((fname1,Script(seq),(u,List.rev_append(List.revcoords)coords_to_append)),{item_fmt=(cmdopts.input_fmt);item_check_sections=(cmdopts.input_check_sections);item_copy_dt_needed=(cmdopts.input_copy_dt_needed);item_force_output=true})]|_->(* guaranteed to be all relocs, from one archive *)let(items_and_options:(input_item*input_options)list)=(mapMaybei(funi->(fun(fname1,reloc1,(u,coords))->let(item:input_item)=(fname1,reloc1,(u,List.rev_append(List.revcoords)coords_to_append))inlet(options:input_options)=({item_fmt=(cmdopts.input_fmt);item_check_sections=(cmdopts.input_check_sections);item_copy_dt_needed=(cmdopts.input_copy_dt_needed);item_force_output=(ifcmdopts.input_whole_archivethentrueelsefalse)})inSome(item,options)))file_list)initems_and_options|_->failwith"impossible expanded input item"))(*val elaborate_input_helper : natural -> list Command_line.input_unit -> input_list -> input_list*)letrecelaborate_input_helperinput_posinputsacc:(input_item*input_options)list=((matchinputswith[]->acc|input::more_inputs->(matchinputwithFile(spec,options)->(matchspecwithFilename(str)->elaborate_input_helper(Nat_big_num.addinput_pos((Nat_big_num.of_int1)))more_inputs(List.rev_append(List.revacc)(make_input_items_and_options(open_file_and_expandstrinputinput_pos)options[InCommandLine(input_pos)]))|Libname(str)->elaborate_input_helper(Nat_big_num.addinput_pos((Nat_big_num.of_int1)))more_inputs(List.rev_append(List.revacc)(make_input_items_and_options(open_file_and_expand(find_one_library_filenameoptionsstr)inputinput_pos)options[InCommandLine(input_pos)])))|Group(specs_and_options)->(* Every member of a group is either a filename or a libname.
* First expand the libnames, leaving the Group intact. *)letgroup_with_lib_files=(Lem_list.map(fun(spec,options)->(matchspecwithFilename(str)->(str,options)|Libname(str)->(find_one_library_filenameoptionsstr,options)))specs_and_options)in(* Now expand archives into file lists. *)letgroup_with_file_lists=(mapMaybei(funi->(fun(str,options)->Some((open_file_and_expandstrinputinput_pos),options)))group_with_lib_files)in(* Now expand them into files and fix up the options appropriately *)letto_add=(mapMaybei(funindex_in_group->(fun(file_list,options)->(Some(make_input_items_and_optionsfile_listoptions[InGroup(input_pos,index_in_group);InCommandLine(input_pos)]))))group_with_file_lists)inelaborate_input_helper(Nat_big_num.addinput_pos((Nat_big_num.of_int1)))more_inputs(List.rev_append(List.revacc)(List.concatto_add)))))(*val elaborate_input : list Command_line.input_unit -> input_list*)letrecelaborate_inputinputs:(input_item*input_options)list=(elaborate_input_helper((Nat_big_num.of_int0))inputs[])