summaryrefslogtreecommitdiff
path: root/checklink/ELF_utils.ml
blob: 898c778d6f2be85b22206371a872f3a750f36cf4 (plain)
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
open ELF_types
open Library

let section_ndx_by_name_noelf (eshdra: elf32_shdr array)(name: string): int =
  match array_exists (fun eshdr -> eshdr.sh_name = name) eshdra with
  | Some sndx -> sndx
  | None      -> assert false

let section_ndx_by_name (e: elf)(name: string): int option =
  array_exists (fun eshdr -> eshdr.sh_name = name) e.e_shdra

let section_bitstring_noelf
    (bs: bitstring)(eshdra: elf32_shdr array)(sndx: int): bitstring =
  let sofs = Safe32.to_int eshdra.(sndx).sh_offset in
  let size = Safe32.to_int eshdra.(sndx).sh_size in
  Bitstring.subbitstring bs Safe.(sofs * 8) Safe.(size * 8)

let section_bitstring (e: elf): int -> bitstring =
  section_bitstring_noelf e.e_bitstring e.e_shdra

let physical_offset_of_vaddr (e: elf)(vaddr: int32): int32 option =
  begin match e.e_sym_phdr vaddr with
  | None -> None
  | Some(pndx) ->
      let phdr = e.e_phdra.(pndx) in
      let vaddr_ofs = Safe32.(vaddr - phdr.p_vaddr) in
      Some(Safe32.(phdr.p_offset + vaddr_ofs))
  end

(* TODO: could make this more efficient, but it's not often called *)
let section_at_vaddr (e: elf)(vaddr: int32): int option =
  array_exists
    (fun shdr ->
      shdr.sh_addr <= vaddr && vaddr < Int32.add shdr.sh_addr shdr.sh_size
    )
    e.e_shdra

(**
   Returns the bitstring of the specified byte size beginning at the specified
   virtual address, along with its physical byte offset and physical byte size,
   that is, the space it occupies in the file.
*)
let bitstring_at_vaddr (e: elf)(vaddr: int32)(size:int32):
    (bitstring * int32 * int32) option =
  match e.e_sym_phdr vaddr with
  | None -> None
  | Some(pndx) ->
      let phdr = e.e_phdra.(pndx) in
      let phdr_mem_first = phdr.p_vaddr in
      let phdr_mem_last = Safe32.(phdr.p_vaddr + phdr.p_memsz - 1l) in
      let vaddr_mem_first = vaddr in
      let vaddr_mem_last = Safe32.(vaddr + size - 1l) in
      if not (phdr_mem_first <= vaddr_mem_first && vaddr_mem_last <= phdr_mem_last)
      then None (* we're overlapping segments *)
      else
        let vaddr_relative_ofs = Safe32.(vaddr_mem_first - phdr_mem_first) in
        let vaddr_file_ofs = Safe32.(phdr.p_offset + vaddr_relative_ofs) in
        if phdr.p_filesz = 0l || vaddr_relative_ofs > phdr.p_filesz
        then
          Some(
            Bitstring.create_bitstring Safe32.(to_int (8l * size)),
            phdr.p_offset, (* whatever? *)
            0l
          )
        else if Safe32.(vaddr_relative_ofs + size) > phdr.p_filesz
        then
          let bit_start = Safe32.(to_int (8l * vaddr_file_ofs)) in
          let vaddr_file_len = Safe32.(phdr.p_filesz - vaddr_relative_ofs) in
          let bit_len = Safe32.(to_int (8l * vaddr_file_len)) in
          let first = Bitstring.subbitstring e.e_bitstring bit_start bit_len in
          let rest = Bitstring.create_bitstring (8 * Safe32.to_int size - bit_len) in
          Some(
            Bitstring.concat [first; rest],
            vaddr_file_ofs,
            vaddr_file_len
          )
        else
          let bit_start = Safe32.(to_int (8l * (phdr.p_offset + vaddr_relative_ofs))) in
          let bit_len = Safe.(8 * Safe32.to_int size) in
          Some(
            Bitstring.subbitstring e.e_bitstring bit_start bit_len,
            vaddr_file_ofs,
            size
          )

(**
   Returns the entire bitstring that begins at the specified virtual address and
   ends at the end of the segment.
*)
let bitstring_at_vaddr_nosize (e: elf)(vaddr: int32):
    (bitstring * int32 * int32) option =
  match e.e_sym_phdr vaddr with
  | None -> None
  | Some(pndx) ->
      let phdr = e.e_phdra.(pndx) in
      let first_byte_vaddr = vaddr in
      let last_byte_vaddr = Safe32.(phdr.p_vaddr + phdr.p_memsz - 1l) in
      let size = Safe32.(last_byte_vaddr - first_byte_vaddr + 1l) in
      bitstring_at_vaddr e vaddr size

(**
   Removes symbol version for GCC's symbols.
*)
let strip_versioning (s: string): string =
  try String.sub s 0 (String.index s '@')
  with Not_found -> s

(**
   Removes CompCert's mangling of variadic functions
*)
let strip_mangling (s: string): string =
  try String.sub s 0 (String.index s '$')
  with Not_found -> s

(**
   Returns the list of all symbols matching the specified name.
*)
let ndxes_of_sym_name (e: elf) (name: string): int list =
  try StringMap.find (strip_mangling name) e.e_syms_by_name with Not_found -> []

(**
   Returns the index of the first symbol matching the specified name, if it
   exists.
*)
let ndx_of_sym_name (e: elf) (name: string): int option =
  match ndxes_of_sym_name e name with
  | [] -> None
  | h::_ -> Some(h)