package owee

  1. Overview
  2. Docs

Source file owee_location.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
type t

external extract : (_ -> _) -> t =
  "ml_owee_code_pointer" "ml_owee_code_pointer" "noalloc"

let count_rows body ~pointers_to_other_sections =
  let open Owee_debug_line in
  let cursor = Owee_buf.cursor body in
  let count = ref 0 in
  let rec aux () =
    match read_chunk cursor ~pointers_to_other_sections with
    | None -> !count
    | Some line_program ->
      let check _header state address =
        if address <> max_int then
          incr count;
        if state.end_sequence
        then max_int
        else state.address
      in
      let _ : int = fold_rows line_program check max_int in
      aux ()
  in
  aux ()

type 'a map_entry = {
  addr_lo: int;
  addr_hi: int;
  payload: 'a;
}

type location = string * int * int

let store_rows body array ~pointers_to_other_sections =
  let open Owee_debug_line in
  let cursor = Owee_buf.cursor body in
  let index = ref 0 in
  let prev_line = ref 0 in
  let prev_col  = ref 0 in
  let prev_file = ref None in
  let rec aux () =
    match read_chunk cursor ~pointers_to_other_sections with
    | None -> ()
    | Some (header, chunk) ->
      let check _header state address =
        if address <> max_int then
          begin
            array.(!index) <- {
              addr_lo = address;
              addr_hi = state.address;
              payload =
                (match !prev_file with
                 | Some fname -> Some (fname, !prev_line, !prev_col : location)
                 | None -> None);
            };
            incr index
          end;
        prev_file := get_filename header state;
        prev_line := state.line;
        prev_col := state.col;
        if state.end_sequence then
          max_int
        else
          state.address
      in
      let _ : int = fold_rows (header, chunk) check max_int in
      aux ()
  in
  aux ()

let extract_debug_info buffer =
  let _header, sections = Owee_elf.read_elf buffer in
  match Owee_elf.find_section sections ".debug_line" with
  | None -> [||],None
  | Some section ->
    (*Printf.eprintf "Looking for 0x%X\n" t;*)
    let body = Owee_elf.section_body buffer section in
    let pointers_to_other_sections =
      Owee_elf.debug_line_pointers buffer sections in
    let count = count_rows body ~pointers_to_other_sections in
    let debug_entries = Array.make count
        {addr_lo = max_int; addr_hi = max_int; payload = None} in
    store_rows body debug_entries ~pointers_to_other_sections;
    debug_entries, (Owee_elf.find_section sections ".text")

let memory_map = lazy begin try
    let slots = Hashtbl.create 7 in
    let find_slot pathname =
      try Hashtbl.find slots pathname
      with Not_found ->
        let slot = lazy (
          try pathname |> Owee_buf.map_binary |> extract_debug_info
          with exn ->
            prerr_endline ("Owee: fail to parse binary " ^ pathname ^ ": " ^
                           Printexc.to_string exn);
            ([||],None)
        ) in
        Hashtbl.replace slots pathname slot;
        slot
    in
    let add_entry acc (entry : Owee_linux_maps.entry) =
      if not (Sys.file_exists entry.pathname) then acc
      else
        {
          addr_lo = Int64.to_int entry.address_start;
          addr_hi = Int64.to_int entry.address_end;
          payload = (Int64.to_int entry.offset, find_slot entry.pathname)
        } :: acc
    in
    let entries =
      Owee_linux_maps.scan_self ()
      |> List.fold_left add_entry []
      |> Array.of_list
    in
    Array.sort (fun e1 e2 -> compare e1.addr_lo e2.addr_lo) entries;
    entries
  with exn ->
    prerr_endline ("Owee: fail to parse memory map: " ^
                   Printexc.to_string exn);
    [||]
end


let force_int i : t = Obj.magic (lnot i lxor -1)
let none = force_int 0

let rec bsearch table i j address =
  if i >= j then raise Not_found
  else
    let k = (i + j) / 2 in
    let entry = table.(k) in
    if entry.addr_lo lsr 1 <= address && address < entry.addr_hi lsr 1 then
      entry
    else if address < entry.addr_lo lsr 1 then
      bsearch table i k address
    else
      bsearch table (k + 1) j address

let bsearch table address =
  bsearch table 0 (Array.length table) address

let lookup t =
  if t = none then None
  else if Obj.is_int (Obj.repr t) then
    let t : int = Obj.magic t in
    let lazy memory_map = memory_map in
    match bsearch memory_map t with
    | exception Not_found -> None
    | { payload = (offset, lazy (entries,text_section)); _ } as map_entry ->
      match text_section with
      | None -> None
      | Some text_section ->
        let sec_offset = Int64.(shift_right text_section.sh_offset 1 |> to_int) in
        let sec_addr = Int64.(shift_right text_section.sh_addr 1 |> to_int) in
        let a = t - map_entry.addr_lo lsr 1 + offset lsr 1 + sec_addr - sec_offset in
        match bsearch entries a with
        | exception Not_found -> None
        | dbg_entry -> dbg_entry.payload
  else
    let t = Obj.repr t in
    assert (Obj.tag t = 0);
    assert (Obj.size t = 1);
    assert (Obj.size (Obj.field t 0) = 3);
    Obj.obj t

let locate f = lookup (extract f)

external nearest_symbol : t -> string = "ml_owee_code_pointer_symbol"

let demangled_symbol s =
  let len = String.length s in
  if len <= 4
     || s.[0] <> 'c'
     || s.[1] <> 'a'
     || s.[2] <> 'm'
     || s.[3] <> 'l'
  then s
  else
    let end_of_name = ref len in
    let skip_at_end = function
      | '0'..'9' -> true
      | '_' -> true
      | _ -> false
    in
    while !end_of_name > 4 && skip_at_end s.[!end_of_name - 1] do
      decr end_of_name
    done;
    let buf = Buffer.create len in
    let skip = ref false in
    for i = 4 to !end_of_name - 1 do
      if !skip then
        skip := false
      else if s.[i] = '_'
         && i + 1 < len
         && s.[i + 1] = '_'
      then (Buffer.add_char buf '.'; skip := true)
      else
        Buffer.add_char buf s.[i]
    done;
    if !end_of_name < len then
      (Buffer.add_char buf '/';
       let e = !end_of_name + 1 in
       Buffer.add_substring buf s e (len - e));
    Buffer.contents buf

let nearest_demangled_symbol t =
  demangled_symbol (nearest_symbol t)
OCaml

Innovation. Community. Security.