Print this page
saveargs: let disasm do the lifting
3544 save-args matcher could be considerably more robust
3545 save-args matcher should accept saves maybe out-of-order
Reviewed by: Joshua M. Clulow <josh@sysmgr.org>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/common/saveargs/saveargs.c
+++ new/usr/src/common/saveargs/saveargs.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 */
25 25
26 26
27 27 /*
28 28 * The Sun Studio and GCC (patched for opensolaris/illumos) compilers
29 29 * implement a argument saving scheme on amd64 via the -Wu,save-args or
30 30 * options. When the option is specified, INTEGER type function arguments
31 31 * passed via registers will be saved on the stack immediately after %rbp, and
32 32 * will not be modified through out the life of the routine.
33 33 *
34 34 * +--------+
35 35 * %rbp --> | %rbp |
36 36 * +--------+
37 37 * -0x8(%rbp) | %rdi |
38 38 * +--------+
39 39 * -0x10(%rbp) | %rsi |
40 40 * +--------+
41 41 * -0x18(%rbp) | %rdx |
42 42 * +--------+
43 43 * -0x20(%rbp) | %rcx |
44 44 * +--------+
45 45 * -0x28(%rbp) | %r8 |
46 46 * +--------+
47 47 * -0x30(%rbp) | %r9 |
48 48 * +--------+
49 49 *
50 50 *
51 51 * For example, for the following function,
52 52 *
53 53 * void
54 54 * foo(int a1, int a2, int a3, int a4, int a5, int a6, int a7)
55 55 * {
56 56 * ...
57 57 * }
58 58 *
59 59 * Disassembled code will look something like the following:
60 60 *
61 61 * pushq %rbp
62 62 * movq %rsp, %rbp
63 63 * subq $imm8, %rsp **
64 64 * movq %rdi, -0x8(%rbp)
65 65 * movq %rsi, -0x10(%rbp)
66 66 * movq %rdx, -0x18(%rbp)
67 67 * movq %rcx, -0x20(%rbp)
68 68 * movq %r8, -0x28(%rbp)
69 69 * movq %r9, -0x30(%rbp)
70 70 * ...
71 71 * or
72 72 * pushq %rbp
73 73 * movq %rsp, %rbp
74 74 * subq $imm8, %rsp **
75 75 * movq %r9, -0x30(%rbp)
76 76 * movq %r8, -0x28(%rbp)
77 77 * movq %rcx, -0x20(%rbp)
78 78 * movq %rdx, -0x18(%rbp)
79 79 * movq %rsi, -0x10(%rbp)
80 80 * movq %rdi, -0x8(%rbp)
81 81 * ...
82 82 * or
83 83 * pushq %rbp
84 84 * movq %rsp, %rbp
85 85 * pushq %rdi
↓ open down ↓ |
85 lines elided |
↑ open up ↑ |
86 86 * pushq %rsi
87 87 * pushq %rdx
88 88 * pushq %rcx
89 89 * pushq %r8
90 90 * pushq %r9
91 91 *
92 92 * **: The space being reserved is in addition to what the current
93 93 * function prolog already reserves.
94 94 *
95 95 * We loop through the first SAVEARGS_INSN_SEQ_LEN bytes of the function
96 - * looking for each argument saving instruction we would expect to see. We
97 - * loop byte-by-byte, rather than doing anything smart about insn lengths,
98 - * only deviating from this when we know we have our insn, and can skip the
99 - * rest of it.
96 + * looking for each argument saving instruction we would expect to see.
100 97 *
101 98 * If there are odd number of arguments to a function, additional space is
102 99 * reserved on the stack to maintain 16-byte alignment. For example,
103 100 *
104 101 * argc == 0: no argument saving.
105 102 * argc == 3: save 3, but space for 4 is reserved
106 103 * argc == 7: save 6.
107 104 */
108 105
109 106 #include <sys/sysmacros.h>
110 107 #include <sys/types.h>
108 +#include <libdisasm.h>
109 +#include <string.h>
110 +
111 111 #include <saveargs.h>
112 112
113 113 /*
114 114 * Size of the instruction sequence arrays. It should correspond to
115 115 * the maximum number of arguments passed via registers.
116 116 */
117 117 #define INSTR_ARRAY_SIZE 6
118 118
119 119 #define INSTR1(ins, off) (ins[(off)])
120 120 #define INSTR2(ins, off) (ins[(off)] + (ins[(off) + 1] << 8))
121 121 #define INSTR3(ins, off) \
122 122 (ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16))
123 123 #define INSTR4(ins, off) \
124 124 (ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16) + \
125 125 (ins[(off) + 3] << 24))
126 126
127 127 /*
128 128 * Sun Studio 10 patch implementation saves %rdi first;
129 129 * GCC 3.4.3 Sun branch implementation saves them in reverse order.
130 130 */
131 131 static const uint32_t save_instr[INSTR_ARRAY_SIZE] = {
132 132 0xf87d8948, /* movq %rdi, -0x8(%rbp) */
133 133 0xf0758948, /* movq %rsi, -0x10(%rbp) */
134 134 0xe8558948, /* movq %rdx, -0x18(%rbp) */
135 135 0xe04d8948, /* movq %rcx, -0x20(%rbp) */
136 136 0xd845894c, /* movq %r8, -0x28(%rbp) */
137 137 0xd04d894c /* movq %r9, -0x30(%rbp) */
138 138 };
139 139
140 140 static const uint16_t save_instr_push[] = {
141 141 0x57, /* pushq %rdi */
142 142 0x56, /* pushq %rsi */
143 143 0x52, /* pushq %rdx */
144 144 0x51, /* pushq %rcx */
145 145 0x5041, /* pushq %r8 */
146 146 0x5141 /* pushq %r9 */
147 147 };
148 148
149 149 /*
150 150 * If the return type of a function is a structure greater than 16 bytes in
151 151 * size, %rdi will contain the address to which it should be stored, and
152 152 * arguments will begin at %rsi. Studio will push all of the normal argument
153 153 * registers anyway, GCC will start pushing at %rsi, so we need a separate
154 154 * pattern.
↓ open down ↓ |
34 lines elided |
↑ open up ↑ |
155 155 */
156 156 static const uint32_t save_instr_sr[INSTR_ARRAY_SIZE-1] = {
157 157 0xf8758948, /* movq %rsi,-0x8(%rbp) */
158 158 0xf0558948, /* movq %rdx,-0x10(%rbp) */
159 159 0xe84d8948, /* movq %rcx,-0x18(%rbp) */
160 160 0xe045894c, /* movq %r8,-0x20(%rbp) */
161 161 0xd84d894c /* movq %r9,-0x28(%rbp) */
162 162 };
163 163
164 164 static const uint8_t save_fp_pushes[] = {
165 - 0x55, /* pushq %rbp */
166 - 0xcc /* int $0x3 */
165 + 0x55, /* pushq %rbp */
166 + 0xcc /* int $0x3 */
167 167 };
168 168 #define NUM_FP_PUSHES (sizeof (save_fp_pushes) / sizeof (save_fp_pushes[0]))
169 169
170 170 static const uint32_t save_fp_movs[] = {
171 171 0x00e58948, /* movq %rsp,%rbp, encoding 1 */
172 172 0x00ec8b48, /* movq %rsp,%rbp, encoding 2 */
173 173 };
174 174 #define NUM_FP_MOVS (sizeof (save_fp_movs) / sizeof (save_fp_movs[0]))
175 175
176 +typedef struct {
177 + uint8_t *data;
178 + size_t size;
179 +} text_t;
180 +
181 +static int
182 +do_read(void *data, uint64_t addr, void *buf, size_t len)
183 +{
184 + text_t *t = data;
185 +
186 + if (addr > t->size)
187 + return (-1);
188 +
189 + len = MIN(len, t->size - addr);
190 +
191 + (void) memcpy(buf, (char *)t->data + addr, len);
192 +
193 + return (len);
194 +}
195 +
196 +/* ARGSUSED */
197 +int
198 +do_lookup(void *data, uint64_t addr, char *buf, size_t buflen, uint64_t *start,
199 + size_t *symlen)
200 +{
201 + /* We don't actually need lookup info */
202 + return (-1);
203 +}
204 +
176 205 static int
177 -has_saved_fp(uint8_t *ins, int size)
206 +instr_size(dis_handle_t *dhp, uint8_t *ins, unsigned int i, size_t size)
207 +{
208 + text_t t;
209 +
210 + t.data = ins;
211 + t.size = size;
212 +
213 + dis_set_data(dhp, &t);
214 + return (dis_instrlen(dhp, i));
215 +}
216 +
217 +static boolean_t
218 +has_saved_fp(dis_handle_t *dhp, uint8_t *ins, int size)
178 219 {
179 - int i, j;
180 - uint32_t n;
181 - int found_push = 0;
220 + int i, j;
221 + uint32_t n;
222 + boolean_t found_push = B_FALSE;
223 + int sz = 0;
224 +
225 + for (i = 0; i < size; i += sz) {
226 + if ((sz = instr_size(dhp, ins, i, size)) == -1)
227 + return (B_FALSE);
228 +
229 + if (found_push == B_FALSE) {
230 + if (sz != 1)
231 + continue;
182 232
183 - for (i = 0; i < size; i++) {
184 - if (found_push == 0) {
185 233 n = INSTR1(ins, i);
186 234 for (j = 0; j <= NUM_FP_PUSHES; j++)
187 235 if (save_fp_pushes[j] == n) {
188 - found_push = 1;
236 + found_push = B_TRUE;
189 237 break;
190 238 }
191 239 } else {
240 + if (sz != 3)
241 + continue;
192 242 n = INSTR3(ins, i);
193 243 for (j = 0; j <= NUM_FP_MOVS; j++)
194 244 if (save_fp_movs[j] == n)
195 - return (1);
245 + return (B_TRUE);
196 246 }
197 247 }
198 248
199 - return (0);
249 + return (B_FALSE);
200 250 }
201 251
202 252 int
203 253 saveargs_has_args(uint8_t *ins, size_t size, uint_t argc, int start_index)
204 254 {
205 255 int i, j;
206 256 uint32_t n;
257 + uint8_t found = 0;
258 + size_t sz = 0;
259 + dis_handle_t *dhp = NULL;
260 + int ret = SAVEARGS_NO_ARGS;
207 261
208 262 argc = MIN((start_index + argc), INSTR_ARRAY_SIZE);
209 263
210 - if (!has_saved_fp(ins, size))
264 + if ((dhp = dis_handle_create(DIS_X86_SIZE64, NULL, do_lookup,
265 + do_read)) == NULL)
211 266 return (SAVEARGS_NO_ARGS);
212 267
268 + if (!has_saved_fp(dhp, ins, size)) {
269 + dis_handle_destroy(dhp);
270 + return (SAVEARGS_NO_ARGS);
271 + }
272 +
213 273 /*
214 - * Compare against Sun Studio implementation
274 + * For each possible style of argument saving, walk the insn stream as
275 + * we've been given it, and set bit N in 'found' if we find the
276 + * instruction saving the Nth argument.
215 277 */
216 - for (i = 4, j = 0; i <= size - 4; i++) {
217 - n = INSTR4(ins, i);
218 -
219 - if (n == save_instr[j]) {
220 - i += 3;
221 - if (++j >= argc)
222 - return (start_index ? SAVEARGS_STRUCT_ARGS :
223 - SAVEARGS_TRAD_ARGS);
224 - }
225 - }
226 278
227 279 /*
228 - * Compare against GCC implementation
280 + * Compare against regular implementation
229 281 */
230 - for (i = 4, j = argc - 1; i <= size - 4; i++) {
282 + found = 0;
283 + for (i = 0; i < size; i += sz) {
284 + sz = instr_size(dhp, ins, i, size);
285 +
286 + if (sz == -1)
287 + break;
288 + else if (sz != 4)
289 + continue;
290 +
231 291 n = INSTR4(ins, i);
232 292
233 - if (n == save_instr[j]) {
234 - i += 3;
235 - if (--j < start_index)
236 - return (SAVEARGS_TRAD_ARGS);
293 + for (j = 0; j < argc; j++) {
294 + if (n == save_instr[j]) {
295 + found |= (1 << j);
296 +
297 + if (found == ((1 << argc) - 1)) {
298 + ret = start_index ?
299 + SAVEARGS_STRUCT_ARGS :
300 + SAVEARGS_TRAD_ARGS;
301 + goto done;
302 + }
303 +
304 + break;
305 + }
237 306 }
238 307 }
239 308
240 309 /*
241 310 * Compare against GCC push-based implementation
242 311 */
243 - for (i = 4, j = start_index; i <= size - 2; i += 1) {
244 - n = (i >= (8 - start_index)) ? INSTR2(ins, i) : INSTR1(ins, i);
312 + found = 0;
313 + for (i = 0; i < size; i += sz) {
314 + if ((sz = instr_size(dhp, ins, i, size)) == -1)
315 + break;
316 +
317 + for (j = start_index; j < argc; j++) {
318 + if (sz == 2) /* Two byte */
319 + n = INSTR2(ins, i);
320 + else if (sz == 1)
321 + n = INSTR1(ins, i);
322 + else
323 + continue;
324 +
325 + if (n == save_instr_push[j]) {
326 + found |= (1 << (j - start_index));
327 +
328 + if (found == ((1 << (argc - start_index)) - 1)) {
329 + ret = SAVEARGS_TRAD_ARGS;
330 + goto done;
331 + }
245 332
246 - if (n == save_instr_push[j]) {
247 - if (i >= (8 - start_index))
248 - i += 1;
249 - if (++j >= argc)
250 - return (SAVEARGS_TRAD_ARGS);
333 + break;
334 + }
251 335 }
252 336 }
253 337
254 - /* Look for a GCC-style returned structure */
338 + /*
339 + * Look for a GCC-style returned structure.
340 + */
341 + found = 0;
255 342 if (start_index != 0) {
256 - for (i = 4, j = argc - 2; i <= size - 4; i++) {
343 + for (i = 0; i < size; i += sz) {
344 + sz = instr_size(dhp, ins, i, size);
345 +
346 + if (sz == -1)
347 + break;
348 + else if (sz != 4)
349 + continue;
350 +
257 351 n = INSTR4(ins, i);
258 352
259 - if (n == save_instr_sr[j]) {
260 - i += 3;
261 - if (--j >= (argc - 1))
262 - return (SAVEARGS_TRAD_ARGS);
353 + /* argc is inclusive of start_index, allow for that */
354 + for (j = 0; j < (argc - start_index); j++) {
355 + if (n == save_instr_sr[j]) {
356 + found |= (1 << j);
357 +
358 + if (found ==
359 + ((1 << (argc - start_index)) - 1)) {
360 + ret = SAVEARGS_TRAD_ARGS;
361 + goto done;
362 + }
363 +
364 + break;
365 + }
263 366 }
264 367 }
265 368 }
266 369
267 - return (SAVEARGS_NO_ARGS);
370 +done:
371 + dis_handle_destroy(dhp);
372 + return (ret);
268 373 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX