forked from ocsigen/js_of_ocaml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
stripdebug.ml
230 lines (198 loc) · 7.29 KB
/
stripdebug.ml
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)