1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //
18 // Generated machine code.
19 
20 #ifndef BERBERIS_ASSEMBLER_MACHINE_CODE_H_
21 #define BERBERIS_ASSEMBLER_MACHINE_CODE_H_
22 
23 #include <cstdint>
24 #include <string>
25 
26 #include "berberis/base/arena_alloc.h"
27 #include "berberis/base/arena_vector.h"
28 #include "berberis/base/forever_map.h"
29 #include "berberis/base/macros.h"  // DISALLOW_COPY_AND_ASSIGN
30 
31 #if defined(__riscv)
32 #include <sys/cachectl.h>
33 #endif
34 
35 namespace berberis {
36 
37 enum class InstructionSize {
38   // x86 assembly has 1 byte instructions.
39   OneByte,
40   // riscv and arm64 assembly have 4 bytes instructions.
41   FourBytes,
42 };
43 
44 enum class RelocationType {
45   // Convert absolute address to PC-relative displacement.
46   // Ensure displacement fits in 32-bit value.
47   RelocAbsToDisp32,
48   // Add recovery point and recovery code to global recovery code map.
49   // TODO(eaeltsin): have recovery map for each region instead!
50   RelocRecoveryPoint,
51 };
52 
53 using RecoveryMap = ForeverMap<uintptr_t, uintptr_t>;
54 
55 // Generated machine code for host architecture. Used by trampolines
56 // and JIT translator.
57 // NOTE: this class is not intended for concurrent usage by multiple threads.
58 class MachineCode {
59  public:
MachineCode()60   MachineCode() : code_(&arena_), relocations_(&arena_) {
61     // The amount is chosen according to the performance of spec2000 benchmarks.
62     code_.reserve(1024);
63   }
64 
arena()65   Arena* arena() { return &arena_; }
66 
67   // TODO(eaeltsin): this will include const pool size when supported.
install_size()68   [[nodiscard]] uint32_t install_size() const { return code_.size(); }
69 
code_offset()70   [[nodiscard]] uint32_t code_offset() const { return code_.size(); }
71 
72   template <typename T>
AddrAs(uint32_t offset)73   T* AddrAs(uint32_t offset) {
74     return reinterpret_cast<T*>(AddrOf(offset));
75   }
76 
77   template <typename T>
AddrAs(uint32_t offset)78   [[nodiscard]] const T* AddrAs(uint32_t offset) const {
79     return reinterpret_cast<const T*>(AddrOf(offset));
80   }
81 
82   template <typename T>
Add(const T & v)83   void Add(const T& v) {
84     memcpy(AddrAs<T>(Grow(sizeof(T))), &v, sizeof(T));
85   }
86 
87   template <typename T>
AddSequence(const T * v,uint32_t count)88   void AddSequence(const T* v, uint32_t count) {
89     memcpy(AddrAs<T>(Grow(sizeof(T) * count)), v, sizeof(T) * count);
90   }
91 
AddU8(uint8_t v)92   void AddU8(uint8_t v) { code_.push_back(v); }
93 
94   void AsString(std::string* result, InstructionSize insn_size) const;
95 
AddRelocation(uint32_t dst,RelocationType type,uint32_t pc,intptr_t data)96   void AddRelocation(uint32_t dst, RelocationType type, uint32_t pc, intptr_t data) {
97     relocations_.push_back(Relocation{dst, type, pc, data});
98   }
99 
100   // Install to executable memory.
101   template <typename ExecRegionType>
Install(ExecRegionType * exec,const uint8_t * code,RecoveryMap * recovery_map)102   void Install(ExecRegionType* exec, const uint8_t* code, RecoveryMap* recovery_map) {
103     PerformRelocations(code, recovery_map);
104     exec->Write(code, AddrAs<uint8_t>(0), code_.size());
105 #if defined(__riscv)
106     __riscv_flush_icache((void*)code, (void*)(code + code_.size()), 0);
107 #endif
108   }
109 
110   // Install to writable memory.
InstallUnsafe(uint8_t * code,RecoveryMap * recovery_map)111   void InstallUnsafe(uint8_t* code, RecoveryMap* recovery_map) {
112     PerformRelocations(code, recovery_map);
113     memcpy(code, AddrAs<uint8_t>(0), code_.size());
114   }
115 
116   // Print generated code to stderr.
117   void DumpCode(InstructionSize insn_size) const;
118 
119  private:
120   struct Relocation {
121     uint32_t dst;
122     RelocationType type;
123     uint32_t pc;
124     intptr_t data;
125   };
126   using RelocationList = ArenaVector<Relocation>;
127 
128   [[nodiscard]] uint8_t* AddrOf(uint32_t offset);
129   [[nodiscard]] const uint8_t* AddrOf(uint32_t offset) const;
130   uint32_t Grow(uint32_t count);
131 
132   // Relocate the code, in assumption it is to be installed at address 'code'.
133   void PerformRelocations(const uint8_t* code, RecoveryMap* recovery_map);
134 
135   Arena arena_;
136   ArenaVector<uint8_t> code_;
137   RelocationList relocations_;
138 
139   DISALLOW_COPY_AND_ASSIGN(MachineCode);
140 };
141 
142 }  // namespace berberis
143 
144 #endif  // BERBERIS_ASSEMBLER_MACHINE_CODE_H_
145