1 /*
2 * Copyright (C) 2023 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 #include "berberis/runtime/translator.h"
18 #include "translator.h"
19
20 #include <cstdint>
21 #include <cstdlib>
22 #include <tuple>
23
24 #include "berberis/assembler/machine_code.h"
25 #include "berberis/guest_os_primitives/guest_map_shadow.h"
26 #include "berberis/interpreter/riscv64/interpreter.h"
27 #include "berberis/runtime_primitives/code_pool.h"
28 #include "berberis/runtime_primitives/host_code.h"
29 #include "berberis/runtime_primitives/profiler_interface.h"
30 #include "berberis/runtime_primitives/translation_cache.h"
31 #include "berberis/runtime_primitives/virtual_guest_call_frame.h"
32
33 namespace berberis {
34
35 namespace {
36
37 // Syntax sugar.
38 GuestCodeEntry::Kind kSpecialHandler = GuestCodeEntry::Kind::kSpecialHandler;
39
40 // Use aligned address of this variable as the default stop address for guest execution.
41 // It should never coincide with any guest address or address of a wrapped host symbol.
42 // Unwinder might examine nearby insns.
43 alignas(4) uint32_t g_native_bridge_call_guest[] = {
44 // <native_bridge_call_guest>:
45 0xd503201f, // nop
46 0xd503201f, // nop <--
47 0xd503201f, // nop
48 };
49
GetRiscv64InsnSize(GuestAddr pc)50 uint8_t GetRiscv64InsnSize(GuestAddr pc) {
51 constexpr uint16_t kInsnLenMask = uint16_t{0b11};
52 if ((*ToHostAddr<uint16_t>(pc) & kInsnLenMask) != kInsnLenMask) {
53 return 2;
54 }
55 return 4;
56 }
57
58 } // namespace
59
InstallTranslated(MachineCode * machine_code,GuestAddr pc,size_t size,const char * prefix)60 HostCodePiece InstallTranslated(MachineCode* machine_code,
61 GuestAddr pc,
62 size_t size,
63 const char* prefix) {
64 HostCodeAddr host_code = GetDefaultCodePoolInstance()->Add(machine_code);
65 ProfilerLogGeneratedCode(AsHostCode(host_code), machine_code->install_size(), pc, size, prefix);
66 return {host_code, machine_code->install_size()};
67 }
68
69 // Check whether the given guest program counter is executable, accounting for compressed
70 // instructions. Returns a tuple indicating whether the memory is executable and the size of the
71 // first instruction in bytes.
IsPcExecutable(GuestAddr pc,GuestMapShadow * guest_map_shadow)72 std::tuple<bool, uint8_t> IsPcExecutable(GuestAddr pc, GuestMapShadow* guest_map_shadow) {
73 // First check if the instruction would be in executable memory if it is compressed. This
74 // prevents dereferencing unknown memory to determine the size of the instruction.
75 constexpr uint8_t kMinimumInsnSize = 2;
76 if (!guest_map_shadow->IsExecutable(pc, kMinimumInsnSize)) {
77 return {false, kMinimumInsnSize};
78 }
79
80 // Now check the rest of the instruction based on its size. It is now safe to dereference the
81 // memory at pc because at least two bytes are within known executable memory.
82 uint8_t first_insn_size = GetRiscv64InsnSize(pc);
83 if (first_insn_size > kMinimumInsnSize &&
84 !guest_map_shadow->IsExecutable(pc + kMinimumInsnSize, first_insn_size - kMinimumInsnSize)) {
85 return {false, first_insn_size};
86 }
87
88 return {true, first_insn_size};
89 }
90
InitTranslator()91 void InitTranslator() {
92 InitTranslatorArch();
93 InitVirtualGuestCallFrameReturnAddress(ToGuestAddr(g_native_bridge_call_guest + 1));
94 InitInterpreter();
95 }
96
97 } // namespace berberis
98