76 * movq %r8, -0x28(%rbp)
77 * movq %rcx, -0x20(%rbp)
78 * movq %rdx, -0x18(%rbp)
79 * movq %rsi, -0x10(%rbp)
80 * movq %rdi, -0x8(%rbp)
81 * ...
82 * or
83 * pushq %rbp
84 * movq %rsp, %rbp
85 * pushq %rdi
86 * pushq %rsi
87 * pushq %rdx
88 * pushq %rcx
89 * pushq %r8
90 * pushq %r9
91 *
92 * **: The space being reserved is in addition to what the current
93 * function prolog already reserves.
94 *
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.
100 *
101 * If there are odd number of arguments to a function, additional space is
102 * reserved on the stack to maintain 16-byte alignment. For example,
103 *
104 * argc == 0: no argument saving.
105 * argc == 3: save 3, but space for 4 is reserved
106 * argc == 7: save 6.
107 */
108
109 #include <sys/sysmacros.h>
110 #include <sys/types.h>
111 #include <saveargs.h>
112
113 /*
114 * Size of the instruction sequence arrays. It should correspond to
115 * the maximum number of arguments passed via registers.
116 */
117 #define INSTR_ARRAY_SIZE 6
118
119 #define INSTR1(ins, off) (ins[(off)])
120 #define INSTR2(ins, off) (ins[(off)] + (ins[(off) + 1] << 8))
121 #define INSTR3(ins, off) \
122 (ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16))
123 #define INSTR4(ins, off) \
124 (ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16) + \
125 (ins[(off) + 3] << 24))
126
127 /*
128 * Sun Studio 10 patch implementation saves %rdi first;
129 * GCC 3.4.3 Sun branch implementation saves them in reverse order.
130 */
156 static const uint32_t save_instr_sr[INSTR_ARRAY_SIZE-1] = {
157 0xf8758948, /* movq %rsi,-0x8(%rbp) */
158 0xf0558948, /* movq %rdx,-0x10(%rbp) */
159 0xe84d8948, /* movq %rcx,-0x18(%rbp) */
160 0xe045894c, /* movq %r8,-0x20(%rbp) */
161 0xd84d894c /* movq %r9,-0x28(%rbp) */
162 };
163
164 static const uint8_t save_fp_pushes[] = {
165 0x55, /* pushq %rbp */
166 0xcc /* int $0x3 */
167 };
168 #define NUM_FP_PUSHES (sizeof (save_fp_pushes) / sizeof (save_fp_pushes[0]))
169
170 static const uint32_t save_fp_movs[] = {
171 0x00e58948, /* movq %rsp,%rbp, encoding 1 */
172 0x00ec8b48, /* movq %rsp,%rbp, encoding 2 */
173 };
174 #define NUM_FP_MOVS (sizeof (save_fp_movs) / sizeof (save_fp_movs[0]))
175
176 static int
177 has_saved_fp(uint8_t *ins, int size)
178 {
179 int i, j;
180 uint32_t n;
181 int found_push = 0;
182
183 for (i = 0; i < size; i++) {
184 if (found_push == 0) {
185 n = INSTR1(ins, i);
186 for (j = 0; j <= NUM_FP_PUSHES; j++)
187 if (save_fp_pushes[j] == n) {
188 found_push = 1;
189 break;
190 }
191 } else {
192 n = INSTR3(ins, i);
193 for (j = 0; j <= NUM_FP_MOVS; j++)
194 if (save_fp_movs[j] == n)
195 return (1);
196 }
197 }
198
199 return (0);
200 }
201
202 int
203 saveargs_has_args(uint8_t *ins, size_t size, uint_t argc, int start_index)
204 {
205 int i, j;
206 uint32_t n;
207
208 argc = MIN((start_index + argc), INSTR_ARRAY_SIZE);
209
210 if (!has_saved_fp(ins, size))
211 return (SAVEARGS_NO_ARGS);
212
213 /*
214 * Compare against Sun Studio implementation
215 */
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
227 /*
228 * Compare against GCC implementation
229 */
230 for (i = 4, j = argc - 1; i <= size - 4; i++) {
231 n = INSTR4(ins, i);
232
233 if (n == save_instr[j]) {
234 i += 3;
235 if (--j < start_index)
236 return (SAVEARGS_TRAD_ARGS);
237 }
238 }
239
240 /*
241 * Compare against GCC push-based implementation
242 */
243 for (i = 4, j = start_index; i <= size - 2; i += 1) {
244 n = (i >= (8 - start_index)) ? INSTR2(ins, i) : INSTR1(ins, i);
245
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);
251 }
252 }
253
254 /* Look for a GCC-style returned structure */
255 if (start_index != 0) {
256 for (i = 4, j = argc - 2; i <= size - 4; i++) {
257 n = INSTR4(ins, i);
258
259 if (n == save_instr_sr[j]) {
260 i += 3;
261 if (--j >= (argc - 1))
262 return (SAVEARGS_TRAD_ARGS);
263 }
264 }
265 }
266
267 return (SAVEARGS_NO_ARGS);
268 }
|
76 * movq %r8, -0x28(%rbp)
77 * movq %rcx, -0x20(%rbp)
78 * movq %rdx, -0x18(%rbp)
79 * movq %rsi, -0x10(%rbp)
80 * movq %rdi, -0x8(%rbp)
81 * ...
82 * or
83 * pushq %rbp
84 * movq %rsp, %rbp
85 * pushq %rdi
86 * pushq %rsi
87 * pushq %rdx
88 * pushq %rcx
89 * pushq %r8
90 * pushq %r9
91 *
92 * **: The space being reserved is in addition to what the current
93 * function prolog already reserves.
94 *
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.
97 *
98 * If there are odd number of arguments to a function, additional space is
99 * reserved on the stack to maintain 16-byte alignment. For example,
100 *
101 * argc == 0: no argument saving.
102 * argc == 3: save 3, but space for 4 is reserved
103 * argc == 7: save 6.
104 */
105
106 #include <sys/sysmacros.h>
107 #include <sys/types.h>
108 #include <libdisasm.h>
109 #include <string.h>
110
111 #include <saveargs.h>
112
113 /*
114 * Size of the instruction sequence arrays. It should correspond to
115 * the maximum number of arguments passed via registers.
116 */
117 #define INSTR_ARRAY_SIZE 6
118
119 #define INSTR1(ins, off) (ins[(off)])
120 #define INSTR2(ins, off) (ins[(off)] + (ins[(off) + 1] << 8))
121 #define INSTR3(ins, off) \
122 (ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16))
123 #define INSTR4(ins, off) \
124 (ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16) + \
125 (ins[(off) + 3] << 24))
126
127 /*
128 * Sun Studio 10 patch implementation saves %rdi first;
129 * GCC 3.4.3 Sun branch implementation saves them in reverse order.
130 */
156 static const uint32_t save_instr_sr[INSTR_ARRAY_SIZE-1] = {
157 0xf8758948, /* movq %rsi,-0x8(%rbp) */
158 0xf0558948, /* movq %rdx,-0x10(%rbp) */
159 0xe84d8948, /* movq %rcx,-0x18(%rbp) */
160 0xe045894c, /* movq %r8,-0x20(%rbp) */
161 0xd84d894c /* movq %r9,-0x28(%rbp) */
162 };
163
164 static const uint8_t save_fp_pushes[] = {
165 0x55, /* pushq %rbp */
166 0xcc /* int $0x3 */
167 };
168 #define NUM_FP_PUSHES (sizeof (save_fp_pushes) / sizeof (save_fp_pushes[0]))
169
170 static const uint32_t save_fp_movs[] = {
171 0x00e58948, /* movq %rsp,%rbp, encoding 1 */
172 0x00ec8b48, /* movq %rsp,%rbp, encoding 2 */
173 };
174 #define NUM_FP_MOVS (sizeof (save_fp_movs) / sizeof (save_fp_movs[0]))
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
205 static int
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)
219 {
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;
232
233 n = INSTR1(ins, i);
234 for (j = 0; j <= NUM_FP_PUSHES; j++)
235 if (save_fp_pushes[j] == n) {
236 found_push = B_TRUE;
237 break;
238 }
239 } else {
240 if (sz != 3)
241 continue;
242 n = INSTR3(ins, i);
243 for (j = 0; j <= NUM_FP_MOVS; j++)
244 if (save_fp_movs[j] == n)
245 return (B_TRUE);
246 }
247 }
248
249 return (B_FALSE);
250 }
251
252 int
253 saveargs_has_args(uint8_t *ins, size_t size, uint_t argc, int start_index)
254 {
255 int i, j;
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;
261
262 argc = MIN((start_index + argc), INSTR_ARRAY_SIZE);
263
264 if ((dhp = dis_handle_create(DIS_X86_SIZE64, NULL, do_lookup,
265 do_read)) == NULL)
266 return (SAVEARGS_NO_ARGS);
267
268 if (!has_saved_fp(dhp, ins, size)) {
269 dis_handle_destroy(dhp);
270 return (SAVEARGS_NO_ARGS);
271 }
272
273 /*
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.
277 */
278
279 /*
280 * Compare against regular implementation
281 */
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
291 n = INSTR4(ins, i);
292
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 }
306 }
307 }
308
309 /*
310 * Compare against GCC push-based implementation
311 */
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 ==
329 ((1 << (argc - start_index)) - 1)) {
330 ret = SAVEARGS_TRAD_ARGS;
331 goto done;
332 }
333
334 break;
335 }
336 }
337 }
338
339 /*
340 * Look for a GCC-style returned structure.
341 */
342 found = 0;
343 if (start_index != 0) {
344 for (i = 0; i < size; i += sz) {
345 sz = instr_size(dhp, ins, i, size);
346
347 if (sz == -1)
348 break;
349 else if (sz != 4)
350 continue;
351
352 n = INSTR4(ins, i);
353
354 /* argc is inclusive of start_index, allow for that */
355 for (j = 0; j < (argc - start_index); j++) {
356 if (n == save_instr_sr[j]) {
357 found |= (1 << j);
358
359 if (found ==
360 ((1 << (argc - start_index)) - 1)) {
361 ret = SAVEARGS_TRAD_ARGS;
362 goto done;
363 }
364
365 break;
366 }
367 }
368 }
369 }
370
371 done:
372 dis_handle_destroy(dhp);
373 return (ret);
374 }
|