forked from AdaCore/gnatcoverage
-
Notifications
You must be signed in to change notification settings - Fork 0
/
elf_disassemblers.ads
136 lines (113 loc) · 5.64 KB
/
elf_disassemblers.ads
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
------------------------------------------------------------------------------
-- --
-- GNATcoverage --
-- --
-- Copyright (C) 2006-2021, AdaCore --
-- --
-- GNATcoverage is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This software is distributed in the hope that it will be useful --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public --
-- License for more details. You should have received a copy of the GNU --
-- General Public License distributed with this software; see file --
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy --
-- of the license. --
------------------------------------------------------------------------------
with Ada.Containers.Ordered_Sets;
with Disassemblers; use Disassemblers;
with Traces; use Traces;
package Elf_Disassemblers is
type Insn_Set_Type is (Default, Data, ARM, Thumb);
-- Even for a specific machine, there can be multiple instruction sets, and
-- thus multiple disassemblers. This type provides enumerators to designate
-- a specific instruction set. Data indicates that there is no instruction
-- to disassemble.
function Disa_For_Machine
(Machine : Machine_Type;
Insn_Set : Insn_Set_Type) return access Disassembler'Class;
pragma Inline (Disa_For_Machine);
type Insn_Set_Ranges is private;
type Insn_Set_Ranges_Acc is access Insn_Set_Ranges;
type Insn_Set_Ranges_Cst_Acc is access constant Insn_Set_Ranges;
No_Insn_Set_Ranges : aliased constant Insn_Set_Ranges;
-- Collection of associations: address range -> instruction set
procedure Free (I_Range : in out Insn_Set_Ranges_Acc);
procedure Add_Range
(Ranges : in out Insn_Set_Ranges;
First, Last : Pc_Type;
Insn_Set : Insn_Set_Type);
-- Add an association: address range -> instruction set
type Insn_Set_Cache is private;
-- When disassembling a lot of instructions, performing a full instruction
-- set lookup for each is costly. This acts as a cache for the lookup so
-- that as few lookups as possible are done when disassembling contiguous
-- instructions.
Empty_Cache : constant Insn_Set_Cache;
function Get_Insn_Set
(Ranges : Insn_Set_Ranges;
Cache : in out Insn_Set_Cache;
PC : Pc_Type) return Insn_Set_Type;
-- Return the instruction set associated to the PC address. Return Default
-- is there is no such association.
function Go_To_Next_Insn
(Ranges : Insn_Set_Ranges;
Cache : in out Insn_Set_Cache;
PC : in out Pc_Type;
Insn_Set : out Insn_Set_Type) return Boolean;
-- Move Cache/PC to the address of the next instruction to decode
-- (including PC, if it is inside a code block) and update Insn_Set for
-- this instruction. This must be used only when Get_Insn_Set returned
-- Data. Return whether we found an instruction (i.e. True when we did
-- and False when the end of section is reached).
--
-- This is useful because in ARM, code section can contain both code and
-- data interleaved. This procedure makes it easy to skip to the next
-- instruction.
function Disa_For_Machine
(Machine : Machine_Type;
Ranges : Insn_Set_Ranges;
Cache : in out Insn_Set_Cache;
PC : Pc_Type) return access Disassembler'Class
is (Disa_For_Machine (Machine, Get_Insn_Set (Ranges, Cache, PC)));
-- Shortcut for Disa_For_Machine that automatically computes the
-- instruction set.
function Iterate_Over_Insns
(Ranges : Insn_Set_Ranges;
Cache : in out Insn_Set_Cache;
Last_PC : Pc_Type;
PC : in out Pc_Type;
Insn_Set : out Insn_Set_Type) return Boolean;
-- Utility around Go_To_Next_Insn: return whether we found in an code
-- block an address greater or equal to PC but lesser or equal to Last_PC.
-- This makes disassembly loops concise.
--
-- Common pitfal: in order to get the next instruction, one has to
-- increase PC with the size of the current instruction before calling
-- this function. Typical disassembly loops will look like:
--
-- while Iterate_Over_Insns (Ranges, Cache, Insn.Last, PC, Insn_Set)
-- loop
-- Next_PC :=
-- PC + Disa_For_Machine (Machine, Insn_set).Get_Insn_Length
-- (Slice (Insn, PC, Insn.Last));
-- -- ... perform work on instruction at PC...
-- PC := Next_PC;
-- end loop;
private
type Insn_Set_Range is record
First, Last : Pc_Type;
Insn_Set : Insn_Set_Type;
end record;
function "<" (L, R : Insn_Set_Range) return Boolean;
package Ranges_Sets is new Ada.Containers.Ordered_Sets
(Element_Type => Insn_Set_Range);
type Insn_Set_Ranges is new Ranges_Sets.Set with null record;
type Insn_Set_Cache is record
Cur : Ranges_Sets.Cursor;
end record;
No_Insn_Set_Ranges : aliased constant Insn_Set_Ranges :=
(Ranges_Sets.Empty_Set with null record);
Empty_Cache : constant Insn_Set_Cache := (Cur => Ranges_Sets.No_Element);
end Elf_Disassemblers;