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         Half            e_machine;      /* ehdr->e_machine */
  44         uchar_t         *e_ident;       /* ehdr->e_ident */
  45         uint64_t        sh_addr;        /* Address of eh_frame section */
  46         int             do_swap;        /* True if object and system byte */
  47                                         /*      order differs */
  48         int             cieRflag;       /* R flag from current CIE */
  49         uint64_t        ciecalign;      /* CIE code align factor */
  50         int64_t         ciedalign;      /* CIE data align factor */
  51         uint64_t        fdeinitloc;     /* FDE initial location */
  52         uint64_t        gotaddr;        /* Address of the GOT */
  53 } dump_cfi_state_t;
  54 
  55 
  56 /*
  57  * Extract an unsigned integer value from an .eh_frame section, converting it
  58  * from its native byte order to that of the running machine if necessary.
  59  *
  60  * entry:
  61  *      data - Base address from which to extract datum
  62  *      ndx - Address of variable giving index to start byte in data.
  63  *      size - # of bytes in datum. Must be one of: 1, 2, 4, 8
  64  *      do_swap - True if the data is in a different byte order than that
  65  *              of the host system.
  66  *
  67  * exit:
  68  *      *ndx is incremented by the size of the extracted datum.
  69  *
  70  *      The requested datum is extracted, byte swapped if necessary,
  71  *      and returned.
  72  */
  73 static uint64_t
  74 dwarf_extract_uint(uchar_t *data, uint64_t *ndx, int size, int do_swap)
  75 {
  76         switch (size) {
  77         case 1:
  78                 return (data[(*ndx)++]);
  79         case 2:
  80                 {
  81                         Half    r;
  82                         uchar_t *p = (uchar_t *)&r;
  83 
  84                         data += *ndx;
  85                         if (do_swap)
  86                                 UL_ASSIGN_BSWAP_HALF(p, data);
  87                         else
  88                                 UL_ASSIGN_HALF(p, data);
  89 
  90                         (*ndx) += 2;
  91                         return (r);
  92                 }
  93         case 4:
  94                 {
  95                         Word    r;
  96                         uchar_t *p = (uchar_t *)&r;
  97 
  98                         data += *ndx;
  99                         if (do_swap)
 100                                 UL_ASSIGN_BSWAP_WORD(p, data);
 101                         else
 102                                 UL_ASSIGN_WORD(p, data);
 103 
 104                         (*ndx) += 4;
 105                         return (r);
 106                 }
 107 
 108         case 8:
 109                 {
 110                         uint64_t        r;
 111                         uchar_t         *p = (uchar_t *)&r;
 112 
 113                         data += *ndx;
 114                         if (do_swap)
 115                                 UL_ASSIGN_BSWAP_LWORD(p, data);
 116                         else
 117                                 UL_ASSIGN_LWORD(p, data);
 118 
 119                         (*ndx) += 8;
 120                         return (r);
 121                 }
 122         }
 123 
 124         /* If here, an invalid size was specified */
 125         assert(0);
 126         return (0);
 127 }
 128 
 129 /*
 130  * Map a DWARF register constant to the machine register name it
 131  * corresponds to, formatting the result into buf.
 132  *
 133  * The assignment of DWARF register numbers is part of the system
 134  * specific ABI for each platform.
 135  *
 136  * entry:
 137  *      regno - DWARF register number
 138  *      mach - ELF machine code for platform
 139  *      buf, bufsize - Buffer to receive the formatted result string
 140  *
 141  * exit:
 142  *      The results are formatted into buf, and buf is returned.
 143  *      If the generated output would exceed the size of the buffer
 144  *      provided, it will be clipped to fit.
 145  */
 146 static const char *
 147 dwarf_regname(Half mach, int regno, char *buf, size_t bufsize)
 148 {
 149         Conv_inv_buf_t  inv_buf;
 150         const char      *name;
 151         int             good_name;
 152 
 153         name = conv_dwarf_regname(mach, regno, 0, &good_name, &inv_buf);
 154 
 155         /*
 156          * If there is a good mnemonic machine name for the register,
 157          * format the result as 'r# (mnemonic)'.  If there is no good
 158          * name for it, then simply format the dwarf name as 'r#'.
 159          */
 160         if (good_name)
 161                 (void) snprintf(buf, bufsize, MSG_ORIG(MSG_REG_FMT_NAME),
 162                     regno, name);
 163         else
 164                 (void) snprintf(buf, bufsize, MSG_ORIG(MSG_REG_FMT_BASIC),
 165                     regno);
 166 
 167         return (buf);
 168 }
 169 
 170 
 171 /*
 172  * Decode eh_frame Call Frame Instructions, printing each one on a
 173  * separate line.
 174  *
 175  * entry:
 176  *      data - Address of base of eh_frame section being processed
 177  *      off - Offset of current FDE within eh_frame
 178  *      ndx - Index of current position within current FDE
 179  *      len - Length of eh_frame section
 180  *      state - Object, CIE, and FDE state for current request
 181  *      msg - Header message to issue before producing output.
 182  *      indent - # of indentation characters issued for each line of output.
 183  *
 184  * exit:
 185  *      The Call Frame Instructions have been decoded and printed.
 186  *
 187  *      *ndx has been incremented to contain the index of the next
 188  *              byte of data to be processed in eh_frame.
 189  *
 190  * note:
 191  *      The format of Call Frame Instructions in .eh_frame sections is based
 192  *      on the DWARF specification.
 193  */
 194 static void
 195 dump_cfi(uchar_t *data, uint64_t off, uint64_t *ndx, uint_t len,
 196     dump_cfi_state_t *state, const char *msg, int indent)
 197 {
 198         /*
 199          * We use %*s%s to insert leading whitespace and the op name.
 200          * PREFIX supplies these arguments.
 201          */
 202 #define PREFIX  indent, MSG_ORIG(MSG_STR_EMPTY), opname
 203 
 204         /* Hide boilerplate clutter in calls to dwarf_regname() */
 205 #define REGNAME(_rnum, _buf) \
 206         dwarf_regname(state->e_machine, _rnum, _buf, sizeof (_buf))
 207 
 208         /* Extract the lower 6 bits from an op code */
 209 #define LOW_OP(_op) (_op & 0x3f)
 210 
 211         char            rbuf1[32], rbuf2[32];
 212         Conv_inv_buf_t  inv_buf;
 213         uchar_t         op;
 214         const char      *opname;
 215         uint64_t        oper1, oper2, cur_pc;
 216         int64_t         soper;
 217         const char      *loc_str;
 218         int             i;
 219 
 220         dbg_print(0, msg);
 221 
 222         /*
 223          * In a CIE/FDE, the length field does not include it's own
 224          * size. Hence, the value passed in is 4 less than the index
 225          * of the actual final location.
 226          */
 227         len += 4;
 228 
 229         /*
 230          * There is a concept of the 'current location', which is the PC
 231          * to which the current item applies. It starts out set to the
 232          * FDE initial location, and can be set or incremented by
 233          * various OP codes. cur_pc is used to track this.
 234          *
 235          * We want to use 'initloc' in the output the first time the location
 236          * is referenced, and then switch to 'loc' for subsequent references.
 237          * loc_str is used to manage that.
 238          */
 239         cur_pc = state->fdeinitloc;
 240         loc_str = MSG_ORIG(MSG_STR_INITLOC);
 241 
 242         while (*ndx < len) {
 243                 /*
 244                  * The first byte contains the primary op code in the top
 245                  * 2 bits, so there are 4 of them. Primary OP code
 246                  * 0 uses the lower 6 bits to specify a sub-opcode, allowing
 247                  * for 64 of them. The other 3 primary op codes use the
 248                  * lower 6 bits to hold an operand (a register #, or value).
 249                  *
 250                  * Check the primary OP code. If it's 1-3, handle it
 251                  * and move to the next loop iteration. For OP code 0,
 252                  * fall through to decode the sub-code.
 253                  */
 254                 op = data[off + (*ndx)++];
 255                 opname = conv_dwarf_cfa(op, 0, &inv_buf);
 256                 switch (op >> 6) {
 257                 case 0x1:               /* v2: DW_CFA_advance_loc, delta */
 258                         oper1 = state->ciecalign * LOW_OP(op);
 259                         cur_pc += oper1;
 260                         dbg_print(0, MSG_ORIG(MSG_CFA_ADV_LOC), PREFIX,
 261                             loc_str, EC_XWORD(oper1), EC_XWORD(cur_pc));
 262                         loc_str = MSG_ORIG(MSG_STR_LOC);
 263                         continue;
 264 
 265                 case 0x2:               /* v2: DW_CFA_offset, reg, offset */
 266                         soper = uleb_extract(&data[off], ndx) *
 267                             state->ciedalign;
 268                         dbg_print(0, MSG_ORIG(MSG_CFA_CFAOFF), PREFIX,
 269                             REGNAME(LOW_OP(op), rbuf1), EC_SXWORD(soper));
 270                         continue;
 271 
 272                 case 0x3:               /* v2: DW_CFA_restore, reg */
 273                         dbg_print(0, MSG_ORIG(MSG_CFA_REG), PREFIX,
 274                             REGNAME(LOW_OP(op), rbuf1));
 275                         continue;
 276                 }
 277 
 278                 /*
 279                  * If we're here, the high order 2 bits are 0. The low 6 bits
 280                  * specify a sub-opcode defining the operation.
 281                  */
 282                 switch (op) {
 283                 case 0x00:              /* v2: DW_CFA_nop */
 284                         /*
 285                          * No-ops are used to fill unused space required
 286                          * for alignment. It is common for there to be
 287                          * multiple adjacent nops. It saves space to report
 288                          * them all with a single line of output.
 289                          */
 290                         for (i = 1;
 291                             (*ndx < len) && (data[off + *ndx] == 0);
 292                             i++, (*ndx)++)
 293                                 ;
 294                         dbg_print(0, MSG_ORIG(MSG_CFA_SIMPLEREP), PREFIX, i);
 295                         break;
 296 
 297                 case 0x0a:              /* v2: DW_CFA_remember_state */
 298                 case 0x0b:              /* v2: DW_CFA_restore_state */
 299                 case 0x2d:              /* GNU: DW_CFA_GNU_window_save */
 300                         dbg_print(0, MSG_ORIG(MSG_CFA_SIMPLE), PREFIX);
 301                         break;
 302 
 303                 case 0x01:              /* v2: DW_CFA_set_loc, address */
 304                         cur_pc = dwarf_ehe_extract(&data[off], ndx,
 305                             state->cieRflag, state->e_ident, B_FALSE,
 306                             state->sh_addr, off + *ndx, state->gotaddr);
 307                         dbg_print(0, MSG_ORIG(MSG_CFA_CFASET), PREFIX,
 308                             EC_XWORD(cur_pc));
 309                         break;
 310 
 311                 case 0x02:      /* v2: DW_CFA_advance_loc_1, 1-byte delta */
 312                 case 0x03:      /* v2: DW_CFA_advance_loc_2, 2-byte delta */
 313                 case 0x04:      /* v2: DW_CFA_advance_loc_4, 4-byte delta */
 314                         /*
 315                          * Since the codes are contiguous, and the sizes are
 316                          * powers of 2, we can compute the word width from
 317                          * the code.
 318                          */
 319                         i = 1 << (op - 0x02);
 320                         oper1 = dwarf_extract_uint(data + off, ndx, i,
 321                             state->do_swap) * state->ciecalign;
 322                         cur_pc += oper1;
 323                         dbg_print(0, MSG_ORIG(MSG_CFA_ADV_LOC), PREFIX,
 324                             loc_str, EC_XWORD(oper1), EC_XWORD(cur_pc));
 325                         loc_str = MSG_ORIG(MSG_STR_LOC);
 326                         break;
 327 
 328                 case 0x05:              /* v2: DW_CFA_offset_extended,reg,off */
 329                         oper1 = uleb_extract(&data[off], ndx);
 330                         soper = uleb_extract(&data[off], ndx) *
 331                             state->ciedalign;
 332                         dbg_print(0, MSG_ORIG(MSG_CFA_CFAOFF), PREFIX,
 333                             REGNAME(oper1, rbuf1), EC_SXWORD(soper));
 334                         break;
 335 
 336                 case 0x06:              /* v2: DW_CFA_restore_extended, reg */
 337                 case 0x0d:              /* v2: DW_CFA_def_cfa_register, reg */
 338                 case 0x08:              /* v2: DW_CFA_same_value, reg */
 339                 case 0x07:              /* v2: DW_CFA_undefined, reg */
 340                         oper1 = uleb_extract(&data[off], ndx);
 341                         dbg_print(0, MSG_ORIG(MSG_CFA_REG), PREFIX,
 342                             REGNAME(oper1, rbuf1));
 343                         break;
 344 
 345 
 346                 case 0x09:              /* v2: DW_CFA_register, reg, reg */
 347                         oper1 = uleb_extract(&data[off], ndx);
 348                         oper2 = uleb_extract(&data[off], ndx);
 349                         dbg_print(0, MSG_ORIG(MSG_CFA_REG_REG), PREFIX,
 350                             REGNAME(oper1, rbuf1), REGNAME(oper2, rbuf2));
 351                         break;
 352 
 353                 case 0x0c:              /* v2: DW_CFA_def_cfa, reg, offset */
 354                         oper1 = uleb_extract(&data[off], ndx);
 355                         oper2 = uleb_extract(&data[off], ndx);
 356                         dbg_print(0, MSG_ORIG(MSG_CFA_REG_OFFLLU), PREFIX,
 357                             REGNAME(oper1, rbuf1), EC_XWORD(oper2));
 358                         break;
 359 
 360                 case 0x0e:              /* v2: DW_CFA_def_cfa_offset, offset */
 361                         oper1 = uleb_extract(&data[off], ndx);
 362                         dbg_print(0, MSG_ORIG(MSG_CFA_LLU), PREFIX,
 363                             EC_XWORD(oper1));
 364                         break;
 365 
 366                 case 0x0f:              /* v3: DW_CFA_def_cfa_expression, blk */
 367                         oper1 = uleb_extract(&data[off], ndx);
 368                         dbg_print(0, MSG_ORIG(MSG_CFA_EBLK), PREFIX,
 369                             EC_XWORD(oper1));
 370                         /* We currently do not decode the expression block */
 371                         *ndx += oper1;
 372                         break;
 373 
 374                 case 0x10:              /* v3: DW_CFA_expression, reg, blk */
 375                 case 0x16:              /* v3: DW_CFA_val_expression,reg,blk */
 376                         oper1 = uleb_extract(&data[off], ndx);
 377                         oper2 = uleb_extract(&data[off], ndx);
 378                         dbg_print(0, MSG_ORIG(MSG_CFA_REG_EBLK), PREFIX,
 379                             REGNAME(oper1, rbuf1), EC_XWORD(oper2));
 380                         /* We currently do not decode the expression block */
 381                         *ndx += oper2;
 382                         break;
 383 
 384                 case 0x11:      /* v3: DW_CFA_offset_extended_sf, reg, off */
 385                         oper1 = uleb_extract(&data[off], ndx);
 386                         soper = sleb_extract(&data[off], ndx) *
 387                             state->ciedalign;
 388                         dbg_print(0, MSG_ORIG(MSG_CFA_CFAOFF), PREFIX,
 389                             REGNAME(oper1, rbuf1), EC_SXWORD(soper));
 390                         break;
 391 
 392                 case 0x12:              /* v3: DW_CFA_def_cfa_sf, reg, offset */
 393                         oper1 = uleb_extract(&data[off], ndx);
 394                         soper = sleb_extract(&data[off], ndx) *
 395                             state->ciedalign;
 396                         dbg_print(0, MSG_ORIG(MSG_CFA_REG_OFFLLD), PREFIX,
 397                             REGNAME(oper1, rbuf1), EC_SXWORD(soper));
 398                         break;
 399 
 400                 case 0x13:              /* DW_CFA_def_cfa_offset_sf, offset */
 401                         soper = sleb_extract(&data[off], ndx) *
 402                             state->ciedalign;
 403                         dbg_print(0, MSG_ORIG(MSG_CFA_LLD), PREFIX,
 404                             EC_SXWORD(soper));
 405                         break;
 406 
 407                 case 0x14:              /* v3: DW_CFA_val_offset, reg, offset */
 408                         oper1 = uleb_extract(&data[off], ndx);
 409                         soper = uleb_extract(&data[off], ndx) *
 410                             state->ciedalign;
 411                         dbg_print(0, MSG_ORIG(MSG_CFA_REG_OFFLLD), PREFIX,
 412                             REGNAME(oper1, rbuf1), EC_SXWORD(soper));
 413                         break;
 414 
 415                 case 0x15:      /* v3: DW_CFA_val_offset_sf, reg, offset */
 416                         oper1 = uleb_extract(&data[off], ndx);
 417                         soper = sleb_extract(&data[off], ndx) *
 418                             state->ciedalign;
 419                         dbg_print(0, MSG_ORIG(MSG_CFA_REG_OFFLLD), PREFIX,
 420                             REGNAME(oper1, rbuf1), EC_SXWORD(soper));
 421                         break;
 422 
 423                 case 0x1d:      /* GNU: DW_CFA_MIPS_advance_loc8, delta */
 424                         oper1 = dwarf_extract_uint(data + off, ndx, i,
 425                             state->do_swap) * state->ciecalign;
 426                         cur_pc += oper1;
 427                         dbg_print(0, MSG_ORIG(MSG_CFA_ADV_LOC), PREFIX,
 428                             loc_str, EC_XWORD(oper1), EC_XWORD(cur_pc));
 429                         loc_str = MSG_ORIG(MSG_STR_LOC);
 430                         break;
 431 
 432                 case 0x2e:              /* GNU: DW_CFA_GNU_args_size, size */
 433                         oper1 = uleb_extract(&data[off], ndx);
 434                         dbg_print(0, MSG_ORIG(MSG_CFA_LLU), PREFIX,
 435                             EC_XWORD(oper1));
 436 
 437                         break;
 438 
 439                 case 0x2f: /* GNU:DW_CFA_GNU_negative_offset_extended,reg,off */
 440                         oper1 = uleb_extract(&data[off], ndx);
 441                         soper = -uleb_extract(&data[off], ndx) *
 442                             state->ciedalign;
 443                         dbg_print(0, MSG_ORIG(MSG_CFA_CFAOFF), PREFIX,
 444                             REGNAME(oper1, rbuf1), EC_SXWORD(soper));
 445                         break;
 446 
 447                 default:
 448                         /*
 449                          * Unrecognized OP code: DWARF data is variable length,
 450                          * so we don't know how many bytes to skip in order to
 451                          * advance to the next item. We cannot decode beyond
 452                          * this point, so dump the remainder in hex.
 453                          */
 454                         (*ndx)--;       /* Back up to unrecognized opcode */
 455                         dump_hex_bytes(data + off + *ndx, len - *ndx,
 456                             indent, 8, 1);
 457                         (*ndx) = len;
 458                         break;
 459                 }
 460         }
 461 
 462 #undef PREFIX
 463 #undef REGNAME
 464 #undef LOW_OP
 465 }
 466 
 467 void
 468 dump_eh_frame(uchar_t *data, size_t datasize, uint64_t sh_addr,
 469     Half e_machine, uchar_t *e_ident, uint64_t gotaddr)
 470 {
 471         Conv_dwarf_ehe_buf_t    dwarf_ehe_buf;
 472         dump_cfi_state_t        cfi_state;
 473         uint64_t        off, ndx;
 474         uint_t          cieid, cielength, cieversion, cieretaddr;
 475         int             ciePflag, cieZflag, cieLflag, cieLflag_present;
 476         uint_t          cieaugndx, length, id;
 477         char            *cieaugstr;
 478 
 479         cfi_state.e_machine = e_machine;
 480         cfi_state.e_ident = e_ident;
 481         cfi_state.sh_addr = sh_addr;
 482         cfi_state.do_swap = _elf_sys_encoding() != e_ident[EI_DATA];
 483         cfi_state.gotaddr = gotaddr;
 484 
 485         off = 0;
 486         while (off < datasize) {
 487                 ndx = 0;
 488 
 489                 /*
 490                  * Extract length in native format.  A zero length indicates
 491                  * that this CIE is a terminator and that processing for this
 492                  * unwind information should end. However, skip this entry and
 493                  * keep processing, just in case there is any other information
 494                  * remaining in this section.  Note, ld(1) will terminate the
 495                  * processing of the .eh_frame contents for this file after a
 496                  * zero length CIE, thus any information that does follow is
 497                  * ignored by ld(1), and is therefore questionable.
 498                  */
 499                 length = (uint_t)dwarf_extract_uint(data + off, &ndx,
 500                     4, cfi_state.do_swap);
 501                 if (length == 0) {
 502                         dbg_print(0, MSG_ORIG(MSG_UNW_ZEROTERM));
 503                         off += 4;
 504                         continue;
 505                 }
 506 
 507                 /*
 508                  * extract CIE id in native format
 509                  */
 510                 id = (uint_t)dwarf_extract_uint(data + off, &ndx,
 511                     4, cfi_state.do_swap);
 512 
 513                 /*
 514                  * A CIE record has an id of '0', otherwise this is a
 515                  * FDE entry and the 'id' is the CIE pointer.
 516                  */
 517                 if (id == 0) {
 518                         uint64_t        persVal, ndx_save;
 519                         uint_t          axsize;
 520 
 521                         cielength = length;
 522                         cieid = id;
 523                         ciePflag = cfi_state.cieRflag = cieZflag = 0;
 524                         cieLflag = cieLflag_present = 0;
 525 
 526                         dbg_print(0, MSG_ORIG(MSG_UNW_CIE),
 527                             EC_XWORD(sh_addr + off));
 528                         dbg_print(0, MSG_ORIG(MSG_UNW_CIELNGTH),
 529                             cielength, cieid);
 530 
 531                         cieversion = data[off + ndx];
 532                         ndx += 1;
 533                         cieaugstr = (char *)(&data[off + ndx]);
 534                         ndx += strlen(cieaugstr) + 1;
 535 
 536                         dbg_print(0, MSG_ORIG(MSG_UNW_CIEVERS),
 537                             cieversion, cieaugstr);
 538 
 539                         cfi_state.ciecalign = uleb_extract(&data[off], &ndx);
 540                         cfi_state.ciedalign = sleb_extract(&data[off], &ndx);
 541                         cieretaddr = data[off + ndx];
 542                         ndx += 1;
 543 
 544                         dbg_print(0, MSG_ORIG(MSG_UNW_CIECALGN),
 545                             EC_XWORD(cfi_state.ciecalign),
 546                             EC_XWORD(cfi_state.ciedalign), cieretaddr);
 547 
 548                         if (cieaugstr[0])
 549                                 dbg_print(0, MSG_ORIG(MSG_UNW_CIEAXVAL));
 550 
 551                         for (cieaugndx = 0; cieaugstr[cieaugndx]; cieaugndx++) {
 552                                 switch (cieaugstr[cieaugndx]) {
 553                                 case 'z':
 554                                         axsize = uleb_extract(&data[off], &ndx);
 555                                         dbg_print(0, MSG_ORIG(MSG_UNW_CIEAXSIZ),
 556                                             axsize);
 557                                         cieZflag = 1;
 558                                         /*
 559                                          * The auxiliary section can contain
 560                                          * unused padding bytes at the end, so
 561                                          * save the current index. Along with
 562                                          * axsize, we will use it to set ndx to
 563                                          * the proper continuation index after
 564                                          * the aux data has been processed.
 565                                          */
 566                                         ndx_save = ndx;
 567                                         break;
 568                                 case 'P':
 569                                         ciePflag = data[off + ndx];
 570                                         ndx += 1;
 571 
 572                                         persVal = dwarf_ehe_extract(&data[off],
 573                                             &ndx, ciePflag, e_ident, B_FALSE,
 574                                             sh_addr, off + ndx, gotaddr);
 575                                         dbg_print(0,
 576                                             MSG_ORIG(MSG_UNW_CIEAXPERS));
 577                                         dbg_print(0,
 578                                             MSG_ORIG(MSG_UNW_CIEAXPERSENC),
 579                                             ciePflag, conv_dwarf_ehe(ciePflag,
 580                                             &dwarf_ehe_buf));
 581                                         dbg_print(0,
 582                                             MSG_ORIG(MSG_UNW_CIEAXPERSRTN),
 583                                             EC_XWORD(persVal));
 584                                         break;
 585                                 case 'R':
 586                                         cfi_state.cieRflag = data[off + ndx];
 587                                         ndx += 1;
 588                                         dbg_print(0,
 589                                             MSG_ORIG(MSG_UNW_CIEAXCENC),
 590                                             cfi_state.cieRflag,
 591                                             conv_dwarf_ehe(cfi_state.cieRflag,
 592                                             &dwarf_ehe_buf));
 593                                         break;
 594                                 case 'L':
 595                                         cieLflag_present = 1;
 596                                         cieLflag = data[off + ndx];
 597                                         ndx += 1;
 598                                         dbg_print(0,
 599                                             MSG_ORIG(MSG_UNW_CIEAXLSDA),
 600                                             cieLflag, conv_dwarf_ehe(
 601                                             cieLflag, &dwarf_ehe_buf));
 602                                         break;
 603                                 default:
 604                                         dbg_print(0,
 605                                             MSG_ORIG(MSG_UNW_CIEAXUNEC),
 606                                             cieaugstr[cieaugndx]);
 607                                         break;
 608                                 }
 609                         }
 610 
 611                         /*
 612                          * If the z flag was present, reposition ndx using the
 613                          * length given. This will safely move us past any
 614                          * unaccessed padding bytes in the auxiliary section.
 615                          */
 616                         if (cieZflag)
 617                                 ndx = ndx_save + axsize;
 618 
 619                         /*
 620                          * Any remaining data are Call Frame Instructions
 621                          */
 622                         if ((cielength + 4) > ndx)
 623                                 dump_cfi(data, off, &ndx, cielength, &cfi_state,
 624                                     MSG_ORIG(MSG_UNW_CIECFI), 3);
 625                         off += cielength + 4;
 626 
 627                 } else {
 628                         uint_t      fdelength = length;
 629                         int         fdecieptr = id;
 630                         uint64_t    fdeaddrrange;
 631 
 632                         dbg_print(0, MSG_ORIG(MSG_UNW_FDE),
 633                             EC_XWORD(sh_addr + off));
 634                         dbg_print(0, MSG_ORIG(MSG_UNW_FDELNGTH),
 635                             fdelength, fdecieptr);
 636 
 637                         cfi_state.fdeinitloc = dwarf_ehe_extract(&data[off],
 638                             &ndx, cfi_state.cieRflag, e_ident, B_FALSE,
 639                             sh_addr, off + ndx, gotaddr);
 640                         fdeaddrrange = dwarf_ehe_extract(&data[off], &ndx,
 641                             (cfi_state.cieRflag & ~DW_EH_PE_pcrel),
 642                             e_ident, B_FALSE, sh_addr, off + ndx, gotaddr);
 643 
 644                         dbg_print(0, MSG_ORIG(MSG_UNW_FDEINITLOC),
 645                             EC_XWORD(cfi_state.fdeinitloc),
 646                             EC_XWORD(fdeaddrrange),
 647                             EC_XWORD(cfi_state.fdeinitloc + fdeaddrrange - 1));
 648 
 649                         if (cieaugstr[0])
 650                                 dbg_print(0, MSG_ORIG(MSG_UNW_FDEAXVAL));
 651                         if (cieZflag) {
 652                                 uint64_t    val;
 653                                 uint64_t    lndx;
 654 
 655                                 val = uleb_extract(&data[off], &ndx);
 656                                 lndx = ndx;
 657                                 ndx += val;
 658                                 dbg_print(0, MSG_ORIG(MSG_UNW_FDEAXSIZE),
 659                                     EC_XWORD(val));
 660                                 if (val && cieLflag_present) {
 661                                         uint64_t    lsda;
 662 
 663                                         lsda = dwarf_ehe_extract(&data[off],
 664                                             &lndx, cieLflag, e_ident,
 665                                             B_FALSE, sh_addr, off + lndx,
 666                                             gotaddr);
 667                                         dbg_print(0,
 668                                             MSG_ORIG(MSG_UNW_FDEAXLSDA),
 669                                             EC_XWORD(lsda));
 670                                 }
 671                         }
 672                         if ((fdelength + 4) > ndx)
 673                                 dump_cfi(data, off, &ndx, fdelength, &cfi_state,
 674                                     MSG_ORIG(MSG_UNW_FDECFI), 6);
 675                         off += fdelength + 4;
 676                 }
 677         }
 678 }