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
exception Error of string
let verbose = ref false
module Pid_or_self = struct
type t =
| Self of int
| Other of int
let self () = Self (Unix.getpid ())
let of_pid pid =
match pid = Unix.getpid () with
| true -> Self pid
| false -> Other pid
;;
let proc_path ~filename = function
| Self _ -> "/proc/self/" ^ filename
| Other pid -> Printf.sprintf "/proc/%d/%s" pid filename
;;
let get_exe t = proc_path ~filename:"exe" t
let to_pid = function
| Self pid | Other pid -> pid
;;
end
type t =
{ vma_offset_text : int64
; vma_offset_semaphores : int64
; start_of_text_segment : int64
; numa_maps : string
; maps : string
}
let vma_offset_text t = t.vma_offset_text
let vma_offset_semaphores t = t.vma_offset_semaphores
let try_find_vma_offset (mmap_entry : Owee_linux_maps.entry) (elf_section : Elf.section) =
if mmap_entry.address_start < elf_section.addr || elf_section.offset < mmap_entry.offset
then None
else (
let ( + ), ( - ) = Int64.(add, sub) in
Some
(mmap_entry.address_start
- mmap_entry.offset
+ elf_section.offset
- elf_section.addr))
;;
external stub_sysconf_pagesize : unit -> int = "probes_lib_sysconf_pagesize"
let pagesize_in_bytes ~allow_gigatext t =
assert (not allow_gigatext);
stub_sysconf_pagesize ()
;;
let in_range x (entry : Owee_linux_maps.entry) =
entry.address_start <= x && x < entry.address_end
;;
external is_prelinking_enabled : unit -> bool = "probes_lib_is_prelinking_enabled"
let print_for_debug maps =
In_channel.with_open_text maps (fun maps_channel ->
Printf.printf "%s\n\n" (In_channel.input_all maps_channel))
;;
let read_shared_object ~maps ~numa_maps (elf : Elf.t) entries =
let filename = elf.filename in
let prelinking_enabled = is_prelinking_enabled () in
let semaphores_section =
match elf.semaphores_section, prelinking_enabled with
| Some s, _ -> Some s
| None, true -> None
| None, false ->
raise (Failure (Printf.sprintf "No .probes section in %s" elf.filename))
in
let is_mapped_from_file (e : Owee_linux_maps.entry) =
not (e.inode = 0L && e.device_major = 0 && e.device_minor = 0)
in
let update p (e : Owee_linux_maps.entry) (elf_section : Elf.section) =
match try_find_vma_offset e elf_section with
| None ->
()
| Some vma_offset ->
let dynamic_address_of_start_of_section = Int64.add vma_offset elf_section.addr in
if not (in_range dynamic_address_of_start_of_section e)
then
raise
(Error
(Printf.sprintf
"Unexpected format of %s: vma of section start is outside the segment \
for %s"
maps
filename));
(match !p with
| None -> ()
| Some (prev, (prev_e : Owee_linux_maps.entry)) ->
if not (prev_e.offset = 0L)
then
raise
(Error
(Printf.sprintf
"Unexpected format of %s: two segments with the same permissions for %s\n\
0x%Lx previously found\n\
0x%Lx from 0x%Lx\n"
maps
filename
prev
vma_offset
e.address_start)));
p := Some (vma_offset, e)
in
let vma_offset_text = ref None in
let vma_offset_semaphores = ref None in
let start_of_text_segment = ref None in
List.iter
(fun (e : Owee_linux_maps.entry) ->
if is_mapped_from_file e && String.equal filename e.pathname
then (
match e.perm_read, e.perm_write, e.perm_execute, e.perm_shared with
| true, false, true, false ->
update vma_offset_text e elf.text_section;
start_of_text_segment := Some e.address_start
| true, true, false, false ->
(match semaphores_section with
| Some semaphores_section -> update vma_offset_semaphores e semaphores_section
| None -> ())
| _ -> ()))
entries;
if Option.is_none !vma_offset_text
then (
print_for_debug maps;
raise
(Error
(Printf.sprintf
"Unexpected format of %s: missing executable segment for %s"
maps
filename)));
if (not prelinking_enabled) && Option.is_none !vma_offset_semaphores
then (
print_for_debug maps;
raise
(Error
(Printf.sprintf
"Unexpected format of %s: missing write segment for %s"
maps
filename)));
{ vma_offset_text =
(if prelinking_enabled then 0L else Option.get !vma_offset_text |> fst)
; vma_offset_semaphores =
(if prelinking_enabled then 0L else Option.get !vma_offset_semaphores |> fst)
; start_of_text_segment = Option.get !start_of_text_segment
; numa_maps
; maps
}
;;
let print_file name =
Printf.printf "READING %s\n" name;
let inc = In_channel.open_text name in
let rec print () =
match In_channel.input_line inc with
| None -> print_newline ()
| Some line ->
Printf.printf "%s\n" line;
print ()
in
print ();
In_channel.close inc
;;
let get_start_of_text_segment maps (elf : Elf.t) entries =
let text = elf.text_section.addr in
match
List.find_opt
(fun (entry : Owee_linux_maps.entry) ->
in_range text entry && entry.perm_read && entry.perm_execute)
entries
with
| Some entry -> entry.address_start
| None ->
raise
(Error
(Printf.sprintf
"Unexpected format of %s: cannot find text segment containing address 0x%Lx"
maps
text))
;;
let read ~pid (elf : Elf.t) =
let numa_maps = Pid_or_self.proc_path ~filename:"numa_maps" pid in
let maps = Pid_or_self.proc_path ~filename:"maps" pid in
if !verbose then print_file maps;
let entries = Owee_linux_maps.scan_file maps in
let exe_inode =
let fd =
Unix.openfile (Pid_or_self.proc_path ~filename:"exe" pid) [ Unix.O_RDONLY ] 0
in
Fun.protect ~finally:(fun () -> Unix.close fd) (fun () -> (Unix.fstat fd).st_ino)
in
if elf.shared_object && elf.inode = exe_inode
then failwith "ocaml-probes: position-independent executables are not supported"
else if elf.shared_object
then read_shared_object ~maps ~numa_maps elf entries
else
{ vma_offset_text = 0L
; vma_offset_semaphores = 0L
; start_of_text_segment = get_start_of_text_segment maps elf entries
; maps
; numa_maps
}
;;