244 lines
8.2 KiB
OCaml
244 lines
8.2 KiB
OCaml
|
(***********************************************************************)
|
||
|
(* *)
|
||
|
(* Objective Caml *)
|
||
|
(* *)
|
||
|
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
|
||
|
(* *)
|
||
|
(* Copyright 2002 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$ *)
|
||
|
|
||
|
(* "Package" a set of .cmo files into one .cmo file having the
|
||
|
original compilation units as sub-modules. *)
|
||
|
|
||
|
open Misc
|
||
|
open Instruct
|
||
|
open Opcodes
|
||
|
open Emitcode
|
||
|
|
||
|
type error =
|
||
|
Forward_reference of string * Ident.t
|
||
|
| Multiple_definition of string * Ident.t
|
||
|
| Not_an_object_file of string
|
||
|
| Illegal_renaming of string * string
|
||
|
| File_not_found of string
|
||
|
|
||
|
exception Error of error
|
||
|
|
||
|
(* References accumulating informations on the .cmo files *)
|
||
|
|
||
|
let relocs = ref ([] : (reloc_info * int) list)
|
||
|
let events = ref ([] : debug_event list)
|
||
|
let primitives = ref ([] : string list)
|
||
|
let force_link = ref false
|
||
|
|
||
|
(* Record a relocation. Update its offset, and rename GETGLOBAL and
|
||
|
SETGLOBAL relocations that correspond to one of the units being
|
||
|
consolidated. *)
|
||
|
|
||
|
let rename_relocation objfile mapping defined base (rel, ofs) =
|
||
|
let rel' =
|
||
|
match rel with
|
||
|
Reloc_getglobal id ->
|
||
|
begin try
|
||
|
let id' = List.assoc id mapping in
|
||
|
if List.mem id defined
|
||
|
then Reloc_getglobal id'
|
||
|
else raise(Error(Forward_reference(objfile, id)))
|
||
|
with Not_found ->
|
||
|
rel
|
||
|
end
|
||
|
| Reloc_setglobal id ->
|
||
|
begin try
|
||
|
let id' = List.assoc id mapping in
|
||
|
if List.mem id defined
|
||
|
then raise(Error(Multiple_definition(objfile, id)))
|
||
|
else Reloc_setglobal id'
|
||
|
with Not_found ->
|
||
|
rel
|
||
|
end
|
||
|
| _ ->
|
||
|
rel in
|
||
|
relocs := (rel', base + ofs) :: !relocs
|
||
|
|
||
|
(* Record and relocate a debugging event *)
|
||
|
|
||
|
let relocate_debug base ev =
|
||
|
ev.ev_pos <- base + ev.ev_pos;
|
||
|
events := ev :: !events
|
||
|
|
||
|
(* Read the unit information from a .cmo file. *)
|
||
|
|
||
|
let read_unit_info objfile =
|
||
|
let ic = open_in_bin objfile in
|
||
|
try
|
||
|
let buffer = String.create (String.length Config.cmo_magic_number) in
|
||
|
really_input ic buffer 0 (String.length Config.cmo_magic_number);
|
||
|
if buffer <> Config.cmo_magic_number then
|
||
|
raise(Error(Not_an_object_file objfile));
|
||
|
let compunit_pos = input_binary_int ic in
|
||
|
seek_in ic compunit_pos;
|
||
|
let compunit = (input_value ic : compilation_unit) in
|
||
|
if compunit.cu_name
|
||
|
<> String.capitalize(Filename.basename(chop_extension_if_any objfile))
|
||
|
then raise(Error(Illegal_renaming(objfile, compunit.cu_name)));
|
||
|
close_in ic;
|
||
|
compunit
|
||
|
with x ->
|
||
|
close_in ic;
|
||
|
raise x
|
||
|
|
||
|
(* Read the bytecode from a .cmo file.
|
||
|
Write bytecode to channel [oc].
|
||
|
Rename globals as indicated by [mapping] in reloc info.
|
||
|
Accumulate relocs, debug info, etc.
|
||
|
Return size of bytecode. *)
|
||
|
|
||
|
let rename_append_bytecode oc mapping defined ofs (objfile, compunit) =
|
||
|
let ic = open_in_bin objfile in
|
||
|
try
|
||
|
Bytelink.check_consistency objfile compunit;
|
||
|
List.iter
|
||
|
(rename_relocation objfile mapping defined ofs)
|
||
|
compunit.cu_reloc;
|
||
|
primitives := compunit.cu_primitives @ !primitives;
|
||
|
if compunit.cu_force_link then force_link := true;
|
||
|
seek_in ic compunit.cu_pos;
|
||
|
Misc.copy_file_chunk ic oc compunit.cu_codesize;
|
||
|
if !Clflags.debug && compunit.cu_debug > 0 then begin
|
||
|
seek_in ic compunit.cu_debug;
|
||
|
List.iter (relocate_debug ofs) (input_value ic);
|
||
|
end;
|
||
|
close_in ic;
|
||
|
compunit.cu_codesize
|
||
|
with x ->
|
||
|
close_in ic;
|
||
|
raise x
|
||
|
|
||
|
(* Same, for a list of .cmo files. Return total size of bytecode. *)
|
||
|
|
||
|
let rec rename_append_bytecode_list oc mapping defined ofs = function
|
||
|
[] ->
|
||
|
ofs
|
||
|
| ((objfile, compunit) as obj_unit) :: rem ->
|
||
|
let size = rename_append_bytecode oc mapping defined ofs obj_unit in
|
||
|
rename_append_bytecode_list
|
||
|
oc mapping (Ident.create_persistent compunit.cu_name :: defined)
|
||
|
(ofs + size) rem
|
||
|
|
||
|
(* Generate the code that builds the tuple representing the package
|
||
|
module:
|
||
|
GETGLOBAL M.An
|
||
|
PUSHGETGLOBAL M.An-1
|
||
|
...
|
||
|
PUSHGETGLOBAL M.A1
|
||
|
MAKEBLOCK tag = 0 size = n
|
||
|
SETGLOBAL M
|
||
|
*)
|
||
|
|
||
|
let build_global_target oc target_name mapping ofs =
|
||
|
let out_word n =
|
||
|
output_byte oc n;
|
||
|
output_byte oc (n lsr 8);
|
||
|
output_byte oc (n lsr 16);
|
||
|
output_byte oc (n lsr 24) in
|
||
|
let rec build_global first pos = function
|
||
|
[] ->
|
||
|
out_word opMAKEBLOCK; (* pos *)
|
||
|
out_word (List.length mapping); (* pos + 4 *)
|
||
|
out_word 0; (* pos + 8 *)
|
||
|
out_word opSETGLOBAL; (* pos + 12 *)
|
||
|
out_word 0; (* pos + 16 *)
|
||
|
relocs := (Reloc_setglobal target_name, pos + 16) :: !relocs
|
||
|
| (oldname, newname) :: rem ->
|
||
|
out_word (if first then opGETGLOBAL else opPUSHGETGLOBAL); (* pos *)
|
||
|
out_word 0; (* pos + 4 *)
|
||
|
relocs := (Reloc_getglobal newname, pos + 4) :: !relocs;
|
||
|
build_global false (pos + 8) rem in
|
||
|
build_global true ofs (List.rev mapping)
|
||
|
|
||
|
(* Build the .cmo file obtained by packaging the given .cmo files. *)
|
||
|
|
||
|
let package_object_files objfiles targetfile targetname =
|
||
|
let units =
|
||
|
List.map (fun f -> (f, read_unit_info f)) objfiles in
|
||
|
let unit_names =
|
||
|
List.map (fun (_, cu) -> cu.cu_name) units in
|
||
|
let mapping =
|
||
|
List.map
|
||
|
(fun name ->
|
||
|
(Ident.create_persistent name,
|
||
|
Ident.create_persistent(targetname ^ "." ^ name)))
|
||
|
unit_names in
|
||
|
let oc = open_out_bin targetfile in
|
||
|
try
|
||
|
output_string oc Config.cmo_magic_number;
|
||
|
let pos_depl = pos_out oc in
|
||
|
output_binary_int oc 0;
|
||
|
let pos_code = pos_out oc in
|
||
|
let ofs = rename_append_bytecode_list oc mapping [] 0 units in
|
||
|
build_global_target oc (Ident.create_persistent targetname) mapping ofs;
|
||
|
let pos_debug = pos_out oc in
|
||
|
if !Clflags.debug && !events <> [] then output_value oc (List.rev !events);
|
||
|
let pos_final = pos_out oc in
|
||
|
let imports =
|
||
|
List.filter
|
||
|
(fun (name, crc) -> not (List.mem name unit_names))
|
||
|
(Bytelink.extract_crc_interfaces()) in
|
||
|
let compunit =
|
||
|
{ cu_name = targetname;
|
||
|
cu_pos = pos_code;
|
||
|
cu_codesize = pos_debug - pos_code;
|
||
|
cu_reloc = List.rev !relocs;
|
||
|
cu_imports = (targetname, Env.crc_of_unit targetname) :: imports;
|
||
|
cu_primitives = !primitives;
|
||
|
cu_force_link = !force_link;
|
||
|
cu_debug = if pos_final > pos_debug then pos_debug else 0;
|
||
|
cu_debugsize = pos_final - pos_debug } in
|
||
|
output_value oc compunit;
|
||
|
seek_out oc pos_depl;
|
||
|
output_binary_int oc pos_final;
|
||
|
close_out oc
|
||
|
with x ->
|
||
|
close_out oc;
|
||
|
raise x
|
||
|
|
||
|
(* The entry point *)
|
||
|
|
||
|
let package_files files targetfile =
|
||
|
let objfiles =
|
||
|
List.map
|
||
|
(fun f ->
|
||
|
try find_in_path !Config.load_path f
|
||
|
with Not_found -> raise(Error(File_not_found f)))
|
||
|
files in
|
||
|
let prefix = chop_extension_if_any targetfile in
|
||
|
let targetcmi = prefix ^ ".cmi" in
|
||
|
let targetname = String.capitalize(Filename.basename prefix) in
|
||
|
try
|
||
|
Typemod.package_units objfiles targetcmi targetname;
|
||
|
package_object_files objfiles targetfile targetname
|
||
|
with x ->
|
||
|
remove_file targetcmi; remove_file targetfile; raise x
|
||
|
|
||
|
(* Error report *)
|
||
|
|
||
|
open Format
|
||
|
|
||
|
let report_error ppf = function
|
||
|
Forward_reference(file, ident) ->
|
||
|
fprintf ppf "Forward reference to %s in file %s" (Ident.name ident) file
|
||
|
| Multiple_definition(file, ident) ->
|
||
|
fprintf ppf "File %s redefines %s" file (Ident.name ident)
|
||
|
| Not_an_object_file file ->
|
||
|
fprintf ppf "%s is not a bytecode object file" file
|
||
|
| Illegal_renaming(file, id) ->
|
||
|
fprintf ppf "Wrong file naming: %s@ contains the code for@ %s"
|
||
|
file id
|
||
|
| File_not_found file ->
|
||
|
fprintf ppf "File %s not found" file
|