1 | //===-- Opcode.h ------------------------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLDB_CORE_OPCODE_H |
10 | #define LLDB_CORE_OPCODE_H |
11 | |
12 | #include "lldb/Utility/Endian.h" |
13 | #include "lldb/lldb-enumerations.h" |
14 | |
15 | #include "llvm/Support/SwapByteOrder.h" |
16 | |
17 | #include <cassert> |
18 | #include <cstdint> |
19 | #include <cstring> |
20 | |
21 | namespace lldb { |
22 | class SBInstruction; |
23 | } |
24 | |
25 | namespace lldb_private { |
26 | class ; |
27 | class Stream; |
28 | |
29 | class Opcode { |
30 | public: |
31 | enum Type { |
32 | eTypeInvalid, |
33 | eType8, |
34 | eType16, |
35 | eType16_2, // a 32-bit Thumb instruction, made up of two words |
36 | eType32, |
37 | eType64, |
38 | eTypeBytes |
39 | }; |
40 | |
41 | Opcode() = default; |
42 | |
43 | Opcode(uint8_t inst, lldb::ByteOrder order) |
44 | : m_byte_order(order), m_type(eType8) { |
45 | m_data.inst8 = inst; |
46 | } |
47 | |
48 | Opcode(uint16_t inst, lldb::ByteOrder order) |
49 | : m_byte_order(order), m_type(eType16) { |
50 | m_data.inst16 = inst; |
51 | } |
52 | |
53 | Opcode(uint32_t inst, lldb::ByteOrder order) |
54 | : m_byte_order(order), m_type(eType32) { |
55 | m_data.inst32 = inst; |
56 | } |
57 | |
58 | Opcode(uint64_t inst, lldb::ByteOrder order) |
59 | : m_byte_order(order), m_type(eType64) { |
60 | m_data.inst64 = inst; |
61 | } |
62 | |
63 | Opcode(uint8_t *bytes, size_t length) |
64 | : m_byte_order(lldb::eByteOrderInvalid) { |
65 | SetOpcodeBytes(bytes, length); |
66 | } |
67 | |
68 | void Clear() { |
69 | m_byte_order = lldb::eByteOrderInvalid; |
70 | m_type = Opcode::eTypeInvalid; |
71 | } |
72 | |
73 | Opcode::Type GetType() const { return m_type; } |
74 | |
75 | uint8_t GetOpcode8(uint8_t invalid_opcode = UINT8_MAX) const { |
76 | switch (m_type) { |
77 | case Opcode::eTypeInvalid: |
78 | break; |
79 | case Opcode::eType8: |
80 | return m_data.inst8; |
81 | case Opcode::eType16: |
82 | break; |
83 | case Opcode::eType16_2: |
84 | break; |
85 | case Opcode::eType32: |
86 | break; |
87 | case Opcode::eType64: |
88 | break; |
89 | case Opcode::eTypeBytes: |
90 | break; |
91 | } |
92 | return invalid_opcode; |
93 | } |
94 | |
95 | uint16_t GetOpcode16(uint16_t invalid_opcode = UINT16_MAX) const { |
96 | switch (m_type) { |
97 | case Opcode::eTypeInvalid: |
98 | break; |
99 | case Opcode::eType8: |
100 | return m_data.inst8; |
101 | case Opcode::eType16: |
102 | return GetEndianSwap() ? llvm::byteswap<uint16_t>(V: m_data.inst16) |
103 | : m_data.inst16; |
104 | case Opcode::eType16_2: |
105 | break; |
106 | case Opcode::eType32: |
107 | break; |
108 | case Opcode::eType64: |
109 | break; |
110 | case Opcode::eTypeBytes: |
111 | break; |
112 | } |
113 | return invalid_opcode; |
114 | } |
115 | |
116 | uint32_t GetOpcode32(uint32_t invalid_opcode = UINT32_MAX) const { |
117 | switch (m_type) { |
118 | case Opcode::eTypeInvalid: |
119 | break; |
120 | case Opcode::eType8: |
121 | return m_data.inst8; |
122 | case Opcode::eType16: |
123 | return GetEndianSwap() ? llvm::byteswap<uint16_t>(V: m_data.inst16) |
124 | : m_data.inst16; |
125 | case Opcode::eType16_2: // passthrough |
126 | case Opcode::eType32: |
127 | return GetEndianSwap() ? llvm::byteswap<uint32_t>(V: m_data.inst32) |
128 | : m_data.inst32; |
129 | case Opcode::eType64: |
130 | break; |
131 | case Opcode::eTypeBytes: |
132 | break; |
133 | } |
134 | return invalid_opcode; |
135 | } |
136 | |
137 | uint64_t GetOpcode64(uint64_t invalid_opcode = UINT64_MAX) const { |
138 | switch (m_type) { |
139 | case Opcode::eTypeInvalid: |
140 | break; |
141 | case Opcode::eType8: |
142 | return m_data.inst8; |
143 | case Opcode::eType16: |
144 | return GetEndianSwap() ? llvm::byteswap<uint16_t>(V: m_data.inst16) |
145 | : m_data.inst16; |
146 | case Opcode::eType16_2: // passthrough |
147 | case Opcode::eType32: |
148 | return GetEndianSwap() ? llvm::byteswap<uint32_t>(V: m_data.inst32) |
149 | : m_data.inst32; |
150 | case Opcode::eType64: |
151 | return GetEndianSwap() ? llvm::byteswap<uint64_t>(V: m_data.inst64) |
152 | : m_data.inst64; |
153 | case Opcode::eTypeBytes: |
154 | break; |
155 | } |
156 | return invalid_opcode; |
157 | } |
158 | |
159 | void SetOpcode8(uint8_t inst, lldb::ByteOrder order) { |
160 | m_type = eType8; |
161 | m_data.inst8 = inst; |
162 | m_byte_order = order; |
163 | } |
164 | |
165 | void SetOpcode16(uint16_t inst, lldb::ByteOrder order) { |
166 | m_type = eType16; |
167 | m_data.inst16 = inst; |
168 | m_byte_order = order; |
169 | } |
170 | |
171 | void SetOpcode16_2(uint32_t inst, lldb::ByteOrder order) { |
172 | m_type = eType16_2; |
173 | m_data.inst32 = inst; |
174 | m_byte_order = order; |
175 | } |
176 | |
177 | void SetOpcode32(uint32_t inst, lldb::ByteOrder order) { |
178 | m_type = eType32; |
179 | m_data.inst32 = inst; |
180 | m_byte_order = order; |
181 | } |
182 | |
183 | void SetOpcode64(uint64_t inst, lldb::ByteOrder order) { |
184 | m_type = eType64; |
185 | m_data.inst64 = inst; |
186 | m_byte_order = order; |
187 | } |
188 | |
189 | void SetOpcodeBytes(const void *bytes, size_t length) { |
190 | if (bytes != nullptr && length > 0) { |
191 | m_type = eTypeBytes; |
192 | m_data.inst.length = length; |
193 | assert(length < sizeof(m_data.inst.bytes)); |
194 | memcpy(dest: m_data.inst.bytes, src: bytes, n: length); |
195 | m_byte_order = lldb::eByteOrderInvalid; |
196 | } else { |
197 | m_type = eTypeInvalid; |
198 | m_data.inst.length = 0; |
199 | } |
200 | } |
201 | |
202 | int Dump(Stream *s, uint32_t min_byte_width); |
203 | |
204 | const void *GetOpcodeBytes() const { |
205 | return ((m_type == Opcode::eTypeBytes) ? m_data.inst.bytes : nullptr); |
206 | } |
207 | |
208 | uint32_t GetByteSize() const { |
209 | switch (m_type) { |
210 | case Opcode::eTypeInvalid: |
211 | break; |
212 | case Opcode::eType8: |
213 | return sizeof(m_data.inst8); |
214 | case Opcode::eType16: |
215 | return sizeof(m_data.inst16); |
216 | case Opcode::eType16_2: // passthrough |
217 | case Opcode::eType32: |
218 | return sizeof(m_data.inst32); |
219 | case Opcode::eType64: |
220 | return sizeof(m_data.inst64); |
221 | case Opcode::eTypeBytes: |
222 | return m_data.inst.length; |
223 | } |
224 | return 0; |
225 | } |
226 | |
227 | // Get the opcode exactly as it would be laid out in memory. |
228 | uint32_t (DataExtractor &data) const; |
229 | |
230 | protected: |
231 | friend class lldb::SBInstruction; |
232 | |
233 | const void *GetOpcodeDataBytes() const { |
234 | switch (m_type) { |
235 | case Opcode::eTypeInvalid: |
236 | break; |
237 | case Opcode::eType8: |
238 | return &m_data.inst8; |
239 | case Opcode::eType16: |
240 | return &m_data.inst16; |
241 | case Opcode::eType16_2: // passthrough |
242 | case Opcode::eType32: |
243 | return &m_data.inst32; |
244 | case Opcode::eType64: |
245 | return &m_data.inst64; |
246 | case Opcode::eTypeBytes: |
247 | return m_data.inst.bytes; |
248 | } |
249 | return nullptr; |
250 | } |
251 | |
252 | lldb::ByteOrder GetDataByteOrder() const; |
253 | |
254 | bool GetEndianSwap() const { |
255 | return (m_byte_order == lldb::eByteOrderBig && |
256 | endian::InlHostByteOrder() == lldb::eByteOrderLittle) || |
257 | (m_byte_order == lldb::eByteOrderLittle && |
258 | endian::InlHostByteOrder() == lldb::eByteOrderBig); |
259 | } |
260 | |
261 | lldb::ByteOrder m_byte_order = lldb::eByteOrderInvalid; |
262 | |
263 | Opcode::Type m_type = eTypeInvalid; |
264 | union { |
265 | uint8_t inst8; |
266 | uint16_t inst16; |
267 | uint32_t inst32; |
268 | uint64_t inst64; |
269 | struct { |
270 | uint8_t bytes[16]; // This must be big enough to handle any opcode for any |
271 | // supported target. |
272 | uint8_t length; |
273 | } inst; |
274 | } m_data; |
275 | }; |
276 | |
277 | } // namespace lldb_private |
278 | |
279 | #endif // LLDB_CORE_OPCODE_H |
280 | |