Print this page
3544 save-args matcher could be considerably more robust
3545 save-args matcher should accept saves may be out-of-order
Reviewed by: Joshua M. Clulow <josh@sysmgr.org>
Reviewed by: Robert Mustacchi <rm@joyent.com>

*** 91,104 **** * * **: The space being reserved is in addition to what the current * function prolog already reserves. * * We loop through the first SAVEARGS_INSN_SEQ_LEN bytes of the function ! * looking for each argument saving instruction we would expect to see. We ! * loop byte-by-byte, rather than doing anything smart about insn lengths, ! * only deviating from this when we know we have our insn, and can skip the ! * rest of it. * * If there are odd number of arguments to a function, additional space is * reserved on the stack to maintain 16-byte alignment. For example, * * argc == 0: no argument saving. --- 91,101 ---- * * **: The space being reserved is in addition to what the current * function prolog already reserves. * * We loop through the first SAVEARGS_INSN_SEQ_LEN bytes of the function ! * looking for each argument saving instruction we would expect to see. * * If there are odd number of arguments to a function, additional space is * reserved on the stack to maintain 16-byte alignment. For example, * * argc == 0: no argument saving.
*** 106,115 **** --- 103,115 ---- * argc == 7: save 6. */ #include <sys/sysmacros.h> #include <sys/types.h> + #include <libdisasm.h> + #include <string.h> + #include <saveargs.h> /* * Size of the instruction sequence arrays. It should correspond to * the maximum number of arguments passed via registers.
*** 171,268 **** 0x00e58948, /* movq %rsp,%rbp, encoding 1 */ 0x00ec8b48, /* movq %rsp,%rbp, encoding 2 */ }; #define NUM_FP_MOVS (sizeof (save_fp_movs) / sizeof (save_fp_movs[0])) static int ! has_saved_fp(uint8_t *ins, int size) { int i, j; uint32_t n; ! int found_push = 0; - for (i = 0; i < size; i++) { - if (found_push == 0) { n = INSTR1(ins, i); for (j = 0; j <= NUM_FP_PUSHES; j++) if (save_fp_pushes[j] == n) { ! found_push = 1; break; } } else { n = INSTR3(ins, i); for (j = 0; j <= NUM_FP_MOVS; j++) if (save_fp_movs[j] == n) ! return (1); } } ! return (0); } int saveargs_has_args(uint8_t *ins, size_t size, uint_t argc, int start_index) { int i, j; uint32_t n; argc = MIN((start_index + argc), INSTR_ARRAY_SIZE); ! if (!has_saved_fp(ins, size)) return (SAVEARGS_NO_ARGS); /* ! * Compare against Sun Studio implementation */ - for (i = 4, j = 0; i <= size - 4; i++) { - n = INSTR4(ins, i); - - if (n == save_instr[j]) { - i += 3; - if (++j >= argc) - return (start_index ? SAVEARGS_STRUCT_ARGS : - SAVEARGS_TRAD_ARGS); - } - } /* ! * Compare against GCC implementation */ ! for (i = 4, j = argc - 1; i <= size - 4; i++) { n = INSTR4(ins, i); if (n == save_instr[j]) { ! i += 3; ! if (--j < start_index) ! return (SAVEARGS_TRAD_ARGS); } } /* * Compare against GCC push-based implementation */ ! for (i = 4, j = start_index; i <= size - 2; i += 1) { ! n = (i >= (8 - start_index)) ? INSTR2(ins, i) : INSTR1(ins, i); if (n == save_instr_push[j]) { ! if (i >= (8 - start_index)) ! i += 1; ! if (++j >= argc) ! return (SAVEARGS_TRAD_ARGS); } } ! /* Look for a GCC-style returned structure */ if (start_index != 0) { ! for (i = 4, j = argc - 2; i <= size - 4; i++) { n = INSTR4(ins, i); if (n == save_instr_sr[j]) { ! i += 3; ! if (--j >= (argc - 1)) ! return (SAVEARGS_TRAD_ARGS); } } } ! return (SAVEARGS_NO_ARGS); } --- 171,374 ---- 0x00e58948, /* movq %rsp,%rbp, encoding 1 */ 0x00ec8b48, /* movq %rsp,%rbp, encoding 2 */ }; #define NUM_FP_MOVS (sizeof (save_fp_movs) / sizeof (save_fp_movs[0])) + typedef struct { + uint8_t *data; + size_t size; + } text_t; + + static int + do_read(void *data, uint64_t addr, void *buf, size_t len) + { + text_t *t = data; + + if (addr >= t->size) + return (-1); + + len = MIN(len, t->size - addr); + + (void) memcpy(buf, (char *)t->data + addr, len); + + return (len); + } + + /* ARGSUSED */ + int + do_lookup(void *data, uint64_t addr, char *buf, size_t buflen, uint64_t *start, + size_t *symlen) + { + /* We don't actually need lookup info */ + return (-1); + } + static int ! instr_size(dis_handle_t *dhp, uint8_t *ins, unsigned int i, size_t size) ! { ! text_t t; ! ! t.data = ins; ! t.size = size; ! ! dis_set_data(dhp, &t); ! return (dis_instrlen(dhp, i)); ! } ! ! static boolean_t ! has_saved_fp(dis_handle_t *dhp, uint8_t *ins, int size) { int i, j; uint32_t n; ! boolean_t found_push = B_FALSE; ! int sz = 0; ! ! for (i = 0; i < size; i += sz) { ! if ((sz = instr_size(dhp, ins, i, size)) < 1) ! return (B_FALSE); ! ! if (found_push == B_FALSE) { ! if (sz != 1) ! continue; n = INSTR1(ins, i); for (j = 0; j <= NUM_FP_PUSHES; j++) if (save_fp_pushes[j] == n) { ! found_push = B_TRUE; break; } } else { + if (sz != 3) + continue; n = INSTR3(ins, i); for (j = 0; j <= NUM_FP_MOVS; j++) if (save_fp_movs[j] == n) ! return (B_TRUE); } } ! return (B_FALSE); } int saveargs_has_args(uint8_t *ins, size_t size, uint_t argc, int start_index) { int i, j; uint32_t n; + uint8_t found = 0; + size_t sz = 0; + dis_handle_t *dhp = NULL; + int ret = SAVEARGS_NO_ARGS; argc = MIN((start_index + argc), INSTR_ARRAY_SIZE); ! if ((dhp = dis_handle_create(DIS_X86_SIZE64, NULL, do_lookup, ! do_read)) == NULL) ! return (SAVEARGS_NO_ARGS); ! ! if (!has_saved_fp(dhp, ins, size)) { ! dis_handle_destroy(dhp); return (SAVEARGS_NO_ARGS); + } /* ! * For each possible style of argument saving, walk the insn stream as ! * we've been given it, and set bit N in 'found' if we find the ! * instruction saving the Nth argument. */ /* ! * Compare against regular implementation */ ! found = 0; ! for (i = 0; i < size; i += sz) { ! sz = instr_size(dhp, ins, i, size); ! ! if (sz < 1) ! break; ! else if (sz != 4) ! continue; ! n = INSTR4(ins, i); + for (j = 0; j < argc; j++) { if (n == save_instr[j]) { ! found |= (1 << j); ! ! if (found == ((1 << argc) - 1)) { ! ret = start_index ? ! SAVEARGS_STRUCT_ARGS : ! SAVEARGS_TRAD_ARGS; ! goto done; ! } ! ! break; ! } } } /* * Compare against GCC push-based implementation */ ! found = 0; ! for (i = 0; i < size; i += sz) { ! if ((sz = instr_size(dhp, ins, i, size)) < 1) ! break; ! ! for (j = start_index; j < argc; j++) { ! if (sz == 2) /* Two byte */ ! n = INSTR2(ins, i); ! else if (sz == 1) ! n = INSTR1(ins, i); ! else ! continue; if (n == save_instr_push[j]) { ! found |= (1 << (j - start_index)); ! ! if (found == ! ((1 << (argc - start_index)) - 1)) { ! ret = SAVEARGS_TRAD_ARGS; ! goto done; ! } ! ! break; ! } } } ! /* ! * Look for a GCC-style returned structure. ! */ ! found = 0; if (start_index != 0) { ! for (i = 0; i < size; i += sz) { ! sz = instr_size(dhp, ins, i, size); ! ! if (sz < 1) ! break; ! else if (sz != 4) ! continue; ! n = INSTR4(ins, i); + /* argc is inclusive of start_index, allow for that */ + for (j = 0; j < (argc - start_index); j++) { if (n == save_instr_sr[j]) { ! found |= (1 << j); ! ! if (found == ! ((1 << (argc - start_index)) - 1)) { ! ret = SAVEARGS_TRAD_ARGS; ! goto done; ! } ! ! break; ! } } } } ! done: ! dis_handle_destroy(dhp); ! return (ret); }