1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include        <_libelf.h>
  28 #include        <dwarf.h>
  29 #include        <stdio.h>
  30 #include        <unistd.h>
  31 #include        <errno.h>
  32 #include        <strings.h>
  33 #include        <debug.h>
  34 #include        <conv.h>
  35 #include        <msg.h>
  36 #include        <_elfdump.h>
  37 
  38 
  39 /*
  40  * Data from eh_frame section used by dump_cfi()
  41  */
  42 typedef struct {
  43         const char      *file;
  44         const char      *sh_name;
  45         Half            e_machine;      /* ehdr->e_machine */
  46         uchar_t         *e_ident;       /* ehdr->e_ident */
  47         uint64_t        sh_addr;        /* Address of eh_frame section */
  48         int             do_swap;        /* True if object and system byte */
  49                                         /*      order differs */
  50         int             cieRflag;       /* R flag from current CIE */
  51         uint64_t        ciecalign;      /* CIE code align factor */
  52         int64_t         ciedalign;      /* CIE data align factor */
  53         uint64_t        fdeinitloc;     /* FDE initial location */
  54         uint64_t        gotaddr;        /* Address of the GOT */
  55 } dump_cfi_state_t;
  56 
  57 
  58 /*
  59  * Extract an unsigned integer value from an .eh_frame section, converting it
  60  * from its native byte order to that of the running machine if necessary.
  61  *
  62  * entry:
  63  *      data - Base address from which to extract datum
  64  *      ndx - Address of variable giving index to start byte in data.
  65  *      size - # of bytes in datum. Must be one of: 1, 2, 4, 8
  66  *      do_swap - True if the data is in a different byte order than that
  67  *              of the host system.
  68  *
  69  * exit:
  70  *      *ndx is incremented by the size of the extracted datum.
  71  *
  72  *      The requested datum is extracted, byte swapped if necessary,
  73  *      and returned.
  74  */
  75 static dwarf_error_t
  76 dwarf_extract_uint(uchar_t *data, size_t len, uint64_t *ndx, int size,
  77     int do_swap, uint64_t *ret)
  78 {
  79         if (((*ndx + size) > len) ||
  80             ((*ndx + size) < *ndx))
  81                 return (DW_OVERFLOW);
  82 
  83         switch (size) {
  84         case 1:
  85                 *ret = (data[(*ndx)++]);
  86                 return (DW_SUCCESS);
  87         case 2:
  88                 {
  89                         Half    r;
  90                         uchar_t *p = (uchar_t *)&r;
  91 
  92                         data += *ndx;
  93                         if (do_swap)
  94                                 UL_ASSIGN_BSWAP_HALF(p, data);
  95                         else
  96                                 UL_ASSIGN_HALF(p, data);
  97 
  98                         (*ndx) += 2;
  99                         *ret = r;
 100                         return (DW_SUCCESS);
 101                 }
 102         case 4:
 103                 {
 104                         Word    r;
 105                         uchar_t *p = (uchar_t *)&r;
 106 
 107                         data += *ndx;
 108                         if (do_swap)
 109                                 UL_ASSIGN_BSWAP_WORD(p, data);
 110                         else
 111                                 UL_ASSIGN_WORD(p, data);
 112 
 113                         (*ndx) += 4;
 114                         *ret = r;
 115                         return (DW_SUCCESS);
 116                 }
 117 
 118         case 8:
 119                 {
 120                         uint64_t        r;
 121                         uchar_t         *p = (uchar_t *)&r;
 122 
 123                         data += *ndx;
 124                         if (do_swap)
 125                                 UL_ASSIGN_BSWAP_LWORD(p, data);
 126                         else
 127                                 UL_ASSIGN_LWORD(p, data);
 128 
 129                         (*ndx) += 8;
 130                         *ret = r;
 131                         return (DW_SUCCESS);
 132                 }
 133         default:
 134                 return (DW_BAD_ENCODING);
 135         }
 136 
 137         /* NOTREACHED */
 138 }
 139 
 140 /*
 141  * Map a DWARF register constant to the machine register name it
 142  * corresponds to, formatting the result into buf.
 143  *
 144  * The assignment of DWARF register numbers is part of the system
 145  * specific ABI for each platform.
 146  *
 147  * entry:
 148  *      regno - DWARF register number
 149  *      mach - ELF machine code for platform
 150  *      buf, bufsize - Buffer to receive the formatted result string
 151  *
 152  * exit:
 153  *      The results are formatted into buf, and buf is returned.
 154  *      If the generated output would exceed the size of the buffer
 155  *      provided, it will be clipped to fit.
 156  */
 157 static const char *
 158 dwarf_regname(Half mach, int regno, char *buf, size_t bufsize)
 159 {
 160         Conv_inv_buf_t  inv_buf;
 161         const char      *name;
 162         int             good_name;
 163 
 164         name = conv_dwarf_regname(mach, regno, 0, &good_name, &inv_buf);
 165 
 166         /*
 167          * If there is a good mnemonic machine name for the register,
 168          * format the result as 'r# (mnemonic)'.  If there is no good
 169          * name for it, then simply format the dwarf name as 'r#'.
 170          */
 171         if (good_name)
 172                 (void) snprintf(buf, bufsize, MSG_ORIG(MSG_REG_FMT_NAME),
 173                     regno, name);
 174         else
 175                 (void) snprintf(buf, bufsize, MSG_ORIG(MSG_REG_FMT_BASIC),
 176                     regno);
 177 
 178         return (buf);
 179 }
 180 
 181 
 182 /*
 183  * Decode eh_frame Call Frame Instructions, printing each one on a
 184  * separate line.
 185  *
 186  * entry:
 187  *      data - Address of base of eh_frame section being processed
 188  *      off - Offset of current FDE within eh_frame
 189  *      ndx - Index of current position within current FDE
 190  *      len - Length of FDE
 191  *      state - Object, CIE, and FDE state for current request
 192  *      msg - Header message to issue before producing output.
 193  *      indent - # of indentation characters issued for each line of output.
 194  *
 195  * exit:
 196  *      The Call Frame Instructions have been decoded and printed.
 197  *
 198  *      *ndx has been incremented to contain the index of the next
 199  *              byte of data to be processed in eh_frame.
 200  *
 201  * note:
 202  *      The format of Call Frame Instructions in .eh_frame sections is based
 203  *      on the DWARF specification.
 204  */
 205 static void
 206 dump_cfi(uchar_t *data, uint64_t off, uint64_t *ndx, uint_t len,
 207     dump_cfi_state_t *state, const char *msg, int indent)
 208 {
 209         /*
 210          * We use %*s%s to insert leading whitespace and the op name.
 211          * PREFIX supplies these arguments.
 212          */
 213 #define PREFIX  indent, MSG_ORIG(MSG_STR_EMPTY), opname
 214 
 215         /* Hide boilerplate clutter in calls to dwarf_regname() */
 216 #define REGNAME(_rnum, _buf) \
 217         dwarf_regname(state->e_machine, _rnum, _buf, sizeof (_buf))
 218 
 219         /* Extract the lower 6 bits from an op code */
 220 #define LOW_OP(_op) (_op & 0x3f)
 221 
 222         char            rbuf1[32], rbuf2[32];
 223         Conv_inv_buf_t  inv_buf;
 224         uchar_t         op;
 225         const char      *opname;
 226         uint64_t        oper1, oper2, cur_pc;
 227         int64_t         soper;
 228         const char      *loc_str;
 229         int             i;
 230 
 231         dbg_print(0, msg);
 232 
 233         /*
 234          * In a CIE/FDE, the length field does not include it's own
 235          * size. Hence, the value passed in is 4 less than the index
 236          * of the actual final location.
 237          */
 238         len += 4;
 239 
 240         /*
 241          * There is a concept of the 'current location', which is the PC
 242          * to which the current item applies. It starts out set to the
 243          * FDE initial location, and can be set or incremented by
 244          * various OP codes. cur_pc is used to track this.
 245          *
 246          * We want to use 'initloc' in the output the first time the location
 247          * is referenced, and then switch to 'loc' for subsequent references.
 248          * loc_str is used to manage that.
 249          */
 250         cur_pc = state->fdeinitloc;
 251         loc_str = MSG_ORIG(MSG_STR_INITLOC);
 252 
 253         while (*ndx < len) {
 254                 /*
 255                  * The first byte contains the primary op code in the top
 256                  * 2 bits, so there are 4 of them. Primary OP code
 257                  * 0 uses the lower 6 bits to specify a sub-opcode, allowing
 258                  * for 64 of them. The other 3 primary op codes use the
 259                  * lower 6 bits to hold an operand (a register #, or value).
 260                  *
 261                  * Check the primary OP code. If it's 1-3, handle it
 262                  * and move to the next loop iteration. For OP code 0,
 263                  * fall through to decode the sub-code.
 264                  */
 265                 op = data[off + (*ndx)++];
 266                 opname = conv_dwarf_cfa(op, 0, &inv_buf);
 267                 switch (op >> 6) {
 268                 case 0x1:               /* v2: DW_CFA_advance_loc, delta */
 269                         oper1 = state->ciecalign * LOW_OP(op);
 270                         cur_pc += oper1;
 271                         dbg_print(0, MSG_ORIG(MSG_CFA_ADV_LOC), PREFIX,
 272                             loc_str, EC_XWORD(oper1), EC_XWORD(cur_pc));
 273                         loc_str = MSG_ORIG(MSG_STR_LOC);
 274                         continue;
 275 
 276                 case 0x2:               /* v2: DW_CFA_offset, reg, offset */
 277                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 278                             DW_OVERFLOW) {
 279                                 (void) fprintf(stderr,
 280                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 281                                     state->file, state->sh_name);
 282                                 return;
 283                         }
 284 
 285                         oper1 *= state->ciedalign;
 286                         dbg_print(0, MSG_ORIG(MSG_CFA_CFAOFF), PREFIX,
 287                             REGNAME(LOW_OP(op), rbuf1), EC_XWORD(oper1));
 288                         continue;
 289 
 290                 case 0x3:               /* v2: DW_CFA_restore, reg */
 291                         dbg_print(0, MSG_ORIG(MSG_CFA_REG), PREFIX,
 292                             REGNAME(LOW_OP(op), rbuf1));
 293                         continue;
 294                 }
 295 
 296                 /*
 297                  * If we're here, the high order 2 bits are 0. The low 6 bits
 298                  * specify a sub-opcode defining the operation.
 299                  */
 300                 switch (op) {
 301                 case 0x00:              /* v2: DW_CFA_nop */
 302                         /*
 303                          * No-ops are used to fill unused space required
 304                          * for alignment. It is common for there to be
 305                          * multiple adjacent nops. It saves space to report
 306                          * them all with a single line of output.
 307                          */
 308                         for (i = 1;
 309                             (*ndx < len) && (data[off + *ndx] == 0);
 310                             i++, (*ndx)++)
 311                                 ;
 312                         dbg_print(0, MSG_ORIG(MSG_CFA_SIMPLEREP), PREFIX, i);
 313                         break;
 314 
 315                 case 0x0a:              /* v2: DW_CFA_remember_state */
 316                 case 0x0b:              /* v2: DW_CFA_restore_state */
 317                 case 0x2d:              /* GNU: DW_CFA_GNU_window_save */
 318                         dbg_print(0, MSG_ORIG(MSG_CFA_SIMPLE), PREFIX);
 319                         break;
 320 
 321                 case 0x01:              /* v2: DW_CFA_set_loc, address */
 322                         switch (dwarf_ehe_extract(&data[off], len, ndx,
 323                             &cur_pc, state->cieRflag, state->e_ident, B_FALSE,
 324                             state->sh_addr, off + *ndx, state->gotaddr)) {
 325                         case DW_OVERFLOW:
 326                                 (void) fprintf(stderr,
 327                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 328                                     state->file, state->sh_name);
 329                                 return;
 330                         case DW_BAD_ENCODING:
 331                                 (void) fprintf(stderr,
 332                                     MSG_INTL(MSG_ERR_DWBADENC),
 333                                     state->file, state->sh_name,
 334                                     state->cieRflag);
 335                                 return;
 336                         case DW_SUCCESS:
 337                                 break;
 338                         }
 339                         dbg_print(0, MSG_ORIG(MSG_CFA_CFASET), PREFIX,
 340                             EC_XWORD(cur_pc));
 341                         break;
 342 
 343                 case 0x02:      /* v2: DW_CFA_advance_loc_1, 1-byte delta */
 344                 case 0x03:      /* v2: DW_CFA_advance_loc_2, 2-byte delta */
 345                 case 0x04:      /* v2: DW_CFA_advance_loc_4, 4-byte delta */
 346                         /*
 347                          * Since the codes are contiguous, and the sizes are
 348                          * powers of 2, we can compute the word width from
 349                          * the code.
 350                          */
 351                         i = 1 << (op - 0x02);
 352                         switch (dwarf_extract_uint(data + off, len,
 353                             ndx, i, state->do_swap, &oper1)) {
 354                         case DW_BAD_ENCODING:
 355                                 (void) fprintf(stderr,
 356                                     MSG_INTL(MSG_ERR_DWBADENC),
 357                                     state->file, state->sh_name,
 358                                     i);
 359                                 return;
 360                         case DW_OVERFLOW:
 361                                 (void) fprintf(stderr,
 362                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 363                                     state->file, state->sh_name);
 364                                 return;
 365                         case DW_SUCCESS:
 366                                 break;
 367                         }
 368                         oper1 *= state->ciecalign;
 369                         cur_pc += oper1;
 370                         dbg_print(0, MSG_ORIG(MSG_CFA_ADV_LOC), PREFIX,
 371                             loc_str, EC_XWORD(oper1), EC_XWORD(cur_pc));
 372                         loc_str = MSG_ORIG(MSG_STR_LOC);
 373                         break;
 374 
 375                 case 0x05:              /* v2: DW_CFA_offset_extended,reg,off */
 376                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 377                             DW_OVERFLOW) {
 378                                 (void) fprintf(stderr,
 379                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 380                                     state->file, state->sh_name);
 381                                 return;
 382                         }
 383 
 384                         if (sleb_extract(&data[off], ndx, len, &soper) ==
 385                             DW_OVERFLOW) {
 386                                 (void) fprintf(stderr,
 387                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 388                                     state->file, state->sh_name);
 389                                 return;
 390                         }
 391 
 392                         soper *= state->ciedalign;
 393                         dbg_print(0, MSG_ORIG(MSG_CFA_CFAOFF), PREFIX,
 394                             REGNAME(oper1, rbuf1), EC_SXWORD(soper));
 395                         break;
 396 
 397                 case 0x06:              /* v2: DW_CFA_restore_extended, reg */
 398                 case 0x0d:              /* v2: DW_CFA_def_cfa_register, reg */
 399                 case 0x08:              /* v2: DW_CFA_same_value, reg */
 400                 case 0x07:              /* v2: DW_CFA_undefined, reg */
 401                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 402                             DW_OVERFLOW) {
 403                                 (void) fprintf(stderr,
 404                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 405                                     state->file, state->sh_name);
 406                                 return;
 407                         }
 408 
 409                         dbg_print(0, MSG_ORIG(MSG_CFA_REG), PREFIX,
 410                             REGNAME(oper1, rbuf1));
 411                         break;
 412 
 413 
 414                 case 0x09:              /* v2: DW_CFA_register, reg, reg */
 415                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 416                             DW_OVERFLOW) {
 417                                 (void) fprintf(stderr,
 418                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 419                                     state->file, state->sh_name);
 420                                 return;
 421                         }
 422 
 423                         if (uleb_extract(&data[off], ndx, len, &oper2) ==
 424                             DW_OVERFLOW) {
 425                                 (void) fprintf(stderr,
 426                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 427                                     state->file, state->sh_name);
 428                                 return;
 429                         }
 430                         dbg_print(0, MSG_ORIG(MSG_CFA_REG_REG), PREFIX,
 431                             REGNAME(oper1, rbuf1), REGNAME(oper2, rbuf2));
 432                         break;
 433 
 434                 case 0x0c:              /* v2: DW_CFA_def_cfa, reg, offset */
 435                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 436                             DW_OVERFLOW) {
 437                                 (void) fprintf(stderr,
 438                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 439                                     state->file, state->sh_name);
 440                                 return;
 441                         }
 442 
 443                         if (uleb_extract(&data[off], ndx, len, &oper2) ==
 444                             DW_OVERFLOW) {
 445                                 (void) fprintf(stderr,
 446                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 447                                     state->file, state->sh_name);
 448                                 return;
 449                         }
 450                         dbg_print(0, MSG_ORIG(MSG_CFA_REG_OFFLLU), PREFIX,
 451                             REGNAME(oper1, rbuf1), EC_XWORD(oper2));
 452                         break;
 453 
 454                 case 0x0e:              /* v2: DW_CFA_def_cfa_offset, offset */
 455                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 456                             DW_OVERFLOW) {
 457                                 (void) fprintf(stderr,
 458                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 459                                     state->file, state->sh_name);
 460                                 return;
 461                         }
 462                         dbg_print(0, MSG_ORIG(MSG_CFA_LLU), PREFIX,
 463                             EC_XWORD(oper1));
 464                         break;
 465 
 466                 case 0x0f:              /* v3: DW_CFA_def_cfa_expression, blk */
 467                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 468                             DW_OVERFLOW) {
 469                                 (void) fprintf(stderr,
 470                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 471                                     state->file, state->sh_name);
 472                                 return;
 473                         }
 474                         dbg_print(0, MSG_ORIG(MSG_CFA_EBLK), PREFIX,
 475                             EC_XWORD(oper1));
 476                         /* We currently do not decode the expression block */
 477                         *ndx += oper1;
 478                         break;
 479 
 480                 case 0x10:              /* v3: DW_CFA_expression, reg, blk */
 481                 case 0x16:              /* v3: DW_CFA_val_expression,reg,blk */
 482                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 483                             DW_OVERFLOW) {
 484                                 (void) fprintf(stderr,
 485                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 486                                     state->file, state->sh_name);
 487                                 return;
 488                         }
 489 
 490                         if (uleb_extract(&data[off], ndx, len, &oper2) ==
 491                             DW_OVERFLOW) {
 492                                 (void) fprintf(stderr,
 493                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 494                                     state->file, state->sh_name);
 495                                 return;
 496                         }
 497                         dbg_print(0, MSG_ORIG(MSG_CFA_REG_EBLK), PREFIX,
 498                             REGNAME(oper1, rbuf1), EC_XWORD(oper2));
 499                         /* We currently do not decode the expression block */
 500                         *ndx += oper2;
 501                         break;
 502 
 503                 case 0x11:      /* v3: DW_CFA_offset_extended_sf, reg, off */
 504                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 505                             DW_OVERFLOW) {
 506                                 (void) fprintf(stderr,
 507                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 508                                     state->file, state->sh_name);
 509                                 return;
 510                         }
 511 
 512                         if (sleb_extract(&data[off], ndx, len, &soper) ==
 513                             DW_OVERFLOW) {
 514                                 (void) fprintf(stderr,
 515                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 516                                     state->file, state->sh_name);
 517                                 return;
 518                         }
 519 
 520                         soper *= state->ciedalign;
 521                         dbg_print(0, MSG_ORIG(MSG_CFA_CFAOFF), PREFIX,
 522                             REGNAME(oper1, rbuf1), EC_SXWORD(soper));
 523                         break;
 524 
 525                 case 0x12:              /* v3: DW_CFA_def_cfa_sf, reg, offset */
 526                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 527                             DW_OVERFLOW) {
 528                                 (void) fprintf(stderr,
 529                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 530                                     state->file, state->sh_name);
 531                                 return;
 532                         }
 533 
 534                         if (sleb_extract(&data[off], ndx, len, &soper) ==
 535                             DW_OVERFLOW) {
 536                                 (void) fprintf(stderr,
 537                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 538                                     state->file, state->sh_name);
 539                                 return;
 540                         }
 541 
 542                         soper *= state->ciedalign;
 543                         dbg_print(0, MSG_ORIG(MSG_CFA_REG_OFFLLD), PREFIX,
 544                             REGNAME(oper1, rbuf1), EC_SXWORD(soper));
 545                         break;
 546 
 547                 case 0x13:              /* DW_CFA_def_cfa_offset_sf, offset */
 548                         if (sleb_extract(&data[off], ndx, len, &soper) ==
 549                             DW_OVERFLOW) {
 550                                 (void) fprintf(stderr,
 551                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 552                                     state->file, state->sh_name);
 553                                 return;
 554                         }
 555 
 556                         soper *= state->ciedalign;
 557                         dbg_print(0, MSG_ORIG(MSG_CFA_LLD), PREFIX,
 558                             EC_SXWORD(soper));
 559                         break;
 560 
 561                 case 0x14:              /* v3: DW_CFA_val_offset, reg, offset */
 562                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 563                             DW_OVERFLOW) {
 564                                 (void) fprintf(stderr,
 565                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 566                                     state->file, state->sh_name);
 567                                 return;
 568                         }
 569 
 570                         if (sleb_extract(&data[off], ndx, len, &soper) ==
 571                             DW_OVERFLOW) {
 572                                 (void) fprintf(stderr,
 573                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 574                                     state->file, state->sh_name);
 575                                 return;
 576                         }
 577 
 578                         soper *= state->ciedalign;
 579                         dbg_print(0, MSG_ORIG(MSG_CFA_REG_OFFLLD), PREFIX,
 580                             REGNAME(oper1, rbuf1), EC_SXWORD(soper));
 581                         break;
 582 
 583                 case 0x15:      /* v3: DW_CFA_val_offset_sf, reg, offset */
 584                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 585                             DW_OVERFLOW) {
 586                                 (void) fprintf(stderr,
 587                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 588                                     state->file, state->sh_name);
 589                                 return;
 590                         }
 591 
 592                         if (sleb_extract(&data[off], ndx, len, &soper) ==
 593                             DW_OVERFLOW) {
 594                                 (void) fprintf(stderr,
 595                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 596                                     state->file, state->sh_name);
 597                                 return;
 598                         }
 599 
 600                         soper *= state->ciedalign;
 601                         dbg_print(0, MSG_ORIG(MSG_CFA_REG_OFFLLD), PREFIX,
 602                             REGNAME(oper1, rbuf1), EC_SXWORD(soper));
 603                         break;
 604 
 605                 case 0x1d:      /* GNU: DW_CFA_MIPS_advance_loc8, delta */
 606                         switch (dwarf_extract_uint(data + off, len,
 607                             ndx, 8, state->do_swap, &oper1)) {
 608                         case DW_BAD_ENCODING:
 609                                 (void) fprintf(stderr,
 610                                     MSG_INTL(MSG_ERR_DWBADENC),
 611                                     state->file, state->sh_name,
 612                                     8);
 613                                 return;
 614                         case DW_OVERFLOW:
 615                                 (void) fprintf(stderr,
 616                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 617                                     state->file, state->sh_name);
 618                                 return;
 619                         case DW_SUCCESS:
 620                                 break;
 621                         }
 622                         oper1 *= state->ciecalign;
 623                         cur_pc += oper1;
 624                         dbg_print(0, MSG_ORIG(MSG_CFA_ADV_LOC), PREFIX,
 625                             loc_str, EC_XWORD(oper1), EC_XWORD(cur_pc));
 626                         loc_str = MSG_ORIG(MSG_STR_LOC);
 627                         break;
 628 
 629                 case 0x2e:              /* GNU: DW_CFA_GNU_args_size, size */
 630                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 631                             DW_OVERFLOW) {
 632                                 (void) fprintf(stderr,
 633                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 634                                     state->file, state->sh_name);
 635                                 return;
 636                         }
 637 
 638                         dbg_print(0, MSG_ORIG(MSG_CFA_LLU), PREFIX,
 639                             EC_XWORD(oper1));
 640 
 641                         break;
 642 
 643                 case 0x2f: /* GNU:DW_CFA_GNU_negative_offset_extended,reg,off */
 644                         if (uleb_extract(&data[off], ndx, len, &oper1) ==
 645                             DW_OVERFLOW) {
 646                                 (void) fprintf(stderr,
 647                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 648                                     state->file, state->sh_name);
 649                                 return;
 650                         }
 651 
 652                         if (sleb_extract(&data[off], ndx, len, &soper) ==
 653                             DW_OVERFLOW) {
 654                                 (void) fprintf(stderr,
 655                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 656                                     state->file, state->sh_name);
 657                                 return;
 658                         }
 659                         soper = -soper * state->ciedalign;
 660                         soper *= state->ciedalign;
 661                         dbg_print(0, MSG_ORIG(MSG_CFA_CFAOFF), PREFIX,
 662                             REGNAME(oper1, rbuf1), EC_SXWORD(soper));
 663                         break;
 664 
 665                 default:
 666                         /*
 667                          * Unrecognized OP code: DWARF data is variable length,
 668                          * so we don't know how many bytes to skip in order to
 669                          * advance to the next item. We cannot decode beyond
 670                          * this point, so dump the remainder in hex.
 671                          */
 672                         (*ndx)--;       /* Back up to unrecognized opcode */
 673                         dump_hex_bytes(data + off + *ndx, len - *ndx,
 674                             indent, 8, 1);
 675                         (*ndx) = len;
 676                         break;
 677                 }
 678         }
 679 
 680 #undef PREFIX
 681 #undef REGNAME
 682 #undef LOW_OP
 683 }
 684 
 685 void
 686 dump_eh_frame(const char *file, char *sh_name, uchar_t *data, size_t datasize,
 687     uint64_t sh_addr, Half e_machine, uchar_t *e_ident, uint64_t gotaddr)
 688 {
 689         Conv_dwarf_ehe_buf_t    dwarf_ehe_buf;
 690         dump_cfi_state_t        cfi_state;
 691         uint64_t        off, ndx, length, id;
 692         uint_t          cieid, cielength, cieversion, cieretaddr;
 693         int             ciePflag = 0, cieZflag = 0, cieLflag = 0;
 694         int             cieLflag_present = 0;
 695         uint_t          cieaugndx;
 696         char            *cieaugstr = NULL;
 697         boolean_t       have_cie = B_FALSE;
 698 
 699         cfi_state.file = file;
 700         cfi_state.sh_name = sh_name;
 701         cfi_state.e_machine = e_machine;
 702         cfi_state.e_ident = e_ident;
 703         cfi_state.sh_addr = sh_addr;
 704         cfi_state.do_swap = _elf_sys_encoding() != e_ident[EI_DATA];
 705         cfi_state.gotaddr = gotaddr;
 706 
 707         off = 0;
 708         while (off < datasize) {
 709                 ndx = 0;
 710 
 711                 /*
 712                  * Extract length in native format.  A zero length indicates
 713                  * that this CIE is a terminator and that processing for this
 714                  * unwind information should end. However, skip this entry and
 715                  * keep processing, just in case there is any other information
 716                  * remaining in this section.  Note, ld(1) will terminate the
 717                  * processing of the .eh_frame contents for this file after a
 718                  * zero length CIE, thus any information that does follow is
 719                  * ignored by ld(1), and is therefore questionable.
 720                  */
 721                 if (dwarf_extract_uint(data + off, datasize - off,
 722                     &ndx, 4, cfi_state.do_swap, &length) == DW_OVERFLOW) {
 723                         (void) fprintf(stderr,
 724                             MSG_INTL(MSG_ERR_DWOVRFLW),
 725                             file, sh_name);
 726                         return;
 727                 }
 728 
 729                 if (length == 0) {
 730                         dbg_print(0, MSG_ORIG(MSG_UNW_ZEROTERM));
 731                         off += 4;
 732                         continue;
 733                 }
 734 
 735                 if (length > (datasize - off)) {
 736                         (void) fprintf(stderr, MSG_INTL(MSG_ERR_BADCIEFDELEN),
 737                             file, sh_name, EC_XWORD(length),
 738                             EC_XWORD(sh_addr + off));
 739                         /*
 740                          * If length is wrong, we have no means to find the
 741                          * next entry, just give up
 742                          */
 743                         return;
 744                 }
 745 
 746                 /*
 747                  * extract CIE id in native format
 748                  */
 749                 if (dwarf_extract_uint(data + off, datasize - off, &ndx,
 750                     4, cfi_state.do_swap, &id) == DW_OVERFLOW) {
 751                         (void) fprintf(stderr,
 752                             MSG_INTL(MSG_ERR_DWOVRFLW),
 753                             file, sh_name);
 754                         return;
 755                 }
 756 
 757                 /*
 758                  * A CIE record has an id of '0', otherwise this is a
 759                  * FDE entry and the 'id' is the CIE pointer.
 760                  */
 761                 if (id == 0) {
 762                         uint64_t        persVal, ndx_save = 0;
 763                         uint64_t        axsize;
 764 
 765 
 766                         have_cie = B_TRUE;
 767                         cielength = length;
 768                         cieid = id;
 769                         ciePflag = cfi_state.cieRflag = cieZflag = 0;
 770                         cieLflag = cieLflag_present = 0;
 771 
 772                         dbg_print(0, MSG_ORIG(MSG_UNW_CIE),
 773                             EC_XWORD(sh_addr + off));
 774                         dbg_print(0, MSG_ORIG(MSG_UNW_CIELNGTH),
 775                             cielength, cieid);
 776 
 777                         cieversion = data[off + ndx];
 778                         ndx += 1;
 779                         cieaugstr = (char *)(&data[off + ndx]);
 780                         ndx += strlen(cieaugstr) + 1;
 781 
 782                         dbg_print(0, MSG_ORIG(MSG_UNW_CIEVERS),
 783                             cieversion, cieaugstr);
 784 
 785                         if (uleb_extract(&data[off], &ndx, datasize - off,
 786                             &cfi_state.ciecalign) == DW_OVERFLOW) {
 787                                 (void) fprintf(stderr,
 788                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 789                                     file, sh_name);
 790                                 return;
 791                         }
 792 
 793                         if (sleb_extract(&data[off], &ndx, datasize - off,
 794                             &cfi_state.ciedalign) == DW_OVERFLOW) {
 795                                 (void) fprintf(stderr,
 796                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 797                                     file, sh_name);
 798                                 return;
 799                         }
 800                         cieretaddr = data[off + ndx];
 801                         ndx += 1;
 802 
 803                         dbg_print(0, MSG_ORIG(MSG_UNW_CIECALGN),
 804                             EC_XWORD(cfi_state.ciecalign),
 805                             EC_XWORD(cfi_state.ciedalign), cieretaddr);
 806 
 807                         if (cieaugstr[0])
 808                                 dbg_print(0, MSG_ORIG(MSG_UNW_CIEAXVAL));
 809 
 810                         for (cieaugndx = 0; cieaugstr[cieaugndx]; cieaugndx++) {
 811                                 switch (cieaugstr[cieaugndx]) {
 812                                 case 'z':
 813                                         if (uleb_extract(&data[off], &ndx,
 814                                             datasize - off, &axsize) ==
 815                                             DW_OVERFLOW) {
 816                                                 (void) fprintf(stderr,
 817                                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 818                                                     file, sh_name);
 819                                                 return;
 820                                         }
 821 
 822                                         dbg_print(0, MSG_ORIG(MSG_UNW_CIEAXSIZ),
 823                                             EC_XWORD(axsize));
 824                                         cieZflag = 1;
 825                                         /*
 826                                          * The auxiliary section can contain
 827                                          * unused padding bytes at the end, so
 828                                          * save the current index. Along with
 829                                          * axsize, we will use it to set ndx to
 830                                          * the proper continuation index after
 831                                          * the aux data has been processed.
 832                                          */
 833                                         ndx_save = ndx;
 834                                         break;
 835                                 case 'P':
 836                                         ciePflag = data[off + ndx];
 837                                         ndx += 1;
 838 
 839                                         switch (dwarf_ehe_extract(&data[off],
 840                                             datasize - off, &ndx, &persVal,
 841                                             ciePflag, e_ident, B_FALSE, sh_addr,
 842                                             off + ndx, gotaddr)) {
 843                                         case DW_OVERFLOW:
 844                                                 (void) fprintf(stderr,
 845                                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 846                                                     file, sh_name);
 847                                                 return;
 848                                         case DW_BAD_ENCODING:
 849                                                 (void) fprintf(stderr,
 850                                                     MSG_INTL(MSG_ERR_DWBADENC),
 851                                                     file, sh_name, ciePflag);
 852                                                 return;
 853                                         case DW_SUCCESS:
 854                                                 break;
 855                                         }
 856                                         dbg_print(0,
 857                                             MSG_ORIG(MSG_UNW_CIEAXPERS));
 858                                         dbg_print(0,
 859                                             MSG_ORIG(MSG_UNW_CIEAXPERSENC),
 860                                             ciePflag, conv_dwarf_ehe(ciePflag,
 861                                             &dwarf_ehe_buf));
 862                                         dbg_print(0,
 863                                             MSG_ORIG(MSG_UNW_CIEAXPERSRTN),
 864                                             EC_XWORD(persVal));
 865                                         break;
 866                                 case 'R':
 867                                         cfi_state.cieRflag = data[off + ndx];
 868                                         ndx += 1;
 869                                         dbg_print(0,
 870                                             MSG_ORIG(MSG_UNW_CIEAXCENC),
 871                                             cfi_state.cieRflag,
 872                                             conv_dwarf_ehe(cfi_state.cieRflag,
 873                                             &dwarf_ehe_buf));
 874                                         break;
 875                                 case 'L':
 876                                         cieLflag_present = 1;
 877                                         cieLflag = data[off + ndx];
 878                                         ndx += 1;
 879                                         dbg_print(0,
 880                                             MSG_ORIG(MSG_UNW_CIEAXLSDA),
 881                                             cieLflag, conv_dwarf_ehe(
 882                                             cieLflag, &dwarf_ehe_buf));
 883                                         break;
 884                                 default:
 885                                         dbg_print(0,
 886                                             MSG_ORIG(MSG_UNW_CIEAXUNEC),
 887                                             cieaugstr[cieaugndx]);
 888                                         break;
 889                                 }
 890                         }
 891 
 892                         /*
 893                          * If the z flag was present, reposition ndx using the
 894                          * length given. This will safely move us past any
 895                          * unaccessed padding bytes in the auxiliary section.
 896                          */
 897                         if (cieZflag)
 898                                 ndx = ndx_save + axsize;
 899 
 900                         /*
 901                          * Any remaining data are Call Frame Instructions
 902                          */
 903                         if ((cielength + 4) > ndx)
 904                                 dump_cfi(data, off, &ndx, cielength, &cfi_state,
 905                                     MSG_ORIG(MSG_UNW_CIECFI), 3);
 906                         off += cielength + 4;
 907 
 908                 } else {
 909                         uint_t      fdelength = length;
 910                         int         fdecieptr = id;
 911                         uint64_t    fdeaddrrange;
 912 
 913                         if (!have_cie) {
 914                                 (void) fprintf(stderr,
 915                                     MSG_INTL(MSG_ERR_DWNOCIE), file, sh_name);
 916                                 return;
 917                         }
 918 
 919                         dbg_print(0, MSG_ORIG(MSG_UNW_FDE),
 920                             EC_XWORD(sh_addr + off));
 921                         dbg_print(0, MSG_ORIG(MSG_UNW_FDELNGTH),
 922                             fdelength, fdecieptr);
 923 
 924                         switch (dwarf_ehe_extract(&data[off], datasize - off,
 925                             &ndx, &cfi_state.fdeinitloc, cfi_state.cieRflag,
 926                             e_ident, B_FALSE, sh_addr, off + ndx, gotaddr)) {
 927                         case DW_OVERFLOW:
 928                                 (void) fprintf(stderr,
 929                                     MSG_INTL(MSG_ERR_DWOVRFLW), file, sh_name);
 930                                 return;
 931                         case DW_BAD_ENCODING:
 932                                 (void) fprintf(stderr,
 933                                     MSG_INTL(MSG_ERR_DWBADENC), file, sh_name,
 934                                     cfi_state.cieRflag);
 935                                 return;
 936                         case DW_SUCCESS:
 937                                 break;
 938                         }
 939 
 940                         switch (dwarf_ehe_extract(&data[off], datasize - off,
 941                             &ndx, &fdeaddrrange,
 942                             (cfi_state.cieRflag & ~DW_EH_PE_pcrel), e_ident,
 943                             B_FALSE, sh_addr, off + ndx, gotaddr)) {
 944                         case DW_OVERFLOW:
 945                                 (void) fprintf(stderr,
 946                                     MSG_INTL(MSG_ERR_DWOVRFLW), file, sh_name);
 947                                 return;
 948                         case DW_BAD_ENCODING:
 949                                 (void) fprintf(stderr,
 950                                     MSG_INTL(MSG_ERR_DWBADENC), file, sh_name,
 951                                     (cfi_state.cieRflag & ~DW_EH_PE_pcrel));
 952                                 return;
 953                         case DW_SUCCESS:
 954                                 break;
 955                         }
 956 
 957                         dbg_print(0, MSG_ORIG(MSG_UNW_FDEINITLOC),
 958                             EC_XWORD(cfi_state.fdeinitloc),
 959                             EC_XWORD(fdeaddrrange),
 960                             EC_XWORD(cfi_state.fdeinitloc + fdeaddrrange - 1));
 961 
 962                         if ((cieaugstr != NULL) && (cieaugstr[0] != '\0'))
 963                                 dbg_print(0, MSG_ORIG(MSG_UNW_FDEAXVAL));
 964                         if (cieZflag) {
 965                                 uint64_t    val;
 966                                 uint64_t    lndx;
 967 
 968                                 if (uleb_extract(&data[off], &ndx,
 969                                     datasize - off, &val) == DW_OVERFLOW) {
 970                                         (void) fprintf(stderr,
 971                                             MSG_INTL(MSG_ERR_DWOVRFLW),
 972                                             file, sh_name);
 973                                         return;
 974                                 }
 975                                 lndx = ndx;
 976                                 ndx += val;
 977                                 dbg_print(0, MSG_ORIG(MSG_UNW_FDEAXSIZE),
 978                                     EC_XWORD(val));
 979                                 if (val && cieLflag_present) {
 980                                         uint64_t    lsda;
 981 
 982                                         switch (dwarf_ehe_extract(&data[off],
 983                                             datasize - off, &lndx, &lsda,
 984                                             cieLflag, e_ident, B_FALSE, sh_addr,
 985                                             off + lndx, gotaddr)) {
 986                                         case DW_OVERFLOW:
 987                                                 (void) fprintf(stderr,
 988                                                     MSG_INTL(MSG_ERR_DWOVRFLW),
 989                                                     file, sh_name);
 990                                                 return;
 991                                         case DW_BAD_ENCODING:
 992                                                 (void) fprintf(stderr,
 993                                                     MSG_INTL(MSG_ERR_DWBADENC),
 994                                                     file, sh_name, cieLflag);
 995                                                 return;
 996                                         case DW_SUCCESS:
 997                                                 break;
 998                                         }
 999                                         dbg_print(0,
1000                                             MSG_ORIG(MSG_UNW_FDEAXLSDA),
1001                                             EC_XWORD(lsda));
1002                                 }
1003                         }
1004                         if ((fdelength + 4) > ndx)
1005                                 dump_cfi(data, off, &ndx, fdelength, &cfi_state,
1006                                     MSG_ORIG(MSG_UNW_FDECFI), 6);
1007                         off += fdelength + 4;
1008                 }
1009         }
1010 }