1 /*
2  * Copyright (C) 2019 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/gen_wrapper.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/logging.h"
23 #include "berberis/guest_abi/guest_arguments.h"
24 #include "berberis/guest_abi/guest_call.h"
25 #include "berberis/guest_state/guest_addr.h"
26 #include "berberis/runtime_primitives/host_code.h"
27 
28 namespace berberis {
29 
30 using x86_32::Assembler;
31 
GenWrapGuestFunction(MachineCode * mc,GuestAddr pc,const char * signature,HostCode guest_runner,const char * name)32 void GenWrapGuestFunction(MachineCode* mc,
33                           GuestAddr pc,
34                           const char* signature,
35                           HostCode guest_runner,
36                           const char* name) {
37   UNUSED(name);
38 
39   // Stack frame
40   // -----------
41   // esp, aligned on 16             -> [argument 0: pc]
42   //                                   [argument 1: guest argument buffer addr]
43   // aligned on 4                   -> [guest argument buffer]
44   //                                   [...]
45   // esp after prologue             -> [saved ebp]
46   // esp after call                 -> [return addr]
47   // esp before call, aligned on 16 -> [parameter 0]
48   //                                   [...]
49 
50   Assembler as(mc);
51 
52   // On function entry, esp + 4 is a multiple of 16.
53   // Right before next function call, esp is a multiple of 16.
54 
55   // Default prologue.
56   as.Push(Assembler::ebp);
57   as.Movl(Assembler::ebp, Assembler::esp);
58 
59   static_assert(alignof(GuestArgumentBuffer) <= 4, "unexpected GuestArgumentBuffer alignment");
60 
61   // Estimate guest argument buffer size.
62   // Each argument can be 2 4-bytes at most. Result can be 2 4-bytes at most.
63   // First 4-byte is in the GuestArgumentBuffer.
64   // TODO(eaeltsin): maybe run parameter passing to calculate exactly?
65   size_t max_argv_size = strlen(signature) * 8;
66   size_t guest_argument_buffer_size = sizeof(GuestArgumentBuffer) - 4 + max_argv_size;
67 
68   // Stack frame size is guest argument buffer + 2 4-bytes for guest runner arguments.
69   size_t frame_size = guest_argument_buffer_size + 8;
70 
71   // Curr esp + 8 is a multiple of 16.
72   // New esp is a multiple of 16.
73   size_t aligned_frame_size = AlignUp(frame_size + 8, 16) - 8;
74 
75   // Allocate stack frame.
76   as.Subl(Assembler::esp, aligned_frame_size);
77 
78   constexpr int kArgcOffset = 8 + offsetof(GuestArgumentBuffer, argc);
79   constexpr int kRescOffset = 8 + offsetof(GuestArgumentBuffer, resc);
80   constexpr int kArgvOffset = 8 + offsetof(GuestArgumentBuffer, argv);
81 
82   const int params_offset = aligned_frame_size + 8;
83 
84   // Convert parameters and set argc.
85   int host_argc = 0;
86   int argc = 0;
87   for (size_t i = 1; signature[i] != '\0'; ++i) {
88     if (signature[i] == 'z' || signature[i] == 'b' || signature[i] == 's' || signature[i] == 'c' ||
89         signature[i] == 'i' || signature[i] == 'p' || signature[i] == 'f') {
90       as.Movl(Assembler::eax, {.base = Assembler::esp, .disp = params_offset + 4 * host_argc});
91       ++host_argc;
92       as.Movl({.base = Assembler::esp, .disp = kArgvOffset + 4 * argc}, Assembler::eax);
93       ++argc;
94     } else if (signature[i] == 'l' || signature[i] == 'd') {
95       as.Movl(Assembler::eax, {.base = Assembler::esp, .disp = params_offset + 4 * host_argc});
96       as.Movl(Assembler::edx, {.base = Assembler::esp, .disp = params_offset + 4 * host_argc + 4});
97       host_argc += 2;
98       argc = AlignUp(argc, 2);
99       as.Movl({.base = Assembler::esp, .disp = kArgvOffset + 4 * argc}, Assembler::eax);
100       as.Movl({.base = Assembler::esp, .disp = kArgvOffset + 4 * argc + 4}, Assembler::edx);
101       argc += 2;
102     } else {
103       FATAL("signature char '%c' not supported", signature[i]);
104     }
105   }
106   as.Movl({.base = Assembler::esp, .disp = kArgcOffset}, argc);
107 
108   // Set resc.
109   if (signature[0] == 'z' || signature[0] == 'b' || signature[0] == 's' ||
110       signature[0] == 'c' | signature[0] == 'i' || signature[0] == 'p' || signature[0] == 'f') {
111     as.Movl({.base = Assembler::esp, .disp = kRescOffset}, 1);
112   } else if (signature[0] == 'l' || signature[0] == 'd') {
113     as.Movl({.base = Assembler::esp, .disp = kRescOffset}, 2);
114   } else {
115     CHECK_EQ('v', signature[0]);
116     as.Movl({.base = Assembler::esp, .disp = kRescOffset}, 0);
117   }
118 
119   // Call guest runner.
120   as.Movl({.base = Assembler::esp, .disp = 0}, pc);
121   as.Leal(Assembler::eax, {.base = Assembler::esp, .disp = 8});
122   as.Movl({.base = Assembler::esp, .disp = 4}, Assembler::eax);
123   as.Call(guest_runner);
124 
125   // Get the result.
126   if (signature[0] == 'z' || signature[0] == 'b' || signature[0] == 's' ||
127       signature[0] == 'c' | signature[0] == 'i' || signature[0] == 'p') {
128     as.Movl(Assembler::eax, {.base = Assembler::esp, .disp = kArgvOffset});
129   } else if (signature[0] == 'l') {
130     as.Movl(Assembler::eax, {.base = Assembler::esp, .disp = kArgvOffset});
131     as.Movl(Assembler::edx, {.base = Assembler::esp, .disp = kArgvOffset + 4});
132   } else if (signature[0] == 'f') {
133     as.Flds({.base = Assembler::esp, .disp = kArgvOffset});
134   } else if (signature[0] == 'd') {
135     as.Fldl({.base = Assembler::esp, .disp = kArgvOffset});
136   }
137 
138   // Free stack frame.
139   as.Addl(Assembler::esp, aligned_frame_size);
140 
141   // Default epilogue.
142   as.Pop(Assembler::ebp);
143   as.Ret();
144 
145   as.Finalize();
146 }
147 
148 }  // namespace berberis
149