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<uint16_t>(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>(m_data.inst16)
124                              : m_data.inst16;
125     case Opcode::eType16_2: // passthrough
126     case Opcode::eType32:
127       return GetEndianSwap() ? llvm::byteswap<uint32_t>(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>(m_data.inst16)
145                              : m_data.inst16;
146     case Opcode::eType16_2: // passthrough
147     case Opcode::eType32:
148       return GetEndianSwap() ? llvm::byteswap<uint32_t>(m_data.inst32)
149                              : m_data.inst32;
150     case Opcode::eType64:
151       return GetEndianSwap() ? llvm::byteswap<uint64_t>(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(m_data.inst.bytes, bytes, 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 GetData(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