1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2003-2016. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20
21
22 #include <stddef.h> /* offsetof() */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include "global.h"
27
28 #include "hipe_arch.h"
29 #include "hipe_native_bif.h" /* nbif_callemu() */
30
31 #undef F_TIMO
32 #undef THE_NON_VALUE
33 #undef ERL_FUN_SIZE
34 #include "hipe_literals.h"
35
hipe_patch_load_fe(Uint32 * address,Uint32 value)36 void hipe_patch_load_fe(Uint32 *address, Uint32 value)
37 {
38 /* address points to a disp32 or imm32 operand */
39 *address += value;
40 }
41
hipe_patch_insn(void * address,Uint32 value,Eterm type)42 int hipe_patch_insn(void *address, Uint32 value, Eterm type)
43 {
44 switch (type) {
45 case am_closure:
46 case am_constant:
47 case am_atom:
48 case am_c_const:
49 break;
50 case am_x86_abs_pcrel:
51 value += (Uint)address;
52 break;
53 default:
54 return -1;
55 }
56 *(Uint32*)address += value;
57 return 0;
58 }
59
hipe_patch_call(void * callAddress,void * destAddress,void * trampoline)60 int hipe_patch_call(void *callAddress, void *destAddress, void *trampoline)
61 {
62 Uint rel32;
63
64 ASSERT(trampoline == NULL);
65
66 rel32 = (Uint)destAddress - (Uint)callAddress - 4;
67 *(Uint32*)callAddress = rel32;
68 hipe_flush_icache_word(callAddress);
69 return 0;
70 }
71
alloc_code(unsigned int alloc_bytes)72 static void *alloc_code(unsigned int alloc_bytes)
73 {
74 return erts_alloc(ERTS_ALC_T_HIPE_EXEC, alloc_bytes);
75 }
76
hipe_alloc_code(Uint nrbytes,Eterm callees,Eterm * trampolines,Process * p)77 void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process *p)
78 {
79 if (is_not_nil(callees))
80 return NULL;
81 *trampolines = NIL;
82 return alloc_code(nrbytes);
83 }
84
hipe_free_code(void * code,unsigned int bytes)85 void hipe_free_code(void* code, unsigned int bytes)
86 {
87 erts_free(ERTS_ALC_T_HIPE_EXEC, code);
88 }
89
hipe_make_native_stub(void * callee_exp,unsigned int beamArity)90 void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity)
91 {
92 /*
93 * This creates a native code stub with the following contents:
94 *
95 * movl $Address, P_CALLEE_EXP(%ebp)
96 * movb $Arity, P_ARITY(%ebp)
97 * jmp callemu
98 *
99 * The stub has variable size, depending on whether the P_CALLEE_EXP
100 * and P_ARITY offsets fit in 8-bit signed displacements or not.
101 * The rel32 offset in the final jmp depends on its actual location,
102 * which also depends on the size of the previous instructions.
103 * Arity is stored with a movb because (a) Bj�rn tells me arities
104 * are <= 255, and (b) a movb is smaller and faster than a movl.
105 */
106 unsigned int codeSize;
107 unsigned char *code, *codep;
108 unsigned int callEmuOffset;
109
110 codeSize = /* 16, 19, or 22 bytes */
111 16 + /* 16 when both offsets are 8-bit */
112 (P_CALLEE_EXP >= 128 ? 3 : 0) +
113 (P_ARITY >= 128 ? 3 : 0);
114 codep = code = alloc_code(codeSize);
115 if (!code)
116 return NULL;
117
118 /* movl $beamAddress, P_CALLEE_EXP(%ebp); 3 or 6 bytes, plus 4 */
119 codep[0] = 0xc7;
120 #if P_CALLEE_EXP >= 128
121 codep[1] = 0x85; /* disp32[EBP] */
122 codep[2] = P_CALLEE_EXP & 0xFF;
123 codep[3] = (P_CALLEE_EXP >> 8) & 0xFF;
124 codep[4] = (P_CALLEE_EXP >> 16) & 0xFF;
125 codep[5] = (P_CALLEE_EXP >> 24) & 0xFF;
126 codep += 6;
127 #else
128 codep[1] = 0x45; /* disp8[EBP] */
129 codep[2] = P_CALLEE_EXP;
130 codep += 3;
131 #endif
132 codep[0] = ((unsigned int)callee_exp) & 0xFF;
133 codep[1] = ((unsigned int)callee_exp >> 8) & 0xFF;
134 codep[2] = ((unsigned int)callee_exp >> 16) & 0xFF;
135 codep[3] = ((unsigned int)callee_exp >> 24) & 0xFF;
136 codep += 4;
137
138 /* movb $beamArity, P_ARITY(%ebp); 3 or 6 bytes */
139 codep[0] = 0xc6;
140 #if P_ARITY >= 128
141 codep[1] = 0x85; /* disp32[EBP] */
142 codep[2] = P_ARITY & 0xFF;
143 codep[3] = (P_ARITY >> 8) & 0xFF;
144 codep[4] = (P_ARITY >> 16) & 0xFF;
145 codep[5] = (P_ARITY >> 24) & 0xFF;
146 codep += 6;
147 #else
148 codep[1] = 0x45; /* disp8[EBP] */
149 codep[2] = P_ARITY;
150 codep += 3;
151 #endif
152 codep[0] = beamArity;
153 codep += 1;
154
155 /* jmp callemu; 5 bytes */
156 callEmuOffset = (unsigned char*)nbif_callemu - (code + codeSize);
157 codep[0] = 0xe9;
158 codep[1] = callEmuOffset & 0xFF;
159 codep[2] = (callEmuOffset >> 8) & 0xFF;
160 codep[3] = (callEmuOffset >> 16) & 0xFF;
161 codep[4] = (callEmuOffset >> 24) & 0xFF;
162 codep += 5;
163 ASSERT(codep == code + codeSize);
164
165 /* I-cache flush? */
166
167 return code;
168 }
169
hipe_free_native_stub(void * stub)170 void hipe_free_native_stub(void* stub)
171 {
172 erts_free(ERTS_ALC_T_HIPE_EXEC, stub);
173 }
174
hipe_arch_print_pcb(struct hipe_process_state * p)175 void hipe_arch_print_pcb(struct hipe_process_state *p)
176 {
177 #define U(n,x) \
178 printf(" % 4d | %s | 0x%08x | |\r\n", (int)offsetof(struct hipe_process_state,x), n, (unsigned)p->x)
179 U("ncsp ", ncsp);
180 U("narity ", narity);
181 #undef U
182 }
183