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>


  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 }