1995-08-09 08:06:35 -07:00
|
|
|
(***********************************************************************)
|
|
|
|
(* *)
|
1996-04-30 07:53:58 -07:00
|
|
|
(* Objective Caml *)
|
1995-08-09 08:06:35 -07:00
|
|
|
(* *)
|
|
|
|
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
|
|
|
|
(* *)
|
1996-04-30 07:53:58 -07:00
|
|
|
(* Copyright 1996 Institut National de Recherche en Informatique et *)
|
1999-11-17 10:59:06 -08:00
|
|
|
(* en Automatique. All rights reserved. This file is distributed *)
|
|
|
|
(* under the terms of the Q Public License version 1.0. *)
|
1995-08-09 08:06:35 -07:00
|
|
|
(* *)
|
|
|
|
(***********************************************************************)
|
|
|
|
|
|
|
|
(* $Id$ *)
|
|
|
|
|
1995-07-02 09:41:48 -07:00
|
|
|
(* Link a set of .cmx/.o files and produce an executable *)
|
|
|
|
|
|
|
|
open Sys
|
|
|
|
open Misc
|
|
|
|
open Config
|
|
|
|
open Compilenv
|
|
|
|
|
|
|
|
type error =
|
|
|
|
File_not_found of string
|
|
|
|
| Not_an_object_file of string
|
1995-07-11 11:03:29 -07:00
|
|
|
| Missing_implementations of string list
|
1995-07-02 09:41:48 -07:00
|
|
|
| Inconsistent_interface of string * string * string
|
|
|
|
| Inconsistent_implementation of string * string * string
|
|
|
|
| Assembler_error of string
|
|
|
|
| Linking_error
|
|
|
|
|
|
|
|
exception Error of error
|
|
|
|
|
|
|
|
(* Consistency check between interfaces and implementations *)
|
|
|
|
|
1995-10-09 06:37:11 -07:00
|
|
|
let crc_interfaces =
|
1996-04-22 04:15:41 -07:00
|
|
|
(Hashtbl.create 17 : (string, string * Digest.t) Hashtbl.t)
|
1995-10-09 06:37:11 -07:00
|
|
|
let crc_implementations =
|
1996-04-22 04:15:41 -07:00
|
|
|
(Hashtbl.create 17 : (string, string * Digest.t) Hashtbl.t)
|
1995-07-02 09:41:48 -07:00
|
|
|
|
|
|
|
let check_consistency file_name unit crc =
|
1995-10-09 06:37:11 -07:00
|
|
|
List.iter
|
|
|
|
(fun (name, crc) ->
|
1997-05-15 06:22:08 -07:00
|
|
|
if name = unit.ui_name then begin
|
|
|
|
Hashtbl.add crc_interfaces name (file_name, crc)
|
|
|
|
end else begin
|
|
|
|
try
|
|
|
|
let (auth_name, auth_crc) = Hashtbl.find crc_interfaces name in
|
|
|
|
if crc <> auth_crc then
|
|
|
|
raise(Error(Inconsistent_interface(name, file_name, auth_name)))
|
|
|
|
with Not_found ->
|
|
|
|
(* Can only happen for unit for which only a .cmi file was used,
|
|
|
|
but no .cmo is provided *)
|
|
|
|
Hashtbl.add crc_interfaces name (file_name, crc)
|
|
|
|
end)
|
1995-10-09 06:37:11 -07:00
|
|
|
unit.ui_imports_cmi;
|
1995-07-02 09:41:48 -07:00
|
|
|
List.iter
|
|
|
|
(fun (name, crc) ->
|
1995-11-09 05:21:49 -08:00
|
|
|
if crc <> cmx_not_found_crc then begin
|
1995-07-02 09:41:48 -07:00
|
|
|
try
|
|
|
|
let (auth_name, auth_crc) = Hashtbl.find crc_implementations name in
|
|
|
|
if crc <> auth_crc then
|
|
|
|
raise(Error(Inconsistent_implementation(name, file_name, auth_name)))
|
|
|
|
with Not_found ->
|
1995-11-09 05:21:49 -08:00
|
|
|
Hashtbl.add crc_implementations name (file_name, crc)
|
|
|
|
end)
|
1995-10-09 06:37:11 -07:00
|
|
|
unit.ui_imports_cmx;
|
1995-10-04 13:52:43 -07:00
|
|
|
Hashtbl.add crc_implementations unit.ui_name (file_name, crc)
|
1995-07-02 09:41:48 -07:00
|
|
|
|
2000-03-09 01:12:28 -08:00
|
|
|
(* 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
|
|
|
|
|
1995-07-02 09:41:48 -07:00
|
|
|
(* First pass: determine which units are needed *)
|
|
|
|
|
|
|
|
module StringSet =
|
|
|
|
Set.Make(struct
|
|
|
|
type t = string
|
|
|
|
let compare = compare
|
|
|
|
end)
|
|
|
|
|
|
|
|
let missing_globals = ref StringSet.empty
|
|
|
|
|
|
|
|
let is_required name =
|
|
|
|
StringSet.mem name !missing_globals
|
|
|
|
|
|
|
|
let add_required (name, crc) =
|
|
|
|
missing_globals := StringSet.add name !missing_globals
|
|
|
|
|
|
|
|
let remove_required name =
|
|
|
|
missing_globals := StringSet.remove name !missing_globals
|
|
|
|
|
1995-11-06 03:04:55 -08:00
|
|
|
let scan_file obj_name tolink =
|
1995-07-02 09:41:48 -07:00
|
|
|
let file_name =
|
|
|
|
try
|
|
|
|
find_in_path !load_path obj_name
|
|
|
|
with Not_found ->
|
|
|
|
raise(Error(File_not_found obj_name)) in
|
1995-07-11 11:03:29 -07:00
|
|
|
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) = Compilenv.read_unit_info file_name in
|
|
|
|
check_consistency file_name info crc;
|
|
|
|
remove_required info.ui_name;
|
1995-10-09 06:37:11 -07:00
|
|
|
List.iter add_required info.ui_imports_cmx;
|
1995-07-11 11:03:29 -07:00
|
|
|
info :: tolink
|
|
|
|
end
|
|
|
|
else if Filename.check_suffix file_name ".cmxa" then begin
|
|
|
|
(* This is an archive file. Each unit contained in it will be linked
|
|
|
|
in only if needed. *)
|
|
|
|
let ic = open_in_bin file_name in
|
|
|
|
let buffer = String.create (String.length cmxa_magic_number) in
|
|
|
|
really_input ic buffer 0 (String.length cmxa_magic_number);
|
|
|
|
if buffer <> cmxa_magic_number then
|
|
|
|
raise(Error(Not_an_object_file file_name));
|
2000-03-09 01:12:28 -08:00
|
|
|
let infos = (input_value ic : library_infos) in
|
1995-07-11 11:03:29 -07:00
|
|
|
close_in ic;
|
2000-03-09 01:12:28 -08:00
|
|
|
add_ccobjs infos;
|
1995-07-11 11:03:29 -07:00
|
|
|
List.fold_right
|
|
|
|
(fun (info, crc) reqd ->
|
1996-04-18 09:26:54 -07:00
|
|
|
if info.ui_force_link
|
2000-03-09 01:12:28 -08:00
|
|
|
|| !Clflags.link_everything
|
|
|
|
|| is_required info.ui_name
|
|
|
|
then begin
|
1995-07-11 11:03:29 -07:00
|
|
|
check_consistency file_name info crc;
|
|
|
|
remove_required info.ui_name;
|
1995-10-09 06:37:11 -07:00
|
|
|
List.iter add_required info.ui_imports_cmx;
|
1995-07-11 11:03:29 -07:00
|
|
|
info :: reqd
|
|
|
|
end else
|
|
|
|
reqd)
|
2000-03-09 01:12:28 -08:00
|
|
|
infos.lib_units tolink
|
1995-07-11 11:03:29 -07:00
|
|
|
end
|
|
|
|
else raise(Error(Not_an_object_file file_name))
|
1995-07-02 09:41:48 -07:00
|
|
|
|
|
|
|
(* Second pass: generate the startup file and link it with everything else *)
|
|
|
|
|
|
|
|
module IntSet = Set.Make(
|
|
|
|
struct
|
|
|
|
type t = int
|
|
|
|
let compare = compare
|
|
|
|
end)
|
|
|
|
|
|
|
|
let make_startup_file filename info_list =
|
|
|
|
let oc = open_out filename in
|
|
|
|
Emitaux.output_channel := oc;
|
1995-07-12 08:32:09 -07:00
|
|
|
Location.input_name := "startup"; (* set the name of the "current" input *)
|
1997-05-15 06:22:08 -07:00
|
|
|
Compilenv.reset "startup"; (* set the name of the "current" compunit *)
|
1995-07-02 09:41:48 -07:00
|
|
|
Emit.begin_assembly();
|
|
|
|
let name_list = List.map (fun ui -> ui.ui_name) info_list in
|
|
|
|
Asmgen.compile_phrase(Cmmgen.entry_point name_list);
|
1995-12-19 07:09:10 -08:00
|
|
|
let apply_functions = ref (IntSet.add 2 (IntSet.add 3 IntSet.empty)) in
|
|
|
|
(* The callback functions always reference caml_apply[23] *)
|
|
|
|
let curry_functions =
|
|
|
|
ref IntSet.empty in
|
1995-07-02 09:41:48 -07:00
|
|
|
List.iter
|
|
|
|
(fun info ->
|
|
|
|
List.iter
|
|
|
|
(fun n -> apply_functions := IntSet.add n !apply_functions)
|
|
|
|
info.ui_apply_fun;
|
|
|
|
List.iter
|
|
|
|
(fun n -> curry_functions := IntSet.add n !curry_functions)
|
|
|
|
info.ui_curry_fun)
|
|
|
|
info_list;
|
|
|
|
IntSet.iter
|
|
|
|
(fun n -> Asmgen.compile_phrase(Cmmgen.apply_function n))
|
|
|
|
!apply_functions;
|
|
|
|
IntSet.iter
|
|
|
|
(fun n -> List.iter Asmgen.compile_phrase (Cmmgen.curry_function n))
|
|
|
|
!curry_functions;
|
1995-07-18 01:40:44 -07:00
|
|
|
Array.iter
|
|
|
|
(fun name -> Asmgen.compile_phrase(Cmmgen.predef_exception name))
|
|
|
|
Runtimedef.builtin_exceptions;
|
1995-07-02 09:41:48 -07:00
|
|
|
Asmgen.compile_phrase(Cmmgen.global_table name_list);
|
1998-11-20 07:35:04 -08:00
|
|
|
Asmgen.compile_phrase
|
|
|
|
(Cmmgen.globals_map
|
|
|
|
(List.map
|
|
|
|
(fun name ->
|
|
|
|
let (auth_name,crc) = Hashtbl.find crc_interfaces name in (name,crc))
|
|
|
|
name_list));
|
1998-01-05 04:43:34 -08:00
|
|
|
Asmgen.compile_phrase(Cmmgen.data_segment_table ("startup" :: name_list));
|
|
|
|
Asmgen.compile_phrase(Cmmgen.code_segment_table ("startup" :: name_list));
|
1995-12-19 07:09:10 -08:00
|
|
|
Asmgen.compile_phrase
|
|
|
|
(Cmmgen.frame_table("startup" :: "system" :: name_list));
|
1995-07-02 09:41:48 -07:00
|
|
|
Emit.end_assembly();
|
|
|
|
close_out oc
|
|
|
|
|
|
|
|
let call_linker file_list startup_file =
|
1998-08-06 06:27:38 -07:00
|
|
|
let libname =
|
|
|
|
if !Clflags.gprofile
|
|
|
|
then "libasmrunp" ^ ext_lib
|
|
|
|
else "libasmrun" ^ ext_lib in
|
1995-07-11 11:03:29 -07:00
|
|
|
let runtime_lib =
|
|
|
|
try
|
1999-11-08 10:50:36 -08:00
|
|
|
if !Clflags.nopervasives then ""
|
1999-11-08 07:25:41 -08:00
|
|
|
else find_in_path !load_path libname
|
1995-07-11 11:03:29 -07:00
|
|
|
with Not_found ->
|
1996-02-15 08:19:09 -08:00
|
|
|
raise(Error(File_not_found libname)) in
|
1999-11-08 10:50:36 -08:00
|
|
|
let c_lib = if !Clflags.nopervasives then "" else Config.c_libraries in
|
1996-02-15 08:19:09 -08:00
|
|
|
let cmd =
|
|
|
|
match Config.system with
|
|
|
|
"win32" ->
|
1996-11-07 02:55:02 -08:00
|
|
|
if not !Clflags.output_c_object then
|
|
|
|
Printf.sprintf "%s /Fe%s -I%s %s %s %s %s %s %s"
|
1998-11-06 07:39:43 -08:00
|
|
|
!Clflags.c_compiler
|
1996-11-07 02:55:02 -08:00
|
|
|
!Clflags.exec_name
|
|
|
|
Config.standard_library
|
|
|
|
(String.concat " " (List.rev !Clflags.ccopts))
|
|
|
|
startup_file
|
|
|
|
(String.concat " " (List.rev file_list))
|
1998-12-02 06:39:27 -08:00
|
|
|
(String.concat " " (List.map Ccomp.expand_libname
|
|
|
|
(List.rev !Clflags.ccobjs)))
|
1996-11-07 02:55:02 -08:00
|
|
|
runtime_lib
|
1999-11-08 07:25:41 -08:00
|
|
|
c_lib
|
1996-11-07 02:55:02 -08:00
|
|
|
else
|
1997-03-17 05:00:28 -08:00
|
|
|
Printf.sprintf "%s /out:%s %s %s"
|
|
|
|
Config.native_partial_linker
|
1996-11-07 02:55:02 -08:00
|
|
|
!Clflags.object_name
|
|
|
|
startup_file
|
|
|
|
(String.concat " " (List.rev file_list))
|
1996-02-15 08:19:09 -08:00
|
|
|
| _ ->
|
1996-11-07 02:55:02 -08:00
|
|
|
if not !Clflags.output_c_object then
|
1998-08-06 06:27:38 -07:00
|
|
|
Printf.sprintf "%s %s -o %s -I%s %s %s %s -L%s %s %s %s"
|
1998-11-06 07:39:43 -08:00
|
|
|
!Clflags.c_compiler
|
1998-08-06 06:27:38 -07:00
|
|
|
(if !Clflags.gprofile then "-pg" else "")
|
1996-11-07 02:55:02 -08:00
|
|
|
!Clflags.exec_name
|
|
|
|
Config.standard_library
|
|
|
|
(String.concat " " (List.rev !Clflags.ccopts))
|
|
|
|
startup_file
|
|
|
|
(String.concat " " (List.rev file_list))
|
|
|
|
Config.standard_library
|
|
|
|
(String.concat " " (List.rev !Clflags.ccobjs))
|
|
|
|
runtime_lib
|
1999-11-08 07:25:41 -08:00
|
|
|
c_lib
|
1996-11-07 02:55:02 -08:00
|
|
|
else
|
1997-03-17 05:00:28 -08:00
|
|
|
Printf.sprintf "%s -o %s %s %s"
|
|
|
|
Config.native_partial_linker
|
1996-11-07 02:55:02 -08:00
|
|
|
!Clflags.object_name
|
|
|
|
startup_file
|
|
|
|
(String.concat " " (List.rev file_list))
|
1997-05-15 06:22:08 -07:00
|
|
|
in if Ccomp.command cmd <> 0 then raise(Error Linking_error)
|
1995-07-02 09:41:48 -07:00
|
|
|
|
|
|
|
let object_file_name name =
|
1995-07-11 11:03:29 -07:00
|
|
|
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
|
1996-02-15 08:19:09 -08:00
|
|
|
Filename.chop_suffix file_name ".cmx" ^ ext_obj
|
1995-07-11 11:03:29 -07:00
|
|
|
else if Filename.check_suffix file_name ".cmxa" then
|
1996-02-15 08:19:09 -08:00
|
|
|
Filename.chop_suffix file_name ".cmxa" ^ ext_lib
|
1995-07-02 09:41:48 -07:00
|
|
|
else
|
1995-07-11 11:03:29 -07:00
|
|
|
fatal_error "Asmlink.object_file_name: bad ext"
|
1995-07-02 09:41:48 -07:00
|
|
|
|
|
|
|
(* Main entry point *)
|
|
|
|
|
|
|
|
let link objfiles =
|
1998-08-06 06:27:38 -07:00
|
|
|
let objfiles =
|
1999-11-08 07:25:41 -08:00
|
|
|
if !Clflags.nopervasives then
|
|
|
|
objfiles
|
|
|
|
else if !Clflags.gprofile then
|
|
|
|
"stdlib.p.cmxa" :: (objfiles @ ["std_exit.p.cmx"])
|
|
|
|
else
|
|
|
|
"stdlib.cmxa" :: (objfiles @ ["std_exit.cmx"]) in
|
1995-11-06 03:04:55 -08:00
|
|
|
let units_tolink = List.fold_right scan_file objfiles [] in
|
1995-11-09 05:21:49 -08:00
|
|
|
Array.iter remove_required Runtimedef.builtin_exceptions;
|
1995-07-11 11:03:29 -07:00
|
|
|
if not (StringSet.is_empty !missing_globals) then
|
|
|
|
raise(Error(Missing_implementations(StringSet.elements !missing_globals)));
|
2000-03-09 01:12:28 -08:00
|
|
|
Clflags.ccobjs := !Clflags.ccobjs @ !lib_ccobjs;
|
|
|
|
Clflags.ccopts := !Clflags.ccopts @ !lib_ccopts;
|
1996-02-15 08:19:09 -08:00
|
|
|
let startup = Filename.temp_file "camlstartup" ext_asm in
|
1995-07-02 09:41:48 -07:00
|
|
|
make_startup_file startup units_tolink;
|
1996-02-15 08:19:09 -08:00
|
|
|
let startup_obj = Filename.temp_file "camlstartup" ext_obj in
|
1995-07-02 09:41:48 -07:00
|
|
|
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;
|
1995-07-10 02:48:27 -07:00
|
|
|
if not !Clflags.keep_startup_file then remove_file startup;
|
1995-07-02 09:41:48 -07:00
|
|
|
remove_file startup_obj
|
|
|
|
with x ->
|
|
|
|
remove_file startup_obj;
|
|
|
|
raise x
|
|
|
|
|
|
|
|
(* Error report *)
|
|
|
|
|
1999-11-08 09:06:33 -08:00
|
|
|
open Formatmsg
|
1995-07-02 09:41:48 -07:00
|
|
|
|
|
|
|
let report_error = function
|
|
|
|
File_not_found name ->
|
2000-02-08 12:00:06 -08:00
|
|
|
printf "Cannot find file %s" name
|
1995-07-02 09:41:48 -07:00
|
|
|
| Not_an_object_file name ->
|
2000-02-08 12:00:06 -08:00
|
|
|
printf "The file %s is not a compilation unit description" name
|
1995-07-11 11:03:29 -07:00
|
|
|
| Missing_implementations l ->
|
2000-02-08 12:00:06 -08:00
|
|
|
printf
|
|
|
|
"@[No implementation(s) provided for the following module(s):%a@]"
|
|
|
|
(fun fmt -> List.iter (fun s -> Format.fprintf fmt "@ %s" s)) l
|
1995-07-02 09:41:48 -07:00
|
|
|
| Inconsistent_interface(intf, file1, file2) ->
|
2000-02-08 12:00:06 -08:00
|
|
|
printf
|
|
|
|
"@[<hv>Files %s@ and %s@ make inconsistent assumptions \
|
|
|
|
over interface %s@]"
|
|
|
|
file1 file2 intf
|
1995-07-02 09:41:48 -07:00
|
|
|
| Inconsistent_implementation(intf, file1, file2) ->
|
2000-02-08 12:00:06 -08:00
|
|
|
printf
|
|
|
|
"@[<hv>Files %s@ and %s@ make inconsistent assumptions \
|
|
|
|
over implementation %s@]"
|
|
|
|
file1 file2 intf
|
1995-07-02 09:41:48 -07:00
|
|
|
| Assembler_error file ->
|
2000-02-08 12:00:06 -08:00
|
|
|
printf "Error while assembling %s" file
|
1995-07-02 09:41:48 -07:00
|
|
|
| Linking_error ->
|
|
|
|
print_string "Error during linking"
|