1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
|
(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Xavier Leroy, projet Gallium, INRIA Paris *)
(* *)
(* Copyright 2015 Institut National de Recherche en Informatique et *)
(* en Automatique. *)
(* *)
(* All rights reserved. This file is distributed under the terms of *)
(* the GNU Lesser General Public License version 2.1, with the *)
(* special exception on linking described in the file LICENSE. *)
(* *)
(**************************************************************************)
(* Copy a bytecode executable, removing debugging information
and #! header from the copy.
Usage: stripdebug <source file> <dest file>
*)
open Printf
open Misc
module Bytesections : sig
module Name : sig
type raw_name = private string
type t =
| CODE (** bytecode *)
| CRCS (** crcs for modules *)
| DATA (** global data (constant) *)
| DBUG (** debug info *)
| DLLS (** dll names *)
| DLPT (** dll paths *)
| PRIM (** primitives names *)
| RNTM (** The path to the bytecode interpreter (use_runtime mode) *)
| SYMB (** global identifiers *)
| Other of raw_name
end
(** Recording sections written to a bytecode executable file *)
type toc_writer
val init_record : out_channel -> toc_writer
(** Start recording sections from the current position in out_channel *)
val record : toc_writer -> Name.t -> unit
(** Record the current position in the out_channel as the end of
the section with the given name. *)
val write_toc_and_trailer : toc_writer -> unit
(** Write the table of contents and the standard trailer for bytecode
executable files *)
(** Reading sections from a bytecode executable file *)
type section_entry =
{ name : Name.t (** name of the section. *)
; pos : int (** byte offset at which the section starts. *)
; len : int (** length of the section. *)
}
type section_table
exception Bad_magic_number
val read_toc : in_channel -> section_table
(** Read the table of sections from a bytecode executable.
Raise [Bad_magic_number] if magic number doesn't match *)
val all : section_table -> section_entry list
(** Returns all [section_entry] from a [section_table] in increasing
position order. *)
val pos_first_section : section_table -> int
(** Return the position of the beginning of the first section *)
end = struct
module Name = struct
type raw_name = string
type t =
| CODE (** bytecode *)
| CRCS (** crcs for modules *)
| DATA (** global data (constant) *)
| DBUG (** debug info *)
| DLLS (** dll names *)
| DLPT (** dll paths *)
| PRIM (** primitives names *)
| RNTM (** The path to the bytecode interpreter (use_runtime mode) *)
| SYMB (** global identifiers *)
| Other of raw_name
let of_string name =
match name with
| "CODE" -> CODE
| "DLPT" -> DLPT
| "DLLS" -> DLLS
| "DATA" -> DATA
| "PRIM" -> PRIM
| "SYMB" -> SYMB
| "DBUG" -> DBUG
| "CRCS" -> CRCS
| "RNTM" -> RNTM
| name ->
if String.length name <> 4
then invalid_arg "Bytesections.Name.of_string: must be of size 4";
Other name
let to_string = function
| CODE -> "CODE"
| DLPT -> "DLPT"
| DLLS -> "DLLS"
| DATA -> "DATA"
| PRIM -> "PRIM"
| SYMB -> "SYMB"
| DBUG -> "DBUG"
| CRCS -> "CRCS"
| RNTM -> "RNTM"
| Other n -> n
end
type section_entry =
{ name : Name.t
; pos : int
; len : int
}
type section_table =
{ sections : section_entry list
; first_pos : int
}
(* Recording sections *)
type toc_writer =
{ (* List of all sections, in reverse order *)
mutable section_table_rev : section_entry list
; mutable section_prev : int
; outchan : out_channel
}
let init_record outchan : toc_writer =
let pos = pos_out outchan in
{ section_prev = pos; section_table_rev = []; outchan }
let record t name =
let pos = pos_out t.outchan in
if pos < t.section_prev
then invalid_arg "Bytesections.record: out_channel offset moved backward";
let entry = { name; pos = t.section_prev; len = pos - t.section_prev } in
t.section_table_rev <- entry :: t.section_table_rev;
t.section_prev <- pos
let write_toc_and_trailer t =
let section_table = List.rev t.section_table_rev in
List.iter
(fun { name; pos = _; len } ->
let name = Name.to_string name in
assert (String.length name = 4);
output_string t.outchan name;
output_binary_int t.outchan len)
section_table;
output_binary_int t.outchan (List.length section_table);
output_string t.outchan Config.exec_magic_number
(* Read the table of sections from a bytecode executable *)
exception Bad_magic_number
let read_toc ic =
let pos_trailer = in_channel_length ic - 16 in
seek_in ic pos_trailer;
let num_sections = input_binary_int ic in
let header = really_input_string ic (String.length Config.exec_magic_number) in
if header <> Config.exec_magic_number then raise Bad_magic_number;
let toc_pos = pos_trailer - (8 * num_sections) in
seek_in ic toc_pos;
let section_table_rev = ref [] in
for _i = 1 to num_sections do
let name = Name.of_string (really_input_string ic 4) in
let len = input_binary_int ic in
section_table_rev := (name, len) :: !section_table_rev
done;
let first_pos, sections =
List.fold_left
(fun (pos, l) (name, len) ->
let section = { name; pos = pos - len; len } in
pos - len, section :: l)
(toc_pos, [])
!section_table_rev
in
{ sections; first_pos }
let all t = t.sections
let pos_first_section t = t.first_pos
end
let stripdebug infile outfile =
let ic = open_in_bin infile in
let toc = Bytesections.read_toc ic in
let pos_first_section = Bytesections.pos_first_section toc in
let oc =
open_out_gen [ Open_wronly; Open_creat; Open_trunc; Open_binary ] 0o777 outfile
in
(* Skip the #! header, going straight to the first section. *)
seek_in ic pos_first_section;
(* Copy each section except DBUG *)
let writer = Bytesections.init_record oc in
List.iter
(fun { Bytesections.name; pos; len } ->
match name with
| Bytesections.Name.DBUG -> ()
| name ->
seek_in ic pos;
copy_file_chunk ic oc len;
Bytesections.record writer name)
(Bytesections.all toc);
(* Rewrite the toc and trailer *)
Bytesections.write_toc_and_trailer writer;
(* Done *)
close_in ic;
close_out oc
let _ =
if Array.length Sys.argv = 3
then stripdebug Sys.argv.(1) Sys.argv.(2)
else (
eprintf "Usage: stripdebug <source file> <destination file>\n";
exit 2)
|