/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <_libelf.h>
#include <dwarf.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <strings.h>
#include <debug.h>
#include <conv.h>
#include <msg.h>
#include <_elfdump.h>
/*
* Data from eh_frame section used by dump_cfi()
*/
typedef struct {
const char *file;
const char *sh_name;
/* order differs */
/*
* Extract an unsigned integer value from an .eh_frame section, converting it
* from its native byte order to that of the running machine if necessary.
*
* entry:
* data - Base address from which to extract datum
* ndx - Address of variable giving index to start byte in data.
* size - # of bytes in datum. Must be one of: 1, 2, 4, 8
* do_swap - True if the data is in a different byte order than that
* of the host system.
*
* exit:
* *ndx is incremented by the size of the extracted datum.
*
* The requested datum is extracted, byte swapped if necessary,
* and returned.
*/
static dwarf_error_t
{
return (DW_OVERFLOW);
switch (size) {
case 1:
return (DW_SUCCESS);
case 2:
{
Half r;
if (do_swap)
UL_ASSIGN_BSWAP_HALF(p, data);
else
UL_ASSIGN_HALF(p, data);
(*ndx) += 2;
*ret = r;
return (DW_SUCCESS);
}
case 4:
{
Word r;
if (do_swap)
UL_ASSIGN_BSWAP_WORD(p, data);
else
UL_ASSIGN_WORD(p, data);
(*ndx) += 4;
*ret = r;
return (DW_SUCCESS);
}
case 8:
{
uint64_t r;
if (do_swap)
else
UL_ASSIGN_LWORD(p, data);
(*ndx) += 8;
*ret = r;
return (DW_SUCCESS);
}
default:
return (DW_BAD_ENCODING);
}
/* NOTREACHED */
}
/*
* Map a DWARF register constant to the machine register name it
* corresponds to, formatting the result into buf.
*
* The assignment of DWARF register numbers is part of the system
* specific ABI for each platform.
*
* entry:
* regno - DWARF register number
* mach - ELF machine code for platform
* buf, bufsize - Buffer to receive the formatted result string
*
* exit:
* The results are formatted into buf, and buf is returned.
* If the generated output would exceed the size of the buffer
* provided, it will be clipped to fit.
*/
static const char *
{
const char *name;
int good_name;
/*
* If there is a good mnemonic machine name for the register,
* format the result as 'r# (mnemonic)'. If there is no good
* name for it, then simply format the dwarf name as 'r#'.
*/
if (good_name)
else
regno);
return (buf);
}
/*
* Decode eh_frame Call Frame Instructions, printing each one on a
* separate line.
*
* entry:
* data - Address of base of eh_frame section being processed
* off - Offset of current FDE within eh_frame
* ndx - Index of current position within current FDE
* len - Length of FDE
* state - Object, CIE, and FDE state for current request
* msg - Header message to issue before producing output.
* indent - # of indentation characters issued for each line of output.
*
* exit:
* The Call Frame Instructions have been decoded and printed.
*
* *ndx has been incremented to contain the index of the next
* byte of data to be processed in eh_frame.
*
* note:
* The format of Call Frame Instructions in .eh_frame sections is based
* on the DWARF specification.
*/
static void
{
/*
* We use %*s%s to insert leading whitespace and the op name.
* PREFIX supplies these arguments.
*/
/* Hide boilerplate clutter in calls to dwarf_regname() */
/* Extract the lower 6 bits from an op code */
const char *opname;
const char *loc_str;
int i;
/*
* size. Hence, the value passed in is 4 less than the index
* of the actual final location.
*/
len += 4;
/*
* There is a concept of the 'current location', which is the PC
* to which the current item applies. It starts out set to the
* FDE initial location, and can be set or incremented by
* various OP codes. cur_pc is used to track this.
*
* We want to use 'initloc' in the output the first time the location
* is referenced, and then switch to 'loc' for subsequent references.
* loc_str is used to manage that.
*/
/*
* The first byte contains the primary op code in the top
* 2 bits, so there are 4 of them. Primary OP code
* 0 uses the lower 6 bits to specify a sub-opcode, allowing
* for 64 of them. The other 3 primary op codes use the
* lower 6 bits to hold an operand (a register #, or value).
*
* Check the primary OP code. If it's 1-3, handle it
* and move to the next loop iteration. For OP code 0,
* fall through to decode the sub-code.
*/
switch (op >> 6) {
case 0x1: /* v2: DW_CFA_advance_loc, delta */
continue;
case 0x2: /* v2: DW_CFA_offset, reg, offset */
DW_OVERFLOW) {
return;
}
continue;
case 0x3: /* v2: DW_CFA_restore, reg */
continue;
}
/*
* If we're here, the high order 2 bits are 0. The low 6 bits
* specify a sub-opcode defining the operation.
*/
switch (op) {
case 0x00: /* v2: DW_CFA_nop */
/*
* No-ops are used to fill unused space required
* for alignment. It is common for there to be
* multiple adjacent nops. It saves space to report
* them all with a single line of output.
*/
for (i = 1;
i++, (*ndx)++)
;
break;
case 0x0a: /* v2: DW_CFA_remember_state */
case 0x0b: /* v2: DW_CFA_restore_state */
case 0x2d: /* GNU: DW_CFA_GNU_window_save */
break;
case 0x01: /* v2: DW_CFA_set_loc, address */
case DW_OVERFLOW:
return;
case DW_BAD_ENCODING:
return;
case DW_SUCCESS:
break;
}
break;
case 0x02: /* v2: DW_CFA_advance_loc_1, 1-byte delta */
case 0x03: /* v2: DW_CFA_advance_loc_2, 2-byte delta */
case 0x04: /* v2: DW_CFA_advance_loc_4, 4-byte delta */
/*
* Since the codes are contiguous, and the sizes are
* powers of 2, we can compute the word width from
* the code.
*/
case DW_BAD_ENCODING:
i);
return;
case DW_OVERFLOW:
return;
case DW_SUCCESS:
break;
}
break;
case 0x05: /* v2: DW_CFA_offset_extended,reg,off */
DW_OVERFLOW) {
return;
}
DW_OVERFLOW) {
return;
}
break;
case 0x06: /* v2: DW_CFA_restore_extended, reg */
case 0x0d: /* v2: DW_CFA_def_cfa_register, reg */
case 0x08: /* v2: DW_CFA_same_value, reg */
case 0x07: /* v2: DW_CFA_undefined, reg */
DW_OVERFLOW) {
return;
}
break;
case 0x09: /* v2: DW_CFA_register, reg, reg */
DW_OVERFLOW) {
return;
}
DW_OVERFLOW) {
return;
}
break;
case 0x0c: /* v2: DW_CFA_def_cfa, reg, offset */
DW_OVERFLOW) {
return;
}
DW_OVERFLOW) {
return;
}
break;
case 0x0e: /* v2: DW_CFA_def_cfa_offset, offset */
DW_OVERFLOW) {
return;
}
break;
case 0x0f: /* v3: DW_CFA_def_cfa_expression, blk */
DW_OVERFLOW) {
return;
}
/* We currently do not decode the expression block */
break;
case 0x10: /* v3: DW_CFA_expression, reg, blk */
case 0x16: /* v3: DW_CFA_val_expression,reg,blk */
DW_OVERFLOW) {
return;
}
DW_OVERFLOW) {
return;
}
/* We currently do not decode the expression block */
break;
case 0x11: /* v3: DW_CFA_offset_extended_sf, reg, off */
DW_OVERFLOW) {
return;
}
DW_OVERFLOW) {
return;
}
break;
case 0x12: /* v3: DW_CFA_def_cfa_sf, reg, offset */
DW_OVERFLOW) {
return;
}
DW_OVERFLOW) {
return;
}
break;
case 0x13: /* DW_CFA_def_cfa_offset_sf, offset */
DW_OVERFLOW) {
return;
}
break;
case 0x14: /* v3: DW_CFA_val_offset, reg, offset */
DW_OVERFLOW) {
return;
}
DW_OVERFLOW) {
return;
}
break;
case 0x15: /* v3: DW_CFA_val_offset_sf, reg, offset */
DW_OVERFLOW) {
return;
}
DW_OVERFLOW) {
return;
}
break;
case 0x1d: /* GNU: DW_CFA_MIPS_advance_loc8, delta */
case DW_BAD_ENCODING:
8);
return;
case DW_OVERFLOW:
return;
case DW_SUCCESS:
break;
}
break;
case 0x2e: /* GNU: DW_CFA_GNU_args_size, size */
DW_OVERFLOW) {
return;
}
break;
case 0x2f: /* GNU:DW_CFA_GNU_negative_offset_extended,reg,off */
DW_OVERFLOW) {
return;
}
DW_OVERFLOW) {
return;
}
break;
default:
/*
* Unrecognized OP code: DWARF data is variable length,
* so we don't know how many bytes to skip in order to
* advance to the next item. We cannot decode beyond
* this point, so dump the remainder in hex.
*/
(*ndx)--; /* Back up to unrecognized opcode */
break;
}
}
}
void
{
int cieLflag_present = 0;
off = 0;
ndx = 0;
/*
* Extract length in native format. A zero length indicates
* that this CIE is a terminator and that processing for this
* unwind information should end. However, skip this entry and
* keep processing, just in case there is any other information
* remaining in this section. Note, ld(1) will terminate the
* processing of the .eh_frame contents for this file after a
* zero length CIE, thus any information that does follow is
* ignored by ld(1), and is therefore questionable.
*/
return;
}
if (length == 0) {
off += 4;
continue;
}
/*
* If length is wrong, we have no means to find the
* next entry, just give up
*/
return;
}
/*
* extract CIE id in native format
*/
return;
}
/*
* A CIE record has an id of '0', otherwise this is a
* FDE entry and the 'id' is the CIE pointer.
*/
if (id == 0) {
cieLflag = cieLflag_present = 0;
ndx += 1;
return;
}
return;
}
ndx += 1;
if (cieaugstr[0])
case 'z':
DW_OVERFLOW) {
return;
}
cieZflag = 1;
/*
* The auxiliary section can contain
* unused padding bytes at the end, so
* save the current index. Along with
* axsize, we will use it to set ndx to
* the proper continuation index after
* the aux data has been processed.
*/
break;
case 'P':
ndx += 1;
case DW_OVERFLOW:
return;
case DW_BAD_ENCODING:
return;
case DW_SUCCESS:
break;
}
dbg_print(0,
dbg_print(0,
&dwarf_ehe_buf));
dbg_print(0,
break;
case 'R':
ndx += 1;
dbg_print(0,
&dwarf_ehe_buf));
break;
case 'L':
cieLflag_present = 1;
ndx += 1;
dbg_print(0,
cieLflag, &dwarf_ehe_buf));
break;
default:
dbg_print(0,
break;
}
}
/*
* If the z flag was present, reposition ndx using the
* length given. This will safely move us past any
* unaccessed padding bytes in the auxiliary section.
*/
if (cieZflag)
/*
* Any remaining data are Call Frame Instructions
*/
} else {
if (!have_cie) {
return;
}
case DW_OVERFLOW:
return;
case DW_BAD_ENCODING:
return;
case DW_SUCCESS:
break;
}
&ndx, &fdeaddrrange,
case DW_OVERFLOW:
return;
case DW_BAD_ENCODING:
return;
case DW_SUCCESS:
break;
}
if (cieZflag) {
return;
}
if (val && cieLflag_present) {
case DW_OVERFLOW:
return;
case DW_BAD_ENCODING:
return;
case DW_SUCCESS:
break;
}
dbg_print(0,
}
}
}
}
}