forked from ocaml/ocaml
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdynlink_common.ml
361 lines (325 loc) · 12.2 KB
/
dynlink_common.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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
#2 "otherlibs/dynlink/dynlink_common.ml"
(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* Mark Shinwell and Leo White, Jane Street Europe *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* en Automatique. *)
(* Copyright 2017--2019 Jane Street Group LLC *)
(* *)
(* 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. *)
(* *)
(**************************************************************************)
[@@@ocaml.warning "+a-4-30-40-41-42"]
open! Dynlink_compilerlibs
module String = struct
include Misc.Stdlib.String
module Map = struct
include Map
let keys t =
fold (fun key _data keys -> Set.add key keys) t Set.empty
end
end
module Make (P : Dynlink_platform_intf.S) = struct
module DT = Dynlink_types
module UH = P.Unit_header
type interface_dep =
| Name (* the only use of the interface can be via a module alias *)
| Contents of Digest.t
type implem = Digest.t option * DT.filename * DT.implem_state
module State = struct
type t = {
ifaces : (interface_dep * DT.filename) String.Map.t;
(* Interfaces that have been depended upon. *)
implems : implem String.Map.t;
(* Implementations that exist in the main program or have been
dynamically loaded. *)
defined_symbols : String.Set.t;
(* Symbols corresponding to compilation units or packed modules (cf.
[Asmpackager.build_package_cmx]). Used as a sanity check. *)
allowed_units : String.Set.t;
(* Units that are allowed to be referenced by a subsequently-loaded
dynamic library. *)
main_program_units : String.Set.t;
(* Units forming part of the main program (i.e. not dynamically
linked). *)
public_dynamically_loaded_units : String.Set.t;
(* All units that have been dynamically linked, not including those that
were privately loaded. *)
}
let invariant t =
let ifaces = String.Map.keys t.ifaces in
let implems = String.Map.keys t.implems in
assert (String.Set.subset implems ifaces);
assert (String.Set.subset t.main_program_units ifaces);
assert (String.Set.subset t.main_program_units implems);
assert (String.Set.subset t.public_dynamically_loaded_units ifaces);
assert (String.Set.subset t.public_dynamically_loaded_units implems)
let empty = {
ifaces = String.Map.empty;
implems = String.Map.empty;
defined_symbols = String.Set.empty;
allowed_units = String.Set.empty;
main_program_units = String.Set.empty;
public_dynamically_loaded_units = String.Set.empty;
}
end
let global_state = ref State.empty
let inited = ref false
let unsafe_allowed = ref false
let allow_unsafe_modules b =
unsafe_allowed := b
let check_symbols_disjoint ~descr syms1 syms2 =
let exe = Sys.executable_name in
let overlap = String.Set.inter syms1 syms2 in
if not (String.Set.is_empty overlap) then begin
let msg =
Format.asprintf "%s: symbols multiply-defined %s: %a"
exe (Lazy.force descr)
(Format.pp_print_list ~pp_sep:(fun ppf () -> Format.fprintf ppf ", ")
Format.pp_print_string)
(String.Set.elements overlap)
in
failwith msg
end
let default_available_units () =
let exe = Sys.executable_name in
let ifaces, implems, defined_symbols =
P.fold_initial_units
~init:(String.Map.empty, String.Map.empty, String.Set.empty)
~f:(fun (ifaces, implems, defined_symbols)
~comp_unit ~interface ~implementation
~defined_symbols:defined_symbols_this_unit ->
let ifaces =
match interface with
| None -> String.Map.add comp_unit (Name, exe) ifaces
| Some crc -> String.Map.add comp_unit (Contents crc, exe) ifaces
in
let implems =
match implementation with
| None -> implems
| Some (crc, state) ->
String.Map.add comp_unit (crc, exe, state) implems
in
let defined_symbols_this_unit =
String.Set.of_list defined_symbols_this_unit
in
check_symbols_disjoint ~descr:(lazy "in the executable file")
defined_symbols_this_unit defined_symbols;
let defined_symbols =
String.Set.union defined_symbols_this_unit defined_symbols
in
ifaces, implems, defined_symbols)
in
let main_program_units = String.Map.keys implems in
let state : State.t =
{ ifaces;
implems;
defined_symbols;
allowed_units = main_program_units;
main_program_units;
public_dynamically_loaded_units = String.Set.empty;
}
in
global_state := state
let init () =
if not !inited then begin
P.init ();
default_available_units ();
inited := true
end
let set_loaded_implem filename ui implems =
String.Map.add (UH.name ui) (UH.crc ui, filename, DT.Loaded) implems
let set_loaded filename ui (state : State.t) =
{ state with implems = set_loaded_implem filename ui state.implems }
let check_interface_imports filename ui ifaces =
List.fold_left (fun ifaces (name, crc) ->
match String.Map.find name ifaces with
| exception Not_found -> begin
match crc with
| None -> String.Map.add name (Name, filename) ifaces
| Some crc -> String.Map.add name (Contents crc, filename) ifaces
end
| old_crc, _old_src ->
match old_crc, crc with
| (Name | Contents _), None -> ifaces
| Name, Some crc ->
String.Map.add name (Contents crc, filename) ifaces
| Contents old_crc, Some crc ->
if old_crc <> crc then raise (DT.Error (Inconsistent_import name))
else ifaces)
ifaces
(UH.interface_imports ui)
let check_implementation_imports ~allowed_units filename ui implems =
List.iter (fun (name, crc) ->
if not (String.Set.mem name allowed_units) then begin
raise (DT.Error (Unavailable_unit name))
end;
match String.Map.find name implems with
| exception Not_found -> raise (DT.Error (Unavailable_unit name))
| ((old_crc, _old_src, unit_state) : implem) ->
begin match old_crc, crc with
| (None | Some _), None -> ()
| None, Some _crc ->
(* The [None] behaves like a CRC different from every other. *)
raise (DT.Error (Inconsistent_implementation name))
| Some old_crc, Some crc ->
if old_crc <> crc then begin
raise (DT.Error (Inconsistent_implementation name))
end
end;
match unit_state with
| Not_initialized ->
raise (DT.Error (Linking_error (
filename, Uninitialized_global name)))
| Check_inited i ->
if P.num_globals_inited () < i then begin
raise (DT.Error (Linking_error (
filename, Uninitialized_global name)))
end
| Loaded -> ())
(UH.implementation_imports ui)
let check_name filename ui priv ifaces implems =
let name = UH.name ui in
if String.Map.mem name implems then begin
raise (DT.Error (Module_already_loaded name))
end;
if priv && String.Map.mem name ifaces then begin
raise (DT.Error (Private_library_cannot_implement_interface name))
end;
String.Map.add name (UH.crc ui, filename, DT.Not_initialized) implems
let check_unsafe_module ui =
if (not !unsafe_allowed) && UH.unsafe_module ui then begin
raise (DT.Error Unsafe_file)
end
let check filename (units : UH.t list) (state : State.t) ~priv =
List.iter (fun ui -> check_unsafe_module ui) units;
let new_units =
String.Set.of_list (List.map (fun ui -> UH.name ui) units)
in
let implems =
List.fold_left (fun implems ui ->
check_name filename ui priv state.ifaces implems)
state.implems units
in
let ifaces =
List.fold_left (fun ifaces ui ->
check_interface_imports filename ui ifaces)
state.ifaces units
in
let allowed_units = String.Set.union state.allowed_units new_units in
let (_ : implem String.Map.t) =
List.fold_left
(fun acc ui ->
check_implementation_imports ~allowed_units filename ui acc;
set_loaded_implem filename ui acc)
implems units
in
let defined_symbols =
List.fold_left (fun defined_symbols ui ->
let descr =
lazy (Printf.sprintf "between the executable file (and any \
existing dynamically-loaded units) and the unit `%s' being \
dynamically loaded from %s"
(UH.name ui)
filename)
in
let symbols = String.Set.of_list (UH.defined_symbols ui) in
check_symbols_disjoint ~descr symbols defined_symbols;
String.Set.union symbols defined_symbols)
state.defined_symbols
units
in
if priv then begin
state
end else begin
let public_dynamically_loaded_units =
String.Set.union state.public_dynamically_loaded_units new_units
in
let state =
{ state with
implems;
ifaces;
defined_symbols;
allowed_units;
public_dynamically_loaded_units;
}
in
State.invariant state;
state
end
let set_allowed_units allowed_units =
let allowed_units = String.Set.of_list allowed_units in
let state =
let state = !global_state in
{ state with
allowed_units;
}
in
global_state := state
let allow_only units =
let allowed_units =
String.Set.inter (!global_state).allowed_units
(String.Set.of_list units)
in
let state =
let state = !global_state in
{ state with
allowed_units;
}
in
global_state := state
let prohibit units =
let allowed_units =
String.Set.diff (!global_state).allowed_units
(String.Set.of_list units)
in
let state =
let state = !global_state in
{ state with
allowed_units;
}
in
global_state := state
let main_program_units () =
String.Set.elements (!global_state).main_program_units
let public_dynamically_loaded_units () =
String.Set.elements (!global_state).public_dynamically_loaded_units
let all_units () =
String.Set.elements (String.Set.union
(!global_state).main_program_units
(!global_state).public_dynamically_loaded_units)
let dll_filename fname =
if Filename.is_implicit fname then Filename.concat (Sys.getcwd ()) fname
else fname
let load priv filename =
init ();
let filename = dll_filename filename in
match P.load ~filename ~priv with
| exception exn -> raise (DT.Error (Cannot_open_dynamic_library exn))
| handle, units ->
try
global_state := check filename units !global_state ~priv;
P.run_shared_startup handle;
List.iter
(fun unit_header ->
P.run handle ~unit_header ~priv;
if not priv then begin
global_state := set_loaded filename unit_header !global_state
end)
units;
P.finish handle
with exn ->
P.finish handle;
raise exn
let loadfile filename = load false filename
let loadfile_private filename = load true filename
let unsafe_get_global_value = P.unsafe_get_global_value
let is_native = P.is_native
let adapt_filename = P.adapt_filename
end