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