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