forked from AdaCore/gnatcoverage
-
Notifications
You must be signed in to change notification settings - Fork 0
/
elf_disassemblers.adb
288 lines (239 loc) · 9.14 KB
/
elf_disassemblers.adb
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
------------------------------------------------------------------------------
-- --
-- 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.Unchecked_Deallocation;
with Interfaces; use Interfaces;
with Disa_ARM;
with Disa_Lmp;
with Disa_Ppc;
with Disa_Sparc;
with Disa_Thumb;
with Disa_X86;
with Disa_AArch64;
package body Elf_Disassemblers is
procedure Deallocate is new Ada.Unchecked_Deallocation
(Insn_Set_Ranges, Insn_Set_Ranges_Acc);
use type Ada.Containers.Count_Type;
Disa_For_ARM : aliased Disa_ARM.ARM_Disassembler;
Disa_For_E500 : aliased Disa_Ppc.E500_Disassembler;
Disa_For_Ppc : aliased Disa_Ppc.PPC_Disassembler;
Disa_For_Sparc : aliased Disa_Sparc.SPARC_Disassembler;
Disa_For_Thumb : aliased Disa_Thumb.Thumb_Disassembler;
Disa_For_Visium : aliased Disa_Lmp.LMP_Disassembler;
Disa_For_X86 : aliased Disa_X86.X86_Disassembler;
Disa_For_AArch64 : aliased Disa_AArch64.AArch64_Disassembler;
----------------------
-- Disa_For_Machine --
----------------------
function Disa_For_Machine
(Machine : Machine_Type;
Insn_Set : Insn_Set_Type) return access Disassembler'Class
is
begin
case Machine is
when Unknown =>
return null;
when ARM =>
case Insn_Set is
when Default =>
raise Program_Error with
"Could not determine instruction set (ARM or Thumb)";
when Data =>
raise Program_Error with
"Cannot disassemble data (ARM)";
when ARM =>
return Disa_For_ARM'Access;
when Thumb =>
return Disa_For_Thumb'Access;
end case;
when E500 =>
return Disa_For_E500'Access;
when PPC =>
return Disa_For_Ppc'Access;
when SPARC =>
return Disa_For_Sparc'Access;
when Visium =>
return Disa_For_Visium'Access;
when X86 | X86_64 =>
return Disa_For_X86'Access;
when AArch64 =>
return Disa_For_AArch64'Access;
end case;
end Disa_For_Machine;
----------
-- Free --
----------
procedure Free (I_Range : in out Insn_Set_Ranges_Acc) is
begin
Deallocate (I_Range);
end Free;
---------------
-- Add_Range --
---------------
procedure Add_Range
(Ranges : in out Insn_Set_Ranges;
First, Last : Pc_Type;
Insn_Set : Insn_Set_Type)
is
begin
Ranges.Insert ((First, Last, Insn_Set));
end Add_Range;
------------------
-- Get_Insn_Set --
------------------
function Get_Insn_Set
(Ranges : Insn_Set_Ranges;
Cache : in out Insn_Set_Cache;
PC : Pc_Type) return Insn_Set_Type
is
use Ranges_Sets;
Cur : Cursor renames Cache.Cur;
begin
-- If there is no specific instruction set information, as it is always
-- the case on most architecture, loose no time in lookups.
if Ranges.Length = 0 then
return Default;
end if;
-- If we have a cache, try to use it
if Cache /= Empty_Cache then
declare
I_Range : constant Insn_Set_Range := Element (Cur);
begin
if PC in I_Range.First .. I_Range.Last then
return I_Range.Insn_Set;
elsif PC > I_Range.Last then
-- It looks like are moving forward in adresses: try to use the
-- cache to avoid lookups.
declare
Next_Cur : constant Cursor := Next (Cur);
Next_I_Range : Insn_Set_Range;
begin
if Next_Cur = No_Element then
-- There is no association anymore for higher PC: next
-- PCs are always default.
Cur := Next_Cur;
return Default;
end if;
Next_I_Range := Element (Next_Cur);
if PC < Next_I_Range.First then
-- This is the case in which PC is between two ranges,
-- but none covers it. This is not supposed to happen
-- in practice, but try to be correct anyway.
Cur := No_Element;
return Default;
elsif PC in Next_I_Range.First .. Next_I_Range.Last then
-- This is the case for which we hope this cache is the
-- more useful: the PC we were looking for is in the next
-- range. Just update the cache for the next lookup and
-- we are done!
Cur := Next_Cur;
return Next_I_Range.Insn_Set;
end if;
-- Execution reaches this point when we failed to get a
-- result just using the cache: it's time to perform a full
-- search...
end;
end if;
-- If we reach this point, we know that PC < I_Range.First: the
-- cache is not useful here.
end;
end if;
Cur := Ranges.Floor ((PC, PC, Default));
return (if Cur = No_Element
then Default
else Element (Cur).Insn_Set);
end Get_Insn_Set;
---------------------
-- Go_To_Next_Insn --
---------------------
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
is
use Ranges_Sets;
pragma Unreferenced (Ranges);
pragma Assert (Cache.Cur /= No_Element);
I_Range : constant Insn_Set_Range := Element (Cache.Cur);
pragma Assert
(PC in I_Range.First .. I_Range.Last
and then I_Range.Insn_Set = Data);
begin
Cache.Cur := Next (Cache.Cur);
if Cache.Cur /= No_Element then
declare
I_Range : constant Insn_Set_Range :=
Element (Cache.Cur);
begin
PC := I_Range.First;
Insn_Set := I_Range.Insn_Set;
return True;
end;
end if;
return False;
end Go_To_Next_Insn;
------------------------
-- Iterate_Over_Insns --
------------------------
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
is
Insn_Set_Tmp : Insn_Set_Type := Get_Insn_Set (Ranges, Cache, PC);
No_Code_Left : constant Boolean :=
(Insn_Set_Tmp = Data
and then not Go_To_Next_Insn (Ranges, Cache, PC, Insn_Set_Tmp));
begin
Insn_Set := Insn_Set_Tmp;
return not No_Code_Left and then PC <= Last_PC;
end Iterate_Over_Insns;
---------
-- "<" --
---------
function "<" (L, R : Insn_Set_Range) return Boolean is
begin
-- Either L and R are the same, either they are not overlapping: assert
-- this.
--
-- Empty ranges (First = Last) are used when looking for the range
-- corrersponding to a single PC, so handle them as well. Make sure
-- empty ranges are ordered right after ranges with the same First
-- so that Ordered_Sets.Floor return the range.
if L.First < R.First then
pragma Assert (R.First = R.Last or else L.Last <= R.First);
return True;
elsif R.First < L.First then
pragma Assert (L.First = L.First or else R.Last <= L.First);
return False;
-- Starting from here, we know that L.First = R.First: check for empty
-- ranges first.
elsif L.First = L.Last then
return False;
elsif R.First = R.Last then
return True;
-- Now, we have two non-empty ranges that have the same First: they
-- *must* be equal.
else
pragma Assert (L = R);
return False;
end if;
end "<";
end Elf_Disassemblers;