summaryrefslogtreecommitdiffstats
path: root/lib/libdwarf/dwarf_loc.c
diff options
context:
space:
mode:
authorjb <jb@FreeBSD.org>2008-05-22 02:14:23 +0000
committerjb <jb@FreeBSD.org>2008-05-22 02:14:23 +0000
commit6bb9fac44691095d77c0fe90a650a6e10efad674 (patch)
treee3765f4585c6589b116b3afadce01322d61764ac /lib/libdwarf/dwarf_loc.c
parentec1dde64a242f2a00001a19b01713bd34b4420ec (diff)
downloadFreeBSD-src-6bb9fac44691095d77c0fe90a650a6e10efad674.zip
FreeBSD-src-6bb9fac44691095d77c0fe90a650a6e10efad674.tar.gz
Add a BSD licensed DWARF library for use by the DTrace clients.
The API for this library is deliberately different to the GPL'd libdwarf to avoid licensing problems.
Diffstat (limited to 'lib/libdwarf/dwarf_loc.c')
-rw-r--r--lib/libdwarf/dwarf_loc.c613
1 files changed, 613 insertions, 0 deletions
diff --git a/lib/libdwarf/dwarf_loc.c b/lib/libdwarf/dwarf_loc.c
new file mode 100644
index 0000000..6012142
--- /dev/null
+++ b/lib/libdwarf/dwarf_loc.c
@@ -0,0 +1,613 @@
+/*-
+ * Copyright (c) 2007 John Birrell (jb@freebsd.org)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdlib.h>
+#include "_libdwarf.h"
+
+static int64_t
+dwarf_decode_sleb128(uint8_t **dp)
+{
+ int64_t ret = 0;
+ uint8_t b;
+ int shift = 0;
+
+ uint8_t *src = *dp;
+
+ do {
+ b = *src++;
+
+ ret |= ((b & 0x7f) << shift);
+
+ shift += 7;
+ } while ((b & 0x80) != 0);
+
+ if (shift < 32 && (b & 0x40) != 0)
+ ret |= (-1 << shift);
+
+ *dp = src;
+
+ return ret;
+}
+
+static uint64_t
+dwarf_decode_uleb128(uint8_t **dp)
+{
+ uint64_t ret = 0;
+ uint8_t b;
+ int shift = 0;
+
+ uint8_t *src = *dp;
+
+ do {
+ b = *src++;
+
+ ret |= ((b & 0x7f) << shift);
+
+ shift += 7;
+ } while ((b & 0x80) != 0);
+
+ *dp = src;
+
+ return ret;
+}
+
+/*
+ * Given an array of bytes of length 'len' representing a
+ * DWARF expression, compute the number of operations based
+ * on there being one byte describing the operation and
+ * zero or more bytes of operands as defined in the standard
+ * for each operation type.
+ */
+int
+dwarf_op_num(uint8_t pointer_size, uint8_t *p, int len)
+{
+ int count = 0;
+ int64_t sval;
+ uint64_t uval;
+ uint8_t *last = p + len;
+
+ /*
+ * Process each byte. If an error occurs, then the
+ * count will be set to -1.
+ */
+ while (p < last && count >= 0) {
+ count++;
+
+ switch (*p++) {
+ /* Operations with no operands. */
+ case DW_OP_deref:
+ case DW_OP_reg0:
+ case DW_OP_reg1:
+ case DW_OP_reg2:
+ case DW_OP_reg3:
+ case DW_OP_reg4:
+ case DW_OP_reg5:
+ case DW_OP_reg6:
+ case DW_OP_reg7:
+ case DW_OP_reg8:
+ case DW_OP_reg9:
+ case DW_OP_reg10:
+ case DW_OP_reg11:
+ case DW_OP_reg12:
+ case DW_OP_reg13:
+ case DW_OP_reg14:
+ case DW_OP_reg15:
+ case DW_OP_reg16:
+ case DW_OP_reg17:
+ case DW_OP_reg18:
+ case DW_OP_reg19:
+ case DW_OP_reg20:
+ case DW_OP_reg21:
+ case DW_OP_reg22:
+ case DW_OP_reg23:
+ case DW_OP_reg24:
+ case DW_OP_reg25:
+ case DW_OP_reg26:
+ case DW_OP_reg27:
+ case DW_OP_reg28:
+ case DW_OP_reg29:
+ case DW_OP_reg30:
+ case DW_OP_reg31:
+
+ case DW_OP_lit0:
+ case DW_OP_lit1:
+ case DW_OP_lit2:
+ case DW_OP_lit3:
+ case DW_OP_lit4:
+ case DW_OP_lit5:
+ case DW_OP_lit6:
+ case DW_OP_lit7:
+ case DW_OP_lit8:
+ case DW_OP_lit9:
+ case DW_OP_lit10:
+ case DW_OP_lit11:
+ case DW_OP_lit12:
+ case DW_OP_lit13:
+ case DW_OP_lit14:
+ case DW_OP_lit15:
+ case DW_OP_lit16:
+ case DW_OP_lit17:
+ case DW_OP_lit18:
+ case DW_OP_lit19:
+ case DW_OP_lit20:
+ case DW_OP_lit21:
+ case DW_OP_lit22:
+ case DW_OP_lit23:
+ case DW_OP_lit24:
+ case DW_OP_lit25:
+ case DW_OP_lit26:
+ case DW_OP_lit27:
+ case DW_OP_lit28:
+ case DW_OP_lit29:
+ case DW_OP_lit30:
+ case DW_OP_lit31:
+
+ case DW_OP_dup:
+ case DW_OP_drop:
+
+ case DW_OP_over:
+
+ case DW_OP_swap:
+ case DW_OP_rot:
+ case DW_OP_xderef:
+
+ case DW_OP_abs:
+ case DW_OP_and:
+ case DW_OP_div:
+ case DW_OP_minus:
+ case DW_OP_mod:
+ case DW_OP_mul:
+ case DW_OP_neg:
+ case DW_OP_not:
+ case DW_OP_or:
+ case DW_OP_plus:
+
+ case DW_OP_shl:
+ case DW_OP_shr:
+ case DW_OP_shra:
+ case DW_OP_xor:
+
+ case DW_OP_eq:
+ case DW_OP_ge:
+ case DW_OP_gt:
+ case DW_OP_le:
+ case DW_OP_lt:
+ case DW_OP_ne:
+
+ case DW_OP_nop:
+ break;
+
+ /* Operations with 1-byte operands. */
+ case DW_OP_const1u:
+ case DW_OP_const1s:
+ case DW_OP_pick:
+ case DW_OP_deref_size:
+ case DW_OP_xderef_size:
+ p++;
+ break;
+
+ /* Operations with 2-byte operands. */
+ case DW_OP_const2u:
+ case DW_OP_const2s:
+ case DW_OP_bra:
+ case DW_OP_skip:
+ p += 2;
+ break;
+
+ /* Operations with 4-byte operands. */
+ case DW_OP_const4u:
+ case DW_OP_const4s:
+ p += 4;
+ break;
+
+ /* Operations with 8-byte operands. */
+ case DW_OP_const8u:
+ case DW_OP_const8s:
+ p += 8;
+ break;
+
+ /* Operations with an unsigned LEB128 operand. */
+ case DW_OP_constu:
+ case DW_OP_plus_uconst:
+ case DW_OP_regx:
+ case DW_OP_piece:
+ uval = dwarf_decode_sleb128(&p);
+ break;
+
+ /* Operations with a signed LEB128 operand. */
+ case DW_OP_consts:
+ case DW_OP_breg0:
+ case DW_OP_breg1:
+ case DW_OP_breg2:
+ case DW_OP_breg3:
+ case DW_OP_breg4:
+ case DW_OP_breg5:
+ case DW_OP_breg6:
+ case DW_OP_breg7:
+ case DW_OP_breg8:
+ case DW_OP_breg9:
+ case DW_OP_breg10:
+ case DW_OP_breg11:
+ case DW_OP_breg12:
+ case DW_OP_breg13:
+ case DW_OP_breg14:
+ case DW_OP_breg15:
+ case DW_OP_breg16:
+ case DW_OP_breg17:
+ case DW_OP_breg18:
+ case DW_OP_breg19:
+ case DW_OP_breg20:
+ case DW_OP_breg21:
+ case DW_OP_breg22:
+ case DW_OP_breg23:
+ case DW_OP_breg24:
+ case DW_OP_breg25:
+ case DW_OP_breg26:
+ case DW_OP_breg27:
+ case DW_OP_breg28:
+ case DW_OP_breg29:
+ case DW_OP_breg30:
+ case DW_OP_breg31:
+ case DW_OP_fbreg:
+ sval = dwarf_decode_sleb128(&p);
+ break;
+
+ /*
+ * Operations with an unsigned LEB128 operand
+ * followed by a signed LEB128 operand.
+ */
+ case DW_OP_bregx:
+ uval = dwarf_decode_uleb128(&p);
+ sval = dwarf_decode_sleb128(&p);
+ break;
+
+ /* Target address size operand. */
+ case DW_OP_addr:
+ p += pointer_size;
+ break;
+
+ /* All other operations cause an error. */
+ default:
+ count = -1;
+ break;
+ }
+ }
+
+ return count;
+}
+
+static int
+dwarf_loc_fill(Dwarf_Locdesc *lbuf, uint8_t pointer_size, uint8_t *p, int len)
+{
+ int count = 0;
+ int ret = DWARF_E_NONE;
+ uint64_t operand1;
+ uint64_t operand2;
+ uint8_t *last = p + len;
+
+ /*
+ * Process each byte. If an error occurs, then the
+ * count will be set to -1.
+ */
+ while (p < last && ret == DWARF_E_NONE) {
+ operand1 = 0;
+ operand2 = 0;
+
+ lbuf->ld_s[count].lr_atom = *p;
+
+ switch (*p++) {
+ /* Operations with no operands. */
+ case DW_OP_deref:
+ case DW_OP_reg0:
+ case DW_OP_reg1:
+ case DW_OP_reg2:
+ case DW_OP_reg3:
+ case DW_OP_reg4:
+ case DW_OP_reg5:
+ case DW_OP_reg6:
+ case DW_OP_reg7:
+ case DW_OP_reg8:
+ case DW_OP_reg9:
+ case DW_OP_reg10:
+ case DW_OP_reg11:
+ case DW_OP_reg12:
+ case DW_OP_reg13:
+ case DW_OP_reg14:
+ case DW_OP_reg15:
+ case DW_OP_reg16:
+ case DW_OP_reg17:
+ case DW_OP_reg18:
+ case DW_OP_reg19:
+ case DW_OP_reg20:
+ case DW_OP_reg21:
+ case DW_OP_reg22:
+ case DW_OP_reg23:
+ case DW_OP_reg24:
+ case DW_OP_reg25:
+ case DW_OP_reg26:
+ case DW_OP_reg27:
+ case DW_OP_reg28:
+ case DW_OP_reg29:
+ case DW_OP_reg30:
+ case DW_OP_reg31:
+
+ case DW_OP_lit0:
+ case DW_OP_lit1:
+ case DW_OP_lit2:
+ case DW_OP_lit3:
+ case DW_OP_lit4:
+ case DW_OP_lit5:
+ case DW_OP_lit6:
+ case DW_OP_lit7:
+ case DW_OP_lit8:
+ case DW_OP_lit9:
+ case DW_OP_lit10:
+ case DW_OP_lit11:
+ case DW_OP_lit12:
+ case DW_OP_lit13:
+ case DW_OP_lit14:
+ case DW_OP_lit15:
+ case DW_OP_lit16:
+ case DW_OP_lit17:
+ case DW_OP_lit18:
+ case DW_OP_lit19:
+ case DW_OP_lit20:
+ case DW_OP_lit21:
+ case DW_OP_lit22:
+ case DW_OP_lit23:
+ case DW_OP_lit24:
+ case DW_OP_lit25:
+ case DW_OP_lit26:
+ case DW_OP_lit27:
+ case DW_OP_lit28:
+ case DW_OP_lit29:
+ case DW_OP_lit30:
+ case DW_OP_lit31:
+
+ case DW_OP_dup:
+ case DW_OP_drop:
+
+ case DW_OP_over:
+
+ case DW_OP_swap:
+ case DW_OP_rot:
+ case DW_OP_xderef:
+
+ case DW_OP_abs:
+ case DW_OP_and:
+ case DW_OP_div:
+ case DW_OP_minus:
+ case DW_OP_mod:
+ case DW_OP_mul:
+ case DW_OP_neg:
+ case DW_OP_not:
+ case DW_OP_or:
+ case DW_OP_plus:
+
+ case DW_OP_shl:
+ case DW_OP_shr:
+ case DW_OP_shra:
+ case DW_OP_xor:
+
+ case DW_OP_eq:
+ case DW_OP_ge:
+ case DW_OP_gt:
+ case DW_OP_le:
+ case DW_OP_lt:
+ case DW_OP_ne:
+
+ case DW_OP_nop:
+ break;
+
+ /* Operations with 1-byte operands. */
+ case DW_OP_const1u:
+ case DW_OP_const1s:
+ case DW_OP_pick:
+ case DW_OP_deref_size:
+ case DW_OP_xderef_size:
+ operand1 = *p++;
+ break;
+
+ /* Operations with 2-byte operands. */
+ case DW_OP_const2u:
+ case DW_OP_const2s:
+ case DW_OP_bra:
+ case DW_OP_skip:
+ p += 2;
+ break;
+
+ /* Operations with 4-byte operands. */
+ case DW_OP_const4u:
+ case DW_OP_const4s:
+ p += 4;
+ break;
+
+ /* Operations with 8-byte operands. */
+ case DW_OP_const8u:
+ case DW_OP_const8s:
+ p += 8;
+ break;
+
+ /* Operations with an unsigned LEB128 operand. */
+ case DW_OP_constu:
+ case DW_OP_plus_uconst:
+ case DW_OP_regx:
+ case DW_OP_piece:
+ operand1 = dwarf_decode_sleb128(&p);
+ break;
+
+ /* Operations with a signed LEB128 operand. */
+ case DW_OP_consts:
+ case DW_OP_breg0:
+ case DW_OP_breg1:
+ case DW_OP_breg2:
+ case DW_OP_breg3:
+ case DW_OP_breg4:
+ case DW_OP_breg5:
+ case DW_OP_breg6:
+ case DW_OP_breg7:
+ case DW_OP_breg8:
+ case DW_OP_breg9:
+ case DW_OP_breg10:
+ case DW_OP_breg11:
+ case DW_OP_breg12:
+ case DW_OP_breg13:
+ case DW_OP_breg14:
+ case DW_OP_breg15:
+ case DW_OP_breg16:
+ case DW_OP_breg17:
+ case DW_OP_breg18:
+ case DW_OP_breg19:
+ case DW_OP_breg20:
+ case DW_OP_breg21:
+ case DW_OP_breg22:
+ case DW_OP_breg23:
+ case DW_OP_breg24:
+ case DW_OP_breg25:
+ case DW_OP_breg26:
+ case DW_OP_breg27:
+ case DW_OP_breg28:
+ case DW_OP_breg29:
+ case DW_OP_breg30:
+ case DW_OP_breg31:
+ case DW_OP_fbreg:
+ operand1 = dwarf_decode_sleb128(&p);
+ break;
+
+ /*
+ * Operations with an unsigned LEB128 operand
+ * followed by a signed LEB128 operand.
+ */
+ case DW_OP_bregx:
+ operand1 = dwarf_decode_uleb128(&p);
+ operand2 = dwarf_decode_sleb128(&p);
+ break;
+
+ /* Target address size operand. */
+ case DW_OP_addr:
+ p += pointer_size;
+ break;
+
+ /* All other operations cause an error. */
+ default:
+ break;
+ }
+
+ lbuf->ld_s[count].lr_number = operand1;
+ lbuf->ld_s[count].lr_number2 = operand2;
+
+ count++;
+ }
+
+ return ret;
+}
+
+int
+dwarf_locdesc(Dwarf_Die die, uint64_t attr, Dwarf_Locdesc **llbuf, Dwarf_Signed *lenp, Dwarf_Error *err)
+{
+ Dwarf_AttrValue av;
+ Dwarf_Locdesc *lbuf;
+ int num;
+ int ret = DWARF_E_NONE;
+
+ if (err == NULL)
+ return DWARF_E_ERROR;
+
+ if (die == NULL || llbuf == NULL || lenp == NULL) {
+ DWARF_SET_ERROR(err, DWARF_E_ARGUMENT);
+ return DWARF_E_ARGUMENT;
+ }
+
+ if ((av = dwarf_attrval_find(die, attr)) == NULL) {
+ DWARF_SET_ERROR(err, DWARF_E_NO_ENTRY);
+ ret = DWARF_E_NO_ENTRY;
+ } else if ((lbuf = calloc(sizeof(Dwarf_Locdesc), 1)) == NULL) {
+ DWARF_SET_ERROR(err, DWARF_E_MEMORY);
+ ret = DWARF_E_MEMORY;
+ } else {
+ *lenp = 0;
+ switch (av->av_form) {
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ /* Compute the number of locations: */
+ if ((num = dwarf_op_num(die->die_cu->cu_pointer_size,
+ av->u[1].u8p, av->u[0].u64)) < 0) {
+ DWARF_SET_ERROR(err, DWARF_E_INVALID_EXPR);
+ ret = DWARF_E_INVALID_EXPR;
+
+ /* Allocate an array of location structures. */
+ } else if ((lbuf->ld_s =
+ calloc(sizeof(Dwarf_Loc), num)) == NULL) {
+ DWARF_SET_ERROR(err, DWARF_E_MEMORY);
+ ret = DWARF_E_MEMORY;
+
+ /* Fill the array of location structures. */
+ } else if ((ret = dwarf_loc_fill(lbuf,
+ die->die_cu->cu_pointer_size,
+ av->u[1].u8p, av->u[0].u64)) != DWARF_E_NONE) {
+ free(lbuf->ld_s);
+ } else
+ /* Only one descriptor is returned. */
+ *lenp = 1;
+ break;
+ default:
+ printf("%s(%d): form %s not handled\n",__func__,
+ __LINE__,get_form_desc(av->av_form));
+ DWARF_SET_ERROR(err, DWARF_E_NOT_IMPLEMENTED);
+ ret = DWARF_E_ERROR;
+ }
+
+ if (ret == DWARF_E_NONE) {
+ *llbuf = lbuf;
+ } else
+ free(lbuf);
+ }
+
+ return ret;
+}
+
+int
+dwarf_locdesc_free(Dwarf_Locdesc *lbuf, Dwarf_Error *err)
+{
+ if (err == NULL)
+ return DWARF_E_ERROR;
+
+ if (lbuf == NULL) {
+ DWARF_SET_ERROR(err, DWARF_E_ARGUMENT);
+ return DWARF_E_ARGUMENT;
+ }
+
+ if (lbuf->ld_s != NULL)
+ free(lbuf->ld_s);
+
+ free(lbuf);
+
+ return DWARF_E_NONE;
+}
OpenPOWER on IntegriCloud