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 DataExtractor;
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_16(m_data.inst16) : m_data.inst16;
103     case Opcode::eType16_2:
104       break;
105     case Opcode::eType32:
106       break;
107     case Opcode::eType64:
108       break;
109     case Opcode::eTypeBytes:
110       break;
111     }
112     return invalid_opcode;
113   }
114 
115   uint32_t GetOpcode32(uint32_t invalid_opcode = UINT32_MAX) const {
116     switch (m_type) {
117     case Opcode::eTypeInvalid:
118       break;
119     case Opcode::eType8:
120       return m_data.inst8;
121     case Opcode::eType16:
122       return GetEndianSwap() ? llvm::ByteSwap_16(m_data.inst16) : m_data.inst16;
123     case Opcode::eType16_2: // passthrough
124     case Opcode::eType32:
125       return GetEndianSwap() ? llvm::ByteSwap_32(m_data.inst32) : m_data.inst32;
126     case Opcode::eType64:
127       break;
128     case Opcode::eTypeBytes:
129       break;
130     }
131     return invalid_opcode;
132   }
133 
134   uint64_t GetOpcode64(uint64_t invalid_opcode = UINT64_MAX) const {
135     switch (m_type) {
136     case Opcode::eTypeInvalid:
137       break;
138     case Opcode::eType8:
139       return m_data.inst8;
140     case Opcode::eType16:
141       return GetEndianSwap() ? llvm::ByteSwap_16(m_data.inst16) : m_data.inst16;
142     case Opcode::eType16_2: // passthrough
143     case Opcode::eType32:
144       return GetEndianSwap() ? llvm::ByteSwap_32(m_data.inst32) : m_data.inst32;
145     case Opcode::eType64:
146       return GetEndianSwap() ? llvm::ByteSwap_64(m_data.inst64) : m_data.inst64;
147     case Opcode::eTypeBytes:
148       break;
149     }
150     return invalid_opcode;
151   }
152 
153   void SetOpcode8(uint8_t inst, lldb::ByteOrder order) {
154     m_type = eType8;
155     m_data.inst8 = inst;
156     m_byte_order = order;
157   }
158 
159   void SetOpcode16(uint16_t inst, lldb::ByteOrder order) {
160     m_type = eType16;
161     m_data.inst16 = inst;
162     m_byte_order = order;
163   }
164 
165   void SetOpcode16_2(uint32_t inst, lldb::ByteOrder order) {
166     m_type = eType16_2;
167     m_data.inst32 = inst;
168     m_byte_order = order;
169   }
170 
171   void SetOpcode32(uint32_t inst, lldb::ByteOrder order) {
172     m_type = eType32;
173     m_data.inst32 = inst;
174     m_byte_order = order;
175   }
176 
177   void SetOpcode64(uint64_t inst, lldb::ByteOrder order) {
178     m_type = eType64;
179     m_data.inst64 = inst;
180     m_byte_order = order;
181   }
182 
183   void SetOpcodeBytes(const void *bytes, size_t length) {
184     if (bytes != nullptr && length > 0) {
185       m_type = eTypeBytes;
186       m_data.inst.length = length;
187       assert(length < sizeof(m_data.inst.bytes));
188       memcpy(m_data.inst.bytes, bytes, length);
189       m_byte_order = lldb::eByteOrderInvalid;
190     } else {
191       m_type = eTypeInvalid;
192       m_data.inst.length = 0;
193     }
194   }
195 
196   int Dump(Stream *s, uint32_t min_byte_width);
197 
198   const void *GetOpcodeBytes() const {
199     return ((m_type == Opcode::eTypeBytes) ? m_data.inst.bytes : nullptr);
200   }
201 
202   uint32_t GetByteSize() const {
203     switch (m_type) {
204     case Opcode::eTypeInvalid:
205       break;
206     case Opcode::eType8:
207       return sizeof(m_data.inst8);
208     case Opcode::eType16:
209       return sizeof(m_data.inst16);
210     case Opcode::eType16_2: // passthrough
211     case Opcode::eType32:
212       return sizeof(m_data.inst32);
213     case Opcode::eType64:
214       return sizeof(m_data.inst64);
215     case Opcode::eTypeBytes:
216       return m_data.inst.length;
217     }
218     return 0;
219   }
220 
221   // Get the opcode exactly as it would be laid out in memory.
222   uint32_t GetData(DataExtractor &data) const;
223 
224 protected:
225   friend class lldb::SBInstruction;
226 
227   const void *GetOpcodeDataBytes() const {
228     switch (m_type) {
229     case Opcode::eTypeInvalid:
230       break;
231     case Opcode::eType8:
232       return &m_data.inst8;
233     case Opcode::eType16:
234       return &m_data.inst16;
235     case Opcode::eType16_2: // passthrough
236     case Opcode::eType32:
237       return &m_data.inst32;
238     case Opcode::eType64:
239       return &m_data.inst64;
240     case Opcode::eTypeBytes:
241       return m_data.inst.bytes;
242     }
243     return nullptr;
244   }
245 
246   lldb::ByteOrder GetDataByteOrder() const;
247 
248   bool GetEndianSwap() const {
249     return (m_byte_order == lldb::eByteOrderBig &&
250             endian::InlHostByteOrder() == lldb::eByteOrderLittle) ||
251            (m_byte_order == lldb::eByteOrderLittle &&
252             endian::InlHostByteOrder() == lldb::eByteOrderBig);
253   }
254 
255   lldb::ByteOrder m_byte_order = lldb::eByteOrderInvalid;
256 
257   Opcode::Type m_type = eTypeInvalid;
258   union {
259     uint8_t inst8;
260     uint16_t inst16;
261     uint32_t inst32;
262     uint64_t inst64;
263     struct {
264       uint8_t bytes[16]; // This must be big enough to handle any opcode for any
265                          // supported target.
266       uint8_t length;
267     } inst;
268   } m_data;
269 };
270 
271 } // namespace lldb_private
272 
273 #endif // LLDB_CORE_OPCODE_H
274