summaryrefslogtreecommitdiff
path: root/ocaml-binary-annot/asmcomp/asmlink.ml
diff options
context:
space:
mode:
Diffstat (limited to 'ocaml-binary-annot/asmcomp/asmlink.ml')
-rw-r--r--ocaml-binary-annot/asmcomp/asmlink.ml385
1 files changed, 385 insertions, 0 deletions
diff --git a/ocaml-binary-annot/asmcomp/asmlink.ml b/ocaml-binary-annot/asmcomp/asmlink.ml
new file mode 100644
index 0000000000..8b8cbc0a48
--- /dev/null
+++ b/ocaml-binary-annot/asmcomp/asmlink.ml
@@ -0,0 +1,385 @@
+(***********************************************************************)
+(* *)
+(* Objective Caml *)
+(* *)
+(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
+(* *)
+(* Copyright 1996 Institut National de Recherche en Informatique et *)
+(* en Automatique. All rights reserved. This file is distributed *)
+(* under the terms of the Q Public License version 1.0. *)
+(* *)
+(***********************************************************************)
+
+(* $Id$ *)
+
+(* Link a set of .cmx/.o files and produce an executable *)
+
+open Sys
+open Misc
+open Config
+open Cmx_format
+open Compilenv
+
+type error =
+ File_not_found of string
+ | Not_an_object_file of string
+ | Missing_implementations of (string * string list) list
+ | Inconsistent_interface of string * string * string
+ | Inconsistent_implementation of string * string * string
+ | Assembler_error of string
+ | Linking_error
+ | Multiple_definition of string * string * string
+ | Missing_cmx of string * string
+
+exception Error of error
+
+(* Consistency check between interfaces and implementations *)
+
+let crc_interfaces = Consistbl.create ()
+let crc_implementations = Consistbl.create ()
+let extra_implementations = ref ([] : string list)
+let implementations_defined = ref ([] : (string * string) list)
+let cmx_required = ref ([] : string list)
+
+let check_consistency file_name unit crc =
+ begin try
+ List.iter
+ (fun (name, crc) ->
+ if name = unit.ui_name
+ then Consistbl.set crc_interfaces name crc file_name
+ else Consistbl.check crc_interfaces name crc file_name)
+ unit.ui_imports_cmi
+ with Consistbl.Inconsistency(name, user, auth) ->
+ raise(Error(Inconsistent_interface(name, user, auth)))
+ end;
+ begin try
+ List.iter
+ (fun (name, crc) ->
+ if crc <> cmx_not_found_crc then
+ Consistbl.check crc_implementations name crc file_name
+ else if List.mem name !cmx_required then
+ raise(Error(Missing_cmx(file_name, name)))
+ else
+ extra_implementations := name :: !extra_implementations)
+ unit.ui_imports_cmx
+ with Consistbl.Inconsistency(name, user, auth) ->
+ raise(Error(Inconsistent_implementation(name, user, auth)))
+ end;
+ begin try
+ let source = List.assoc unit.ui_name !implementations_defined in
+ raise (Error(Multiple_definition(unit.ui_name, file_name, source)))
+ with Not_found -> ()
+ end;
+ Consistbl.set crc_implementations unit.ui_name crc file_name;
+ implementations_defined :=
+ (unit.ui_name, file_name) :: !implementations_defined;
+ if unit.ui_symbol <> unit.ui_name then
+ cmx_required := unit.ui_name :: !cmx_required
+
+let extract_crc_interfaces () =
+ Consistbl.extract crc_interfaces
+let extract_crc_implementations () =
+ List.fold_left
+ (fun ncl n ->
+ if List.mem_assoc n ncl then ncl else (n, cmx_not_found_crc) :: ncl)
+ (Consistbl.extract crc_implementations)
+ !extra_implementations
+
+(* Add C objects and options and "custom" info from a library descriptor.
+ See bytecomp/bytelink.ml for comments on the order of C objects. *)
+
+let lib_ccobjs = ref []
+let lib_ccopts = ref []
+
+let add_ccobjs l =
+ if not !Clflags.no_auto_link then begin
+ lib_ccobjs := l.lib_ccobjs @ !lib_ccobjs;
+ lib_ccopts := l.lib_ccopts @ !lib_ccopts
+ end
+
+let runtime_lib () =
+ let libname =
+ if !Clflags.gprofile
+ then "libasmrunp" ^ ext_lib
+ else "libasmrun" ^ ext_lib in
+ try
+ if !Clflags.nopervasives then []
+ else [ find_in_path !load_path libname ]
+ with Not_found ->
+ raise(Error(File_not_found libname))
+
+let object_file_name name =
+ let file_name =
+ try
+ find_in_path !load_path name
+ with Not_found ->
+ fatal_error "Asmlink.object_file_name: not found" in
+ if Filename.check_suffix file_name ".cmx" then
+ Filename.chop_suffix file_name ".cmx" ^ ext_obj
+ else if Filename.check_suffix file_name ".cmxa" then
+ Filename.chop_suffix file_name ".cmxa" ^ ext_lib
+ else
+ fatal_error "Asmlink.object_file_name: bad ext"
+
+(* First pass: determine which units are needed *)
+
+let missing_globals = (Hashtbl.create 17 : (string, string list ref) Hashtbl.t)
+
+let is_required name =
+ try ignore (Hashtbl.find missing_globals name); true
+ with Not_found -> false
+
+let add_required by (name, crc) =
+ try
+ let rq = Hashtbl.find missing_globals name in
+ rq := by :: !rq
+ with Not_found ->
+ Hashtbl.add missing_globals name (ref [by])
+
+let remove_required name =
+ Hashtbl.remove missing_globals name
+
+let extract_missing_globals () =
+ let mg = ref [] in
+ Hashtbl.iter (fun md rq -> mg := (md, !rq) :: !mg) missing_globals;
+ !mg
+
+type file =
+ | Unit of string * unit_infos * Digest.t
+ | Library of string * library_infos
+
+let read_file obj_name =
+ let file_name =
+ try
+ find_in_path !load_path obj_name
+ with Not_found ->
+ raise(Error(File_not_found obj_name)) in
+ if Filename.check_suffix file_name ".cmx" then begin
+ (* This is a .cmx file. It must be linked in any case.
+ Read the infos to see which modules it requires. *)
+ let (info, crc) = read_unit_info file_name in
+ Unit (file_name,info,crc)
+ end
+ else if Filename.check_suffix file_name ".cmxa" then begin
+ let infos =
+ try read_library_info file_name
+ with Compilenv.Error(Not_a_unit_info _) ->
+ raise(Error(Not_an_object_file file_name))
+ in
+ Library (file_name,infos)
+ end
+ else raise(Error(Not_an_object_file file_name))
+
+let scan_file obj_name tolink = match read_file obj_name with
+ | Unit (file_name,info,crc) ->
+ (* This is a .cmx file. It must be linked in any case. *)
+ remove_required info.ui_name;
+ List.iter (add_required file_name) info.ui_imports_cmx;
+ (info, file_name, crc) :: tolink
+ | Library (file_name,infos) ->
+ (* This is an archive file. Each unit contained in it will be linked
+ in only if needed. *)
+ add_ccobjs infos;
+ List.fold_right
+ (fun (info, crc) reqd ->
+ if info.ui_force_link
+ || !Clflags.link_everything
+ || is_required info.ui_name
+ then begin
+ remove_required info.ui_name;
+ List.iter (add_required (Printf.sprintf "%s(%s)"
+ file_name info.ui_name))
+ info.ui_imports_cmx;
+ (info, file_name, crc) :: reqd
+ end else
+ reqd)
+ infos.lib_units tolink
+
+(* Second pass: generate the startup file and link it with everything else *)
+
+let make_startup_file ppf filename units_list =
+ let compile_phrase p = Asmgen.compile_phrase ppf p in
+ let oc = open_out filename in
+ Emitaux.output_channel := oc;
+ Location.input_name := "caml_startup"; (* set name of "current" input *)
+ Compilenv.reset "_startup"; (* set the name of the "current" compunit *)
+ Emit.begin_assembly();
+ let name_list =
+ List.flatten (List.map (fun (info,_,_) -> info.ui_defines) units_list) in
+ compile_phrase (Cmmgen.entry_point name_list);
+ let units = List.map (fun (info,_,_) -> info) units_list in
+ List.iter compile_phrase (Cmmgen.generic_functions false units);
+ Array.iter
+ (fun name -> compile_phrase (Cmmgen.predef_exception name))
+ Runtimedef.builtin_exceptions;
+ compile_phrase (Cmmgen.global_table name_list);
+ compile_phrase
+ (Cmmgen.globals_map
+ (List.map
+ (fun (unit,_,crc) ->
+ try (unit.ui_name, List.assoc unit.ui_name unit.ui_imports_cmi,
+ crc,
+ unit.ui_defines)
+ with Not_found -> assert false)
+ units_list));
+ compile_phrase(Cmmgen.data_segment_table ("_startup" :: name_list));
+ compile_phrase(Cmmgen.code_segment_table ("_startup" :: name_list));
+ compile_phrase
+ (Cmmgen.frame_table("_startup" :: "_system" :: name_list));
+
+ Emit.end_assembly();
+ close_out oc
+
+let make_shared_startup_file ppf units filename =
+ let compile_phrase p = Asmgen.compile_phrase ppf p in
+ let oc = open_out filename in
+ Emitaux.output_channel := oc;
+ Location.input_name := "caml_startup";
+ Compilenv.reset "_shared_startup";
+ Emit.begin_assembly();
+ List.iter compile_phrase
+ (Cmmgen.generic_functions true (List.map fst units));
+ compile_phrase (Cmmgen.plugin_header units);
+ compile_phrase
+ (Cmmgen.global_table
+ (List.map (fun (ui,_) -> ui.ui_symbol) units));
+ (* this is to force a reference to all units, otherwise the linker
+ might drop some of them (in case of libraries) *)
+
+ Emit.end_assembly();
+ close_out oc
+
+
+let call_linker_shared file_list output_name =
+ if not (Ccomp.call_linker Ccomp.Dll output_name file_list "")
+ then raise(Error Linking_error)
+
+let link_shared ppf objfiles output_name =
+ let units_tolink = List.fold_right scan_file objfiles [] in
+ List.iter
+ (fun (info, file_name, crc) -> check_consistency file_name info crc)
+ units_tolink;
+ Clflags.ccobjs := !Clflags.ccobjs @ !lib_ccobjs;
+ Clflags.ccopts := !lib_ccopts @ !Clflags.ccopts;
+ let objfiles = List.rev (List.map object_file_name objfiles) @
+ !Clflags.ccobjs in
+
+ let startup =
+ if !Clflags.keep_startup_file
+ then output_name ^ ".startup" ^ ext_asm
+ else Filename.temp_file "camlstartup" ext_asm in
+ make_shared_startup_file ppf
+ (List.map (fun (ui,_,crc) -> (ui,crc)) units_tolink) startup;
+ let startup_obj = output_name ^ ".startup" ^ ext_obj in
+ if Proc.assemble_file startup startup_obj <> 0
+ then raise(Error(Assembler_error startup));
+ if not !Clflags.keep_startup_file then remove_file startup;
+ call_linker_shared (startup_obj :: objfiles) output_name;
+ remove_file startup_obj
+
+let call_linker file_list startup_file output_name =
+ let main_dll = !Clflags.output_c_object
+ && Filename.check_suffix output_name Config.ext_dll
+ in
+ let files = startup_file :: (List.rev file_list) in
+ let files, c_lib =
+ if (not !Clflags.output_c_object) || main_dll then
+ files @ (List.rev !Clflags.ccobjs) @ runtime_lib (),
+ (if !Clflags.nopervasives then "" else Config.native_c_libraries)
+ else
+ files, ""
+ in
+ let mode =
+ if main_dll then Ccomp.MainDll
+ else if !Clflags.output_c_object then Ccomp.Partial
+ else Ccomp.Exe
+ in
+ if not (Ccomp.call_linker mode output_name files c_lib)
+ then raise(Error Linking_error)
+
+(* Main entry point *)
+
+let link ppf objfiles output_name =
+ let stdlib =
+ if !Clflags.gprofile then "stdlib.p.cmxa" else "stdlib.cmxa" in
+ let stdexit =
+ if !Clflags.gprofile then "std_exit.p.cmx" else "std_exit.cmx" in
+ let objfiles =
+ if !Clflags.nopervasives then objfiles
+ else if !Clflags.output_c_object then stdlib :: objfiles
+ else stdlib :: (objfiles @ [stdexit]) in
+ let units_tolink = List.fold_right scan_file objfiles [] in
+ Array.iter remove_required Runtimedef.builtin_exceptions;
+ begin match extract_missing_globals() with
+ [] -> ()
+ | mg -> raise(Error(Missing_implementations mg))
+ end;
+ List.iter
+ (fun (info, file_name, crc) -> check_consistency file_name info crc)
+ units_tolink;
+ Clflags.ccobjs := !Clflags.ccobjs @ !lib_ccobjs;
+ Clflags.ccopts := !lib_ccopts @ !Clflags.ccopts; (* put user's opts first *)
+ let startup =
+ if !Clflags.keep_startup_file then output_name ^ ".startup" ^ ext_asm
+ else Filename.temp_file "camlstartup" ext_asm in
+ make_startup_file ppf startup units_tolink;
+ let startup_obj = Filename.temp_file "camlstartup" ext_obj in
+ if Proc.assemble_file startup startup_obj <> 0 then
+ raise(Error(Assembler_error startup));
+ try
+ call_linker (List.map object_file_name objfiles) startup_obj output_name;
+ if not !Clflags.keep_startup_file then remove_file startup;
+ remove_file startup_obj
+ with x ->
+ remove_file startup_obj;
+ raise x
+
+(* Error report *)
+
+open Format
+
+let report_error ppf = function
+ | File_not_found name ->
+ fprintf ppf "Cannot find file %s" name
+ | Not_an_object_file name ->
+ fprintf ppf "The file %s is not a compilation unit description" name
+ | Missing_implementations l ->
+ let print_references ppf = function
+ | [] -> ()
+ | r1 :: rl ->
+ fprintf ppf "%s" r1;
+ List.iter (fun r -> fprintf ppf ",@ %s" r) rl in
+ let print_modules ppf =
+ List.iter
+ (fun (md, rq) ->
+ fprintf ppf "@ @[<hov 2>%s referenced from %a@]" md
+ print_references rq) in
+ fprintf ppf
+ "@[<v 2>No implementations provided for the following modules:%a@]"
+ print_modules l
+ | Inconsistent_interface(intf, file1, file2) ->
+ fprintf ppf
+ "@[<hov>Files %s@ and %s@ make inconsistent assumptions \
+ over interface %s@]"
+ file1 file2 intf
+ | Inconsistent_implementation(intf, file1, file2) ->
+ fprintf ppf
+ "@[<hov>Files %s@ and %s@ make inconsistent assumptions \
+ over implementation %s@]"
+ file1 file2 intf
+ | Assembler_error file ->
+ fprintf ppf "Error while assembling %s" file
+ | Linking_error ->
+ fprintf ppf "Error during linking"
+ | Multiple_definition(modname, file1, file2) ->
+ fprintf ppf
+ "@[<hov>Files %s@ and %s@ both define a module named %s@]"
+ file1 file2 modname
+ | Missing_cmx(filename, name) ->
+ fprintf ppf
+ "@[<hov>File %s@ was compiled without access@ \
+ to the .cmx file@ for module %s,@ \
+ which was produced by `ocamlopt -for-pack'.@ \
+ Please recompile %s@ with the correct `-I' option@ \
+ so that %s.cmx@ is found.@]"
+ filename name filename name