• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef LIBPANDAFILE_BYTECODE_INSTRUCTION_H
17 #define LIBPANDAFILE_BYTECODE_INSTRUCTION_H
18 
19 #include "file.h"
20 
21 #include <cstdint>
22 #include <cstddef>
23 #include <type_traits>
24 
25 #include "utils/bit_helpers.h"
26 
27 #if !PANDA_TARGET_WINDOWS
28 #include "securec.h"
29 #endif
30 
31 namespace panda {
32 
33 enum class BytecodeInstMode { FAST, SAFE };
34 
35 template <const BytecodeInstMode>
36 class BytecodeInstBase;
37 
38 class BytecodeId {
39 public:
BytecodeId(uint32_t id)40     constexpr explicit BytecodeId(uint32_t id) : id_(id) {}
41 
42     constexpr BytecodeId() = default;
43 
44     ~BytecodeId() = default;
45 
46     DEFAULT_COPY_SEMANTIC(BytecodeId);
47     NO_MOVE_SEMANTIC(BytecodeId);
48 
AsIndex()49     panda_file::File::Index AsIndex() const
50     {
51         ASSERT(id_ < std::numeric_limits<uint16_t>::max());
52         return id_;
53     }
54 
AsFileId()55     panda_file::File::EntityId AsFileId() const
56     {
57         return panda_file::File::EntityId(id_);
58     }
59 
AsRawValue()60     uint32_t AsRawValue() const
61     {
62         return id_;
63     }
64 
IsValid()65     bool IsValid() const
66     {
67         return id_ != INVALID;
68     }
69 
70     bool operator==(BytecodeId id) const noexcept
71     {
72         return id_ == id.id_;
73     }
74 
75     friend std::ostream &operator<<(std::ostream &stream, BytecodeId id)
76     {
77         return stream << id.id_;
78     }
79 
80 private:
81     static constexpr size_t INVALID = std::numeric_limits<uint32_t>::max();
82 
83     uint32_t id_ {INVALID};
84 };
85 
86 template <>
87 // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions)
88 class BytecodeInstBase<BytecodeInstMode::FAST> {
89 public:
90     BytecodeInstBase() = default;
BytecodeInstBase(const uint8_t * pc)91     explicit BytecodeInstBase(const uint8_t *pc) : pc_ {pc} {}
92     ~BytecodeInstBase() = default;
93 
94 protected:
GetPointer(int32_t offset)95     const uint8_t *GetPointer(int32_t offset) const
96     {
97         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
98         return pc_ + offset;
99     }
100 
GetAddress()101     const uint8_t *GetAddress() const
102     {
103         return pc_;
104     }
105 
GetAddress()106     const uint8_t *GetAddress() volatile const
107     {
108         return pc_;
109     }
110 
111     template <class T>
Read(size_t offset)112     T Read(size_t offset) const
113     {
114         using unaligned_type __attribute__((aligned(1))) = const T;
115         return *reinterpret_cast<unaligned_type *>(GetPointer(offset));
116     }
117 
Write(uint32_t value,uint32_t offset,uint32_t width)118     void Write(uint32_t value, uint32_t offset, uint32_t width)
119     {
120         auto *dst = const_cast<uint8_t *>(GetPointer(offset));
121         if (memcpy_s(dst, width, &value, width) != 0) {
122             LOG(FATAL, PANDAFILE) << "Cannot write value : " << value << "at the dst offset : " << offset;
123         }
124     }
125 
ReadByte(size_t offset)126     uint8_t ReadByte(size_t offset) const
127     {
128         return Read<uint8_t>(offset);
129     }
130 
131 private:
132     const uint8_t *pc_ {nullptr};
133 };
134 
135 template <>
136 class BytecodeInstBase<BytecodeInstMode::SAFE> {
137 public:
138     BytecodeInstBase() = default;
BytecodeInstBase(const uint8_t * pc,const uint8_t * from,const uint8_t * to)139     explicit BytecodeInstBase(const uint8_t *pc, const uint8_t *from, const uint8_t *to)
140         : pc_ {pc}, from_ {from}, to_ {to}, valid_ {true}
141     {
142         ASSERT(from_ <= to_ && pc_ >= from_ && pc_ <= to_);
143     }
144 
145 protected:
GetPointer(int32_t offset)146     const uint8_t *GetPointer(int32_t offset) const
147     {
148         return GetPointer(offset, 1);
149     }
150 
IsLast(size_t size)151     bool IsLast(size_t size) const
152     {
153         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
154         const uint8_t *ptr_next = pc_ + size;
155         return ptr_next > to_;
156     }
157 
GetPointer(int32_t offset,size_t size)158     const uint8_t *GetPointer(int32_t offset, size_t size) const
159     {
160         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
161         const uint8_t *ptr_from = pc_ + offset;
162         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
163         const uint8_t *ptr_to = ptr_from + size - 1;
164         if (from_ == nullptr || ptr_from < from_ || ptr_to > to_) {
165             valid_ = false;
166             return from_;
167         }
168         return ptr_from;
169     }
170 
GetAddress()171     const uint8_t *GetAddress() const
172     {
173         return pc_;
174     }
175 
GetFrom()176     const uint8_t *GetFrom() const
177     {
178         return from_;
179     }
180 
GetTo()181     const uint8_t *GetTo() const
182     {
183         return to_;
184     }
185 
GetOffset()186     uint32_t GetOffset() const
187     {
188         return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc_) - reinterpret_cast<uintptr_t>(from_));
189     }
190 
GetAddress()191     const uint8_t *GetAddress() volatile const
192     {
193         return pc_;
194     }
195 
196     template <class T>
Read(size_t offset)197     T Read(size_t offset) const
198     {
199         using unaligned_type __attribute__((aligned(1))) = const T;
200         auto ptr = reinterpret_cast<unaligned_type *>(GetPointer(offset, sizeof(T)));
201         if (IsValid()) {
202             return *ptr;
203         }
204         return {};
205     }
206 
IsValid()207     bool IsValid() const
208     {
209         return valid_;
210     }
211 
212 private:
213     const uint8_t *pc_ {nullptr};
214     const uint8_t *from_ {nullptr};
215     const uint8_t *to_ {nullptr};
216     mutable bool valid_ {false};
217 };
218 
219 template <const BytecodeInstMode Mode = BytecodeInstMode::FAST>
220 
221 // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions)
222 class BytecodeInst : public BytecodeInstBase<Mode> {
223     using Base = BytecodeInstBase<Mode>;
224 
225 public:
226 #include <bytecode_instruction_enum_gen.h>
227 
228     BytecodeInst() = default;
229 
230     ~BytecodeInst() = default;
231 
232     template <const BytecodeInstMode M = Mode, typename = std::enable_if_t<M == BytecodeInstMode::FAST>>
BytecodeInst(const uint8_t * pc)233     explicit BytecodeInst(const uint8_t *pc) : Base {pc}
234     {
235     }
236 
237     template <const BytecodeInstMode M = Mode, typename = std::enable_if_t<M == BytecodeInstMode::SAFE>>
BytecodeInst(const uint8_t * pc,const uint8_t * from,const uint8_t * to)238     explicit BytecodeInst(const uint8_t *pc, const uint8_t *from, const uint8_t *to) : Base {pc, from, to}
239     {
240     }
241 
242     template <Format format, size_t idx = 0>
243     BytecodeId GetId() const;
244 
245     template <Format format, size_t idx = 0>
246     uint16_t GetVReg() const;
247 
248     template <Format format, size_t idx = 0>
249     auto GetImm() const;
250 
251     BytecodeId GetId(size_t idx = 0) const;
252 
253     void UpdateId(BytecodeId new_id, uint32_t idx = 0);
254 
255     uint16_t GetVReg(size_t idx = 0) const;
256 
257     // Read imm and return it as int64_t/uint64_t
258     auto GetImm64(size_t idx = 0) const;
259 
260     /**
261      * Primary and Secondary Opcodes are used in interpreter/verifier instruction dispatch
262      * while full Opcode is typically used for various instruction property query.
263      *
264      * Implementation note: one can describe Opcode in terms of Primary/Secondary opcodes
265      * or vice versa. The first way is more preferable, because Primary/Secondary opcodes
266      * are more performance critical and compiler is not always clever enough to reduce them
267      * to simple byte reads.
268      */
269     BytecodeInst::Opcode GetOpcode() const;
270 
GetPrimaryOpcode()271     uint8_t GetPrimaryOpcode() const
272     {
273         return ReadByte(0);
274     }
275 
276     bool IsPrimaryOpcodeValid() const;
277 
278     uint8_t GetSecondaryOpcode() const;
279 
280     bool IsPrefixed() const;
281 
282     static constexpr uint8_t GetMinPrefixOpcodeIndex();
283 
284     template <const BytecodeInstMode M = Mode>
285     auto JumpTo(int32_t offset) const -> std::enable_if_t<M == BytecodeInstMode::FAST, BytecodeInst>
286     {
287         return BytecodeInst(Base::GetPointer(offset));
288     }
289 
290     template <const BytecodeInstMode M = Mode>
291     auto JumpTo(int32_t offset) const -> std::enable_if_t<M == BytecodeInstMode::SAFE, BytecodeInst>
292     {
293         if (!IsValid()) {
294             return {};
295         }
296         const uint8_t *ptr = Base::GetPointer(offset);
297         if (!IsValid()) {
298             return {};
299         }
300         return BytecodeInst(ptr, Base::GetFrom(), Base::GetTo());
301     }
302 
303     template <const BytecodeInstMode M = Mode>
304     auto IsLast() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, bool>
305     {
306         return Base::IsLast(GetSize());
307     }
308 
309     template <const BytecodeInstMode M = Mode>
310     auto IsValid() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, bool>
311     {
312         return Base::IsValid();
313     }
314 
315     template <Format format>
GetNext()316     BytecodeInst GetNext() const
317     {
318         return JumpTo(Size(format));
319     }
320 
GetNext()321     BytecodeInst GetNext() const
322     {
323         return JumpTo(GetSize());
324     }
325 
GetAddress()326     const uint8_t *GetAddress() const
327     {
328         return Base::GetAddress();
329     }
330 
GetAddress()331     const uint8_t *GetAddress() volatile const
332     {
333         return Base::GetAddress();
334     }
335 
336     template <const BytecodeInstMode M = Mode>
337     auto GetFrom() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, const uint8_t *>
338     {
339         return Base::GetFrom();
340     }
341 
342     template <const BytecodeInstMode M = Mode>
343     auto GetTo() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, const uint8_t *>
344     {
345         return Base::GetTo();
346     }
347 
348     template <const BytecodeInstMode M = Mode>
349     auto GetOffset() const -> std::enable_if_t<M == BytecodeInstMode::SAFE, uint32_t>
350     {
351         return Base::GetOffset();
352     }
353 
ReadByte(size_t offset)354     uint8_t ReadByte(size_t offset) const
355     {
356         return Base::template Read<uint8_t>(offset);
357     }
358 
359     template <class R, class S>
360     auto ReadHelper(size_t byteoffset, size_t bytecount, size_t offset, size_t width) const;
361 
362     template <size_t offset, size_t width, bool is_signed = false>
363     auto Read() const;
364 
365     template <bool is_signed = false>
366     auto Read64(size_t offset, size_t width) const;
367 
368     size_t GetSize() const;
369 
370     Format GetFormat() const;
371 
372     bool HasFlag(Flags flag) const;
373 
374     bool IsIdMatchFlag(size_t idx, Flags flag) const;  // idx-th id matches flag or not
375 
376     bool IsThrow(Exceptions exception) const;
377 
378     bool CanThrow() const;
379 
IsTerminator()380     bool IsTerminator() const
381     {
382         return HasFlag(Flags::RETURN) || HasFlag(Flags::JUMP) || IsThrow(Exceptions::X_THROW);
383     }
384 
IsSuspend()385     bool IsSuspend() const
386     {
387         return HasFlag(Flags::SUSPEND);
388     }
389 
390     static constexpr bool HasId(Format format, size_t idx);
391 
392     static constexpr bool HasVReg(Format format, size_t idx);
393 
394     static constexpr bool HasImm(Format format, size_t idx);
395 
396     static constexpr Format GetFormat(Opcode opcode);
397 
398     static constexpr size_t Size(Format format);
399 
Size(Opcode opcode)400     static constexpr size_t Size(Opcode opcode)
401     {
402         return Size(GetFormat(opcode));
403     }
404 };
405 
406 template <const BytecodeInstMode Mode>
407 std::ostream &operator<<(std::ostream &os, const BytecodeInst<Mode> &inst);
408 
409 using BytecodeInstruction = BytecodeInst<BytecodeInstMode::FAST>;
410 using BytecodeInstructionSafe = BytecodeInst<BytecodeInstMode::SAFE>;
411 
412 }  // namespace panda
413 
414 #endif  // LIBPANDAFILE_BYTECODE_INSTRUCTION_H
415