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 <bit> 24 #include <cstdint> 25 #include <string> 26 27 #include "berberis/base/arena_alloc.h" 28 #include "berberis/base/arena_vector.h" 29 #include "berberis/base/forever_map.h" 30 #include "berberis/base/macros.h" // DISALLOW_COPY_AND_ASSIGN 31 32 #if defined(__riscv) 33 #include <sys/cachectl.h> 34 #endif 35 36 namespace berberis { 37 38 enum class InstructionSize { 39 // x86 assembly has 1 byte instructions. 40 OneByte, 41 // riscv and arm64 assembly have 4 bytes instructions. 42 FourBytes, 43 }; 44 45 enum class RelocationType { 46 // Convert absolute address to PC-relative displacement. 47 // Ensure displacement fits in 32-bit value. 48 RelocAbsToDisp32, 49 // Add recovery point and recovery code to global recovery code map. 50 // TODO(eaeltsin): have recovery map for each region instead! 51 RelocRecoveryPoint, 52 }; 53 54 using RecoveryMap = ForeverMap<uintptr_t, uintptr_t>; 55 56 // Generated machine code for host architecture. Used by trampolines 57 // and JIT translator. 58 // NOTE: this class is not intended for concurrent usage by multiple threads. 59 class MachineCode { 60 public: MachineCode()61 MachineCode() : code_(&arena_), relocations_(&arena_) { 62 // The amount is chosen according to the performance of spec2000 benchmarks. 63 code_.reserve(1024); 64 } 65 arena()66 Arena* arena() { return &arena_; } 67 68 // TODO(eaeltsin): this will include const pool size when supported. install_size()69 [[nodiscard]] uint32_t install_size() const { return code_.size(); } 70 code_offset()71 [[nodiscard]] uint32_t code_offset() const { return code_.size(); } 72 73 template <typename T> AddrAs(uint32_t offset)74 T* AddrAs(uint32_t offset) { 75 return reinterpret_cast<T*>(AddrOf(offset)); 76 } 77 78 template <typename T> AddrAs(uint32_t offset)79 [[nodiscard]] const T* AddrAs(uint32_t offset) const { 80 return std::bit_cast<const T*>(AddrOf(offset)); 81 } 82 83 template <typename T> Add(const T & v)84 void Add(const T& v) { 85 memcpy(AddrAs<T>(Grow(sizeof(T))), &v, sizeof(T)); 86 } 87 88 template <typename T> AddSequence(const T * v,uint32_t count)89 void AddSequence(const T* v, uint32_t count) { 90 memcpy(AddrAs<T>(Grow(sizeof(T) * count)), v, sizeof(T) * count); 91 } 92 AddU8(uint8_t v)93 void AddU8(uint8_t v) { code_.push_back(v); } 94 95 void AsString(std::string* result, InstructionSize insn_size) const; 96 AddRelocation(uint32_t dst,RelocationType type,uint32_t pc,intptr_t data)97 void AddRelocation(uint32_t dst, RelocationType type, uint32_t pc, intptr_t data) { 98 relocations_.push_back(Relocation{dst, type, pc, data}); 99 } 100 101 // Install to executable memory. 102 template <typename ExecRegionType> Install(ExecRegionType * exec,const uint8_t * code,RecoveryMap * recovery_map)103 void Install(ExecRegionType* exec, const uint8_t* code, RecoveryMap* recovery_map) { 104 PerformRelocations(code, recovery_map); 105 exec->Write(code, AddrAs<uint8_t>(0), code_.size()); 106 #if defined(__riscv) 107 __riscv_flush_icache((void*)code, (void*)(code + code_.size()), 0); 108 #endif 109 } 110 111 // Install to writable memory. InstallUnsafe(uint8_t * code,RecoveryMap * recovery_map)112 void InstallUnsafe(uint8_t* code, RecoveryMap* recovery_map) { 113 PerformRelocations(code, recovery_map); 114 memcpy(code, AddrAs<uint8_t>(0), code_.size()); 115 } 116 117 // Print generated code to stderr. 118 void DumpCode(InstructionSize insn_size) const; 119 120 private: 121 struct Relocation { 122 uint32_t dst; 123 RelocationType type; 124 uint32_t pc; 125 intptr_t data; 126 }; 127 using RelocationList = ArenaVector<Relocation>; 128 129 [[nodiscard]] uint8_t* AddrOf(uint32_t offset); 130 [[nodiscard]] const uint8_t* AddrOf(uint32_t offset) const; 131 uint32_t Grow(uint32_t count); 132 133 // Relocate the code, in assumption it is to be installed at address 'code'. 134 void PerformRelocations(const uint8_t* code, RecoveryMap* recovery_map); 135 136 Arena arena_; 137 ArenaVector<uint8_t> code_; 138 RelocationList relocations_; 139 140 DISALLOW_COPY_AND_ASSIGN(MachineCode); 141 }; 142 143 } // namespace berberis 144 145 #endif // BERBERIS_ASSEMBLER_MACHINE_CODE_H_ 146