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 #include "berberis/code_gen_lib/code_gen_lib.h"
18 
19 #include "berberis/assembler/machine_code.h"
20 #include "berberis/assembler/x86_32.h"
21 #include "berberis/base/bit_util.h"
22 #include "berberis/base/config.h"
23 #include "berberis/calling_conventions/calling_conventions_x86_32.h"
24 #include "berberis/code_gen_lib/code_gen_lib_arch.h"
25 #include "berberis/guest_state/guest_addr.h"
26 #include "berberis/guest_state/guest_state.h"
27 #include "berberis/instrument/trampolines.h"
28 #include "berberis/runtime_primitives/host_code.h"
29 #include "berberis/runtime_primitives/translation_cache.h"
30 
31 namespace berberis {
32 
33 namespace x86_32 {
34 
35 namespace {
36 
37 // State register pointer must be callee saved.
38 // Use of EBP allows shorter context read instructions.
39 constexpr Assembler::Register kStateRegister = Assembler::ebp;
40 
41 // Emitted code checks if some emulated signal is pending. If yes,
42 // it returns to the main dispatcher to handle the signal.
43 // To ensure we don't loop endlessly in generated code without checks
44 // for pending signals, this must be called on every exit from region (given
45 // there are no loops in regions). Thus we call it in EmitJump for static
46 // branches out of the regions and EmitDispatch for dynamic ones.
EmitCheckSignalsAndMaybeReturn(Assembler * as)47 void EmitCheckSignalsAndMaybeReturn(Assembler* as) {
48   // C++:
49   //   std::atomic_int_least8_t pending_signals_status;
50   //   uint8_t status = pending_signals_status.load(std::memory_order_acquire);
51   //   if (status == kPendingSignalsPresent) { ... }
52   // x86_32 asm:
53   //   cmpb pending_signals_status, kPendingSignalsPresent
54   const size_t offset = offsetof(ThreadState, pending_signals_status);
55   as->Cmpb({.base = kStateRegister, .disp = offset}, kPendingSignalsPresent);
56   as->Jcc(Assembler::Condition::kEqual, kEntryExitGeneratedCode);
57 }
58 
59 // The offset of insn_addr is hard-coded in runtime_library_x86_32.S.  The
60 // static_assert below is to ensure that the offset is still as expected.
61 static_assert(offsetof(ThreadState, cpu.insn_addr) == 0x48, "");
62 
EmitDispatch(Assembler * as,Assembler::Register target)63 void EmitDispatch(Assembler* as, Assembler::Register target) {
64   // We are carrying target over in EAX, but we also need it in another
65   // temporary register that we'll clobber during mapping to the host address.
66   Assembler::Register reg1{Assembler::no_register};
67   if (target == Assembler::eax) {
68     reg1 = Assembler::ecx;
69     as->Movl(reg1, target);
70   } else {
71     reg1 = target;
72     as->Movl(Assembler::eax, target);
73   }
74 
75   // Allocate another temporary register.
76   Assembler::Register reg2 = reg1 == Assembler::ecx ? Assembler::edx : Assembler::ecx;
77 
78   if (!config::kLinkJumpsBetweenRegions) {
79     as->Jmp(kEntryExitGeneratedCode);
80     return;
81   }
82 
83   EmitCheckSignalsAndMaybeReturn(as);
84 
85   auto* translation_cache = TranslationCache::GetInstance();
86   auto main_table_ptr = translation_cache->main_table_ptr();
87 
88   // eax, reg1: guest pc
89   //
90   // movzwl %eax,%reg2
91   // shr    $0x10,%reg1
92   // mov    main_table_ptr(,%reg1,4),%reg1
93   // jmp    *(%reg1,%reg2,4)
94   as->Movzxwl(reg2, Assembler::eax);
95   as->Shrl(reg1, int8_t{16});
96   as->Movl(
97       reg1,
98       {.index = reg1, .scale = Assembler::kTimesFour, .disp = bit_cast<int32_t>(main_table_ptr)});
99   as->Jmpl({.base = reg1, .index = reg2, .scale = Assembler::kTimesFour});
100 }
101 
GenTrampolineAdaptor(MachineCode * mc,GuestAddr pc,HostCode marshall,const void * callee,const char * name)102 void GenTrampolineAdaptor(MachineCode* mc,
103                           GuestAddr pc,
104                           HostCode marshall,
105                           const void* callee,
106                           const char* name) {
107   Assembler as(mc);
108   // void Trampoline(void*, ThreadState*);
109   // void LogTrampoline(ThreadState*, const char*);
110   EmitAllocStackFrame(&as, 8);
111 
112   // Update insn_addr to the current PC.  This way, code generated by this
113   // function does not require insn_addr to be up to date upon entry.  Note that
114   // the trampoline that we call requires insn_addr to be up to date.
115   as.Movl({.base = kStateRegister, .disp = offsetof(ThreadState, cpu.insn_addr)}, pc);
116   as.Movl({.base = kStateRegister, .disp = offsetof(ThreadState, residence)},
117           kOutsideGeneratedCode);
118 
119   if (kInstrumentTrampolines) {
120     if (auto instrument = GetOnTrampolineCall(name)) {
121       as.Movl({.base = as.esp}, kStateRegister);
122       as.Movl({.base = as.esp, .disp = 4}, bit_cast<int32_t>(name));
123       as.Call(AsHostCode(instrument));
124     }
125   }
126 
127   as.Movl({.base = as.esp}, reinterpret_cast<uintptr_t>(callee));
128   as.Movl({.base = as.esp, .disp = 4}, kStateRegister);
129   as.Call(marshall);
130 
131   if (kInstrumentTrampolines) {
132     if (auto instrument = GetOnTrampolineReturn(name)) {
133       as.Movl({.base = as.esp}, kStateRegister);
134       as.Movl({.base = as.esp, .disp = 4}, bit_cast<int32_t>(name));
135       as.Call(AsHostCode(instrument));
136     }
137   }
138 
139   EmitFreeStackFrame(&as, 8);
140   // jump to guest return address
141   as.Movl(as.eax, {.base = kStateRegister, .disp = kReturnAddressRegisterOffset});
142   // We are returning to generated code.
143   as.Movl({.base = kStateRegister, .disp = offsetof(ThreadState, residence)}, kInsideGeneratedCode);
144   EmitDispatch(&as, as.eax);
145   as.Finalize();
146 }
147 
148 }  // namespace
149 
EmitAllocStackFrame(Assembler * as,uint32_t frame_size)150 void EmitAllocStackFrame(Assembler* as, uint32_t frame_size) {
151   if (frame_size > config::kFrameSizeAtTranslatedCode) {
152     uint32_t extra_size = AlignUp(frame_size - config::kFrameSizeAtTranslatedCode,
153                                   CallingConventions::kStackAlignmentBeforeCall);
154     as->Subl(Assembler::esp, extra_size);
155   }
156 }
157 
EmitFreeStackFrame(Assembler * as,uint32_t frame_size)158 void EmitFreeStackFrame(Assembler* as, uint32_t frame_size) {
159   if (frame_size > config::kFrameSizeAtTranslatedCode) {
160     uint32_t extra_size = AlignUp(frame_size - config::kFrameSizeAtTranslatedCode,
161                                   CallingConventions::kStackAlignmentBeforeCall);
162     as->Addl(Assembler::esp, extra_size);
163   }
164 }
165 
EmitJump(Assembler * as,GuestAddr target)166 void EmitJump(Assembler* as, GuestAddr target) {
167   // Attention! Always sync insn_addr as we may be jumping out of translated code (e.g.
168   // non-translated code handler or trampolines that require synced state to run signal handlers).
169   as->Movl(Assembler::eax, target);
170 
171   if (!config::kLinkJumpsBetweenRegions) {
172     as->Jmp(kEntryExitGeneratedCode);
173     return;
174   }
175 
176   EmitCheckSignalsAndMaybeReturn(as);
177 
178   // Now we have same stack state as we had on entry to this
179   // code, so we can just do tail call to other translation unit.
180   as->Jmpl({.disp = bit_cast<int32_t>(TranslationCache::GetInstance()->GetHostCodePtr(target))});
181 }
182 
183 // ATTENTION: 'target' should be a general register - see constraints for PseudoIndirectJump!
EmitIndirectJump(Assembler * as,Assembler::Register target)184 void EmitIndirectJump(Assembler* as, Assembler::Register target) {
185   EmitDispatch(as, target);
186 }
187 
188 }  // namespace x86_32
189 
GenTrampolineAdaptor(MachineCode * mc,GuestAddr pc,HostCode marshall,const void * callee,const char * name)190 void GenTrampolineAdaptor(MachineCode* mc,
191                           GuestAddr pc,
192                           HostCode marshall,
193                           const void* callee,
194                           const char* name) {
195   x86_32::GenTrampolineAdaptor(mc, pc, marshall, callee, name);
196 }
197 
198 }  // namespace berberis
199