diff options
author | graydon <graydon> | 2005-07-29 01:47:56 +0000 |
---|---|---|
committer | graydon <graydon> | 2005-07-29 01:47:56 +0000 |
commit | 77de5e9eca19f041e85681439f41ea35d30a37c9 (patch) | |
tree | 0d4ec2f749ace9659df8f1082f1a8a8e2797e148 | |
parent | b2d5d95caa573e486b6503406b688da824cd20a9 (diff) | |
download | systemtap-steved-77de5e9eca19f041e85681439f41ea35d30a37c9.tar.gz systemtap-steved-77de5e9eca19f041e85681439f41ea35d30a37c9.tar.xz systemtap-steved-77de5e9eca19f041e85681439f41ea35d30a37c9.zip |
2005-07-28 Graydon Hoare <graydon@redhat.com>
* elaborate.cxx (derived_probe::derived_probe): Accept NULL probe.
* staptree.cxx (provide, require): Move from here...
* staptree.h: to here, and virtualize deep-copy methods.
* translate.cxx
(c_unparser::emit_common_header): Include loc2c-runtime.h
* tapsets.cxx
(dwflpp::iterate_over_modules): Use new, faster getmodules loop.
(dwflpp::literal_stmt_for_local): New method, call loc2c.
(var_expanding_copy_visitor): New struct which expands $-vars.
(dwarf_derived_probe): Rebuild this->body using var expander.
(query_function): Refactor logic a bit for speed.
* loc2c.{c,h}: Copies (with minor changes) of Roland's work
from elfutils CVS.
* Makefile.am (AM_CFLAGS): Set to elfutils-style.
(stap_SOURCES): Add loc2c.c.
* Makefile.in: Regenerate.
2005-07-28 Graydon Hoare <graydon@redhat.com>
* loc2c-runtime.h: New file from elfutils CVS.
-rw-r--r-- | ChangeLog | 19 | ||||
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | Makefile.in | 6 | ||||
-rw-r--r-- | elaborate.cxx | 16 | ||||
-rw-r--r-- | loc2c.c | 1398 | ||||
-rw-r--r-- | loc2c.h | 63 | ||||
-rw-r--r-- | runtime/ChangeLog | 4 | ||||
-rw-r--r-- | runtime/loc2c-runtime.h | 125 | ||||
-rw-r--r-- | staptree.cxx | 19 | ||||
-rw-r--r-- | staptree.h | 84 | ||||
-rw-r--r-- | tapsets.cxx | 338 | ||||
-rw-r--r-- | translate.cxx | 1 |
12 files changed, 1960 insertions, 117 deletions
@@ -1,3 +1,22 @@ +2005-07-28 Graydon Hoare <graydon@redhat.com> + + * elaborate.cxx (derived_probe::derived_probe): Accept NULL probe. + * staptree.cxx (provide, require): Move from here... + * staptree.h: to here, and virtualize deep-copy methods. + * translate.cxx + (c_unparser::emit_common_header): Include loc2c-runtime.h + * tapsets.cxx + (dwflpp::iterate_over_modules): Use new, faster getmodules loop. + (dwflpp::literal_stmt_for_local): New method, call loc2c. + (var_expanding_copy_visitor): New struct which expands $-vars. + (dwarf_derived_probe): Rebuild this->body using var expander. + (query_function): Refactor logic a bit for speed. + * loc2c.{c,h}: Copies (with minor changes) of Roland's work + from elfutils CVS. + * Makefile.am (AM_CFLAGS): Set to elfutils-style. + (stap_SOURCES): Add loc2c.c. + * Makefile.in: Regenerate. + 2005-07-28 Frank Ch. Eigler <fche@redhat.com> * stap.1: Beginnings of a man page. diff --git a/Makefile.am b/Makefile.am index 1a20caf1..b09cc3a8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,11 +6,13 @@ AM_MAKEFLAGS = 'CXXFLAGS=$(CXXFLAGS)' 'LDFLAGS=$(LDFLAGS)' AM_CPPFLAGS = -DPKGLIBDIR='"${libexecdir}/${PACKAGE}"' \ -DPKGDATADIR='"${datadir}/${PACKAGE}"' +AM_CFLAGS = -D_GNU_SOURCE -Wall -Werror -Wshadow -Wunused -Wformat=2 -Wextra -std=gnu99 + man_MANS = stap.1 bin_PROGRAMS = stap stap_SOURCES = main.cxx \ parse.cxx staptree.cxx elaborate.cxx translate.cxx \ - tapsets.cxx buildrun.cxx + tapsets.cxx buildrun.cxx loc2c.c stap_CXXFLAGS = -Werror $(AM_CXXFLAGS) libexec_PROGRAMS = stpd diff --git a/Makefile.in b/Makefile.in index 2bdf9aab..36cd6448 100644 --- a/Makefile.in +++ b/Makefile.in @@ -62,7 +62,7 @@ PROGRAMS = $(bin_PROGRAMS) $(libexec_PROGRAMS) am_stap_OBJECTS = stap-main.$(OBJEXT) stap-parse.$(OBJEXT) \ stap-staptree.$(OBJEXT) stap-elaborate.$(OBJEXT) \ stap-translate.$(OBJEXT) stap-tapsets.$(OBJEXT) \ - stap-buildrun.$(OBJEXT) + stap-buildrun.$(OBJEXT) loc2c.$(OBJEXT) stap_OBJECTS = $(am_stap_OBJECTS) stap_LDADD = $(LDADD) stap_DEPENDENCIES = @@ -189,10 +189,11 @@ AM_MAKEFLAGS = 'CXXFLAGS=$(CXXFLAGS)' 'LDFLAGS=$(LDFLAGS)' AM_CPPFLAGS = -DPKGLIBDIR='"${libexecdir}/${PACKAGE}"' \ -DPKGDATADIR='"${datadir}/${PACKAGE}"' +AM_CFLAGS = -D_GNU_SOURCE -Wall -Werror -Wshadow -Wunused -Wformat=2 -Wextra -std=gnu99 man_MANS = stap.1 stap_SOURCES = main.cxx \ parse.cxx staptree.cxx elaborate.cxx translate.cxx \ - tapsets.cxx buildrun.cxx + tapsets.cxx buildrun.cxx loc2c.c stap_CXXFLAGS = -Werror $(AM_CXXFLAGS) stpd_SOURCES = runtime/stpd/stpd.c runtime/stpd/librelay.c @@ -336,6 +337,7 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librelay.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/loc2c.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-buildrun.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-elaborate.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-main.Po@am__quote@ diff --git a/elaborate.cxx b/elaborate.cxx index ca5237df..76c5b39c 100644 --- a/elaborate.cxx +++ b/elaborate.cxx @@ -38,9 +38,12 @@ using namespace std; derived_probe::derived_probe (probe *p): base (p) { - this->locations = p->locations; - this->tok = p->tok; - this->body = deep_copy_visitor::deep_copy(p->body); + if (p) + { + this->locations = p->locations; + this->tok = p->tok; + this->body = deep_copy_visitor::deep_copy(p->body); + } } @@ -48,8 +51,11 @@ derived_probe::derived_probe (probe *p, probe_point *l): base (p) { this->locations.push_back (l); - this->tok = p->tok; - this->body = deep_copy_visitor::deep_copy(p->body); + if (p) + { + this->tok = p->tok; + this->body = deep_copy_visitor::deep_copy(p->body); + } } // ------------------------------------------------------------------------ diff --git a/loc2c.c b/loc2c.c new file mode 100644 index 00000000..86e7e82d --- /dev/null +++ b/loc2c.c @@ -0,0 +1,1398 @@ +#include <config.h> +#include <inttypes.h> +#include <stdbool.h> +#include <obstack.h> +#include <stdio.h> +#include <stdlib.h> +#include <error.h> +#include <dwarf.h> +#include <libdw.h> +#include <assert.h> + +#define _(x) x + +#define STACK_TYPE "intptr_t" /* Must be the signed type. */ +#define UTYPE "uintptr_t" /* Must be the unsigned type. */ +#define SFORMAT "%" PRId64 "L" +#define UFORMAT "%" PRIu64 "UL" +#define AFORMAT "%#" PRIx64 "UL" +#define STACKFMT "s%u" + +struct location +{ + struct location *next; + + const Dwarf_Loc *ops; + size_t nops; + + Dwarf_Word byte_size; + + enum { loc_address, loc_register, loc_noncontiguous, loc_final } type; + union + { + struct /* loc_address or loc_final */ + { + char *program; /* C fragment, leaves address in s0. */ + unsigned int stack_depth; /* Temporaries "s0..<N>" used by it. */ + struct location *frame_base; + bool used_deref; /* Program uses "deref" macro. */ + } address; + unsigned int regno; /* loc_register */ + struct location *pieces; /* loc_noncontiguous */ + }; +}; + + +static const char * +dwarf_diename_integrate (Dwarf_Die *die) +{ + Dwarf_Attribute attr_mem; + return dwarf_formstring (dwarf_attr_integrate (die, DW_AT_name, &attr_mem)); +} + +/* Synthesize a new loc_address using the program on the obstack. */ +static struct location * +new_synthetic_loc (struct obstack *pool, struct location *origin, bool deref) +{ + obstack_1grow (pool, '\0'); + char *program = obstack_finish (pool); + + struct location *loc = obstack_alloc (pool, sizeof *loc); + loc->next = NULL; + loc->byte_size = 0; + loc->type = loc_address; + loc->address.program = program; + loc->address.stack_depth = 0; + loc->address.frame_base = NULL; + loc->address.used_deref = deref; + + if (origin->type == loc_register) + { + loc->ops = origin->ops; + loc->nops = origin->nops; + } + else + { + loc->ops = NULL; + loc->nops = 0; + } + + return loc; +} + + +/* Die in the middle of an expression. */ +static struct location * +lose (const char *failure, const Dwarf_Loc *lexpr, size_t i) +{ + error (2, 0, _("%s in DWARF expression [%Zu] at %" PRIu64 + " (%#x: %" PRId64 ", %" PRId64 ")"), + failure, i, lexpr[i].offset, + lexpr[i].atom, lexpr[i].number, lexpr[i].number2); + return NULL; +} + +/* Translate a (constrained) DWARF expression into C code + emitted to the obstack POOL. INDENT is the number of indentation levels. + ADDRBIAS is the difference between runtime and Dwarf info addresses. + INPUT is null or an expression to be initially pushed on the stack. + If NEED_FB is null, fail on DW_OP_fbreg, else set *NEED_FB to true + and emit "frame_base" for it. On success, set *MAX_STACK to the number + of stack slots required. On failure, set *LOSER to the index in EXPR + of the operation we could not handle. + + Returns a failure message or null for success. */ + +static const char * +translate (struct obstack *pool, int indent, Dwarf_Addr addrbias, + const Dwarf_Loc *expr, const size_t len, + struct location *input, + bool *need_fb, size_t *loser, + struct location *loc) +{ + loc->ops = expr; + loc->nops = len; + +#define DIE(msg) return (*loser = i, _(msg)) + +#define emit(fmt, ...) obstack_printf (pool, fmt, ## __VA_ARGS__) + + unsigned int stack_depth = 0, max_stack = 0; + inline void deepen (void) + { + if (stack_depth == max_stack) + ++max_stack; + } + +#define POP(var) \ + if (stack_depth > 0) \ + --stack_depth; \ + else if (tos_register != -1) \ + fetch_tos_register (); \ + else \ + goto underflow; \ + int var = stack_depth +#define PUSH (deepen (), stack_depth++) +#define STACK(idx) (stack_depth - 1 - (idx)) + + /* Don't put stack operations in the arguments to this. */ +#define push(fmt, ...) \ + emit ("%*s" STACKFMT " = " fmt ";\n", indent * 2, "", PUSH, ## __VA_ARGS__) + + int tos_register = -1; + inline void fetch_tos_register (void) + { + deepen (); + emit ("%*s" STACKFMT " = fetch_register (%d);\n", + indent * 2, "", stack_depth, tos_register); + tos_register = -1; + } + + if (input != NULL) + switch (input->type) + { + case loc_address: + push ("addr"); + break; + + case loc_register: + tos_register = input->regno; + break; + + default: + abort (); + break; + } + + size_t i; + + inline const char *finish (struct location *piece) + { + if (stack_depth > 1) + DIE ("multiple values left on stack"); + if (stack_depth == 1) + { + obstack_1grow (pool, '\0'); + char *program = obstack_finish (pool); + piece->type = loc_address; + piece->address.program = program; + piece->address.stack_depth = max_stack; + piece->address.frame_base = NULL; + } + else if (tos_register == -1) + DIE ("stack underflow"); + else if (obstack_object_size (pool) != 0) + DIE ("register value must stand alone in location expression"); + else + { + piece->type = loc_register; + piece->regno = tos_register; + } + return NULL; + } + + struct location *pieces = NULL, **tailpiece = &pieces; + size_t piece_expr_start = 0; + for (i = 0; i < len; ++i) + { + unsigned int reg; + uint_fast8_t sp; + Dwarf_Word value; + + switch (expr[i].atom) + { + /* Basic stack operations. */ + case DW_OP_nop: + break; + + case DW_OP_dup: + if (stack_depth < 1) + goto underflow; + else + { + unsigned int tos = STACK (0); + push (STACKFMT, tos); + } + break; + + case DW_OP_drop: + POP (ignore); + emit ("%*s/* drop " STACKFMT "*/\n", indent * 2, "", ignore); + break; + + case DW_OP_pick: + sp = expr[i].number; + op_pick: + if (sp >= stack_depth) + goto underflow; + sp = STACK (sp); + push (STACKFMT, sp); + break; + + case DW_OP_over: + sp = 1; + goto op_pick; + + case DW_OP_swap: + if (stack_depth < 2) + goto underflow; + deepen (); /* Use a temporary slot. */ + emit ("%*s" + STACKFMT " = " STACKFMT ", " + STACKFMT " = " STACKFMT ", " + STACKFMT " = " STACKFMT ";\n", + indent * 2, "", + STACK (-1), STACK (0), + STACK (0), STACK (1), + STACK (1), STACK (-1)); + break; + + case DW_OP_rot: + if (stack_depth < 3) + goto underflow; + deepen (); /* Use a temporary slot. */ + emit ("%*s" + STACKFMT " = " STACKFMT ", " + STACKFMT " = " STACKFMT ", " + STACKFMT " = " STACKFMT ", " + STACKFMT " = " STACKFMT ";\n", + indent * 2, "", + STACK (-1), STACK (0), + STACK (0), STACK (1), + STACK (1), STACK (2), + STACK (3), STACK (-1)); + break; + + + /* Control flow operations. */ + case DW_OP_skip: + { + Dwarf_Off target = expr[i].offset + 3 + expr[i].number; + while (i + 1 < len && expr[i + 1].offset < target) + ++i; + if (expr[i + 1].offset != target) + DIE ("invalid skip target"); + break; + } + + case DW_OP_bra: + DIE ("conditional branches not supported"); + break; + + + /* Memory access. */ + case DW_OP_deref: + { + POP (addr); + push ("deref (sizeof (void *), " STACKFMT ")", addr); + loc->address.used_deref = true; + } + break; + + case DW_OP_deref_size: + { + POP (addr); + push ("deref (" UFORMAT ", " STACKFMT ")", + expr[i].number, addr); + loc->address.used_deref = true; + } + break; + + case DW_OP_xderef: + { + POP (addr); + POP (as); + push ("xderef (sizeof (void *), " STACKFMT ", " STACKFMT ")", + addr, as); + loc->address.used_deref = true; + } + break; + + case DW_OP_xderef_size: + { + POP (addr); + POP (as); + push ("xderef (" UFORMAT ", " STACKFMT ", " STACKFMT ")", + expr[i].number, addr, as); + loc->address.used_deref = true; + } + break; + + /* Constant-value operations. */ + + case DW_OP_addr: + push (AFORMAT, addrbias + expr[i].number); + break; + + case DW_OP_lit0 ... DW_OP_lit31: + value = expr[i].atom - DW_OP_lit0; + goto op_const; + + case DW_OP_const1u: + case DW_OP_const1s: + case DW_OP_const2u: + case DW_OP_const2s: + case DW_OP_const4u: + case DW_OP_const4s: + case DW_OP_const8u: + case DW_OP_const8s: + case DW_OP_constu: + case DW_OP_consts: + value = expr[i].number; + op_const: + push (SFORMAT, value); + break; + + /* Arithmetic operations. */ +#define UNOP(dw_op, c_op) \ + case DW_OP_##dw_op: \ + { \ + POP (tos); \ + push ("%s (" STACKFMT ")", #c_op, tos); \ + } \ + break +#define BINOP(dw_op, c_op) \ + case DW_OP_##dw_op: \ + { \ + POP (b); \ + POP (a); \ + push (STACKFMT " %s " STACKFMT, a, #c_op, b); \ + } \ + break + + UNOP (abs, op_abs); + BINOP (and, &); + BINOP (div, /); + BINOP (minus, -); + BINOP (mod, %); + BINOP (mul, *); + UNOP (neg, -); + UNOP (not, ~); + BINOP (or, |); + BINOP (plus, +); + BINOP (shl, <<); + BINOP (shra, >>); + BINOP (xor, ^); + + /* Comparisons are binary operators too. */ + BINOP (le, <=); + BINOP (ge, >=); + BINOP (eq, ==); + BINOP (lt, <); + BINOP (gt, >); + BINOP (ne, !=); + +#undef UNOP +#undef BINOP + + case DW_OP_shr: + { + POP (b); + POP (a); + push ("(%s) " STACKFMT " >> (%s)" STACKFMT, + UTYPE, a, UTYPE, b); + break; + } + + case DW_OP_plus_uconst: + { + POP (x); + push (STACKFMT " + " UFORMAT, x, expr[i].number); + } + break; + + + /* Register-relative addressing. */ + case DW_OP_breg0 ... DW_OP_breg31: + reg = expr[i].atom - DW_OP_breg0; + value = expr[i].number; + goto op_breg; + + case DW_OP_bregx: + reg = expr[i].number; + value = expr[i].number2; + op_breg: + push ("fetch_register (%u) + " SFORMAT, reg, value); + break; + + case DW_OP_fbreg: + if (need_fb == NULL) + DIE ("DW_OP_fbreg from DW_AT_frame_base"); + *need_fb = true; + push ("frame_base + " SFORMAT, expr[i].number); + break; + + /* Direct register contents. */ + case DW_OP_reg0 ... DW_OP_reg31: + reg = expr[i].atom - DW_OP_reg0; + goto op_reg; + + case DW_OP_regx: + reg = expr[i].number; + op_reg: + tos_register = reg; + break; + + /* Special magic. */ + case DW_OP_piece: + if (stack_depth > 1) + /* If this ever happens we could copy the program. */ + DIE ("DW_OP_piece left multiple values on stack"); + else + { + struct location *piece = obstack_alloc (pool, sizeof *piece); + const char *failure = finish (piece); + if (failure != NULL) + return failure; + + piece->ops = &expr[piece_expr_start]; + piece->nops = i - piece_expr_start; + piece_expr_start = i + 1; + + piece->byte_size = expr[i].number; + + *tailpiece = piece; + tailpiece = &piece->next; + piece->next = NULL; + } + break; + + case DW_OP_push_object_address: + DIE ("XXX DW_OP_push_object_address"); + break; + + default: + DIE ("unrecognized operation"); + break; + } + } + + if (pieces == NULL) + return finish (loc); + + if (piece_expr_start != i) + DIE ("extra operations after last DW_OP_piece"); + + loc->type = loc_noncontiguous; + loc->pieces = pieces; + + return NULL; + + underflow: + DIE ("stack underflow"); + +#undef emit +#undef push +#undef PUSH +#undef POP +#undef STACK +#undef DIE +} + +/* Translate a location starting from an address or nothing. */ +static struct location * +location_from_address (struct obstack *pool, + int indent, Dwarf_Addr dwbias, + const Dwarf_Loc *expr, size_t len, Dwarf_Addr address, + struct location **input, Dwarf_Attribute *fb_attr) +{ + bool need_fb = false; + size_t loser; + struct location *loc = obstack_alloc (pool, sizeof *loc); + const char *failure = translate (pool, indent + 1, dwbias, expr, len, + *input, &need_fb, &loser, loc); + if (failure != NULL) + return lose (failure, expr, loser); + + loc->next = NULL; + if (need_fb) + { + /* The main expression uses DW_OP_fbreg, so we need to compute + the DW_AT_frame_base attribute expression's value first. */ + + Dwarf_Loc *fb_expr; + size_t fb_len; + switch (dwarf_addrloclists (fb_attr, address - dwbias, + &fb_expr, &fb_len, 1)) + { + case 1: /* Should always happen. */ + if (fb_len == 0) + goto fb_inaccessible; + break; + + default: /* Shouldn't happen. */ + case -1: + error (2, 0, "dwarf_addrloclists (form %#x): %s", + dwarf_whatform (fb_attr), dwarf_errmsg (-1)); + return NULL; + + case 0: /* Shouldn't happen. */ + fb_inaccessible: + error (2, 0, "DW_AT_frame_base not accessible at this address"); + return NULL; + } + + loc->address.frame_base = obstack_alloc (pool, sizeof *loc); + failure = translate (pool, indent + 1, dwbias, fb_expr, fb_len, NULL, + NULL, &loser, loc->address.frame_base); + if (failure != NULL) + return lose (failure, fb_expr, loser); + } + + if (*input != NULL) + (*input)->next = loc; + *input = loc; + + return loc; +} + +/* Translate a location starting from a non-address "on the top of the + stack". The *INPUT location is a register name or noncontiguous + object specification, and this expression wants to find the "address" + of an object relative to that "address". */ + +static struct location * +location_relative (struct obstack *pool, + int indent, Dwarf_Addr dwbias, + const Dwarf_Loc *expr, size_t len, Dwarf_Addr address, + struct location **input, Dwarf_Attribute *fb_attr) +{ + Dwarf_Sword *stack; + unsigned int stack_depth = 0, max_stack = 0; + inline void deepen (void) + { + if (stack_depth == max_stack) + { + ++max_stack; + obstack_blank (pool, sizeof stack[0]); + stack = (void *) obstack_base (pool); + } + } + +#define POP(var) \ + if (stack_depth > 0) \ + --stack_depth; \ + else \ + goto underflow; \ + int var = stack_depth +#define PUSH (deepen (), stack_depth++) +#define STACK(idx) (stack_depth - 1 - (idx)) +#define STACKWORD(idx) stack[STACK (idx)] + + /* Don't put stack operations in the arguments to this. */ +#define push(value) (stack[PUSH] = (value)) + + const char *failure = NULL; +#define DIE(msg) do { failure = _(msg); goto fail; } while (0) + + struct location *head = NULL; + size_t i; + for (i = 0; i < len; ++i) + { + uint_fast8_t sp; + Dwarf_Word value; + + switch (expr[i].atom) + { + /* Basic stack operations. */ + case DW_OP_nop: + break; + + case DW_OP_dup: + if (stack_depth < 1) + goto underflow; + else + { + unsigned int tos = STACK (0); + push (stack[tos]); + } + break; + + case DW_OP_drop: + if (stack_depth > 0) + --stack_depth; + else if (*input != NULL) + /* Mark that we have consumed the input. */ + *input = NULL; + else + /* Hits if cleared above, or if we had no input at all. */ + goto underflow; + break; + + case DW_OP_pick: + sp = expr[i].number; + op_pick: + if (sp >= stack_depth) + goto underflow; + sp = STACK (sp); + push (stack[sp]); + break; + + case DW_OP_over: + sp = 1; + goto op_pick; + + case DW_OP_swap: + if (stack_depth < 2) + goto underflow; + deepen (); /* Use a temporary slot. */ + STACKWORD (-1) = STACKWORD (0); + STACKWORD (0) = STACKWORD (1); + STACKWORD (1) = STACKWORD (-1); + break; + + case DW_OP_rot: + if (stack_depth < 3) + goto underflow; + deepen (); /* Use a temporary slot. */ + STACKWORD (-1) = STACKWORD (0); + STACKWORD (0) = STACKWORD (1); + STACKWORD (2) = STACKWORD (2); + STACKWORD (2) = STACKWORD (-1); + break; + + + /* Control flow operations. */ + case DW_OP_bra: + { + POP (taken); + if (stack[taken] == 0) + break; + } + /*FALLTHROUGH*/ + + case DW_OP_skip: + { + Dwarf_Off target = expr[i].offset + 3 + expr[i].number; + while (i + 1 < len && expr[i + 1].offset < target) + ++i; + if (expr[i + 1].offset != target) + DIE ("invalid skip target"); + break; + } + + /* Memory access. */ + case DW_OP_deref: + case DW_OP_deref_size: + case DW_OP_xderef: + case DW_OP_xderef_size: + + /* Register-relative addressing. */ + case DW_OP_breg0 ... DW_OP_breg31: + case DW_OP_bregx: + case DW_OP_fbreg: + + /* This started from a register, but now it's following a pointer. + So we can do the translation starting from address here. */ + return location_from_address (pool, indent, dwbias, + expr, len, address, input, fb_attr); + + + /* Constant-value operations. */ + case DW_OP_addr: + push (dwbias + expr[i].number); + break; + + case DW_OP_lit0 ... DW_OP_lit31: + value = expr[i].atom - DW_OP_lit0; + goto op_const; + + case DW_OP_const1u: + case DW_OP_const1s: + case DW_OP_const2u: + case DW_OP_const2s: + case DW_OP_const4u: + case DW_OP_const4s: + case DW_OP_const8u: + case DW_OP_const8s: + case DW_OP_constu: + case DW_OP_consts: + value = expr[i].number; + op_const: + push (value); + break; + + /* Arithmetic operations. */ +#define UNOP(dw_op, c_op) \ + case DW_OP_##dw_op: \ + { \ + POP (tos); \ + push (c_op (stack[tos])); \ + } \ + break +#define BINOP(dw_op, c_op) \ + case DW_OP_##dw_op: \ + { \ + POP (b); \ + POP (a); \ + push (stack[a] c_op stack[b]); \ + } \ + break + +#define op_abs(x) (x < 0 ? -x : x) + UNOP (abs, op_abs); + BINOP (and, &); + BINOP (div, /); + BINOP (mod, %); + BINOP (mul, *); + UNOP (neg, -); + UNOP (not, ~); + BINOP (or, |); + BINOP (shl, <<); + BINOP (shra, >>); + BINOP (xor, ^); + + /* Comparisons are binary operators too. */ + BINOP (le, <=); + BINOP (ge, >=); + BINOP (eq, ==); + BINOP (lt, <); + BINOP (gt, >); + BINOP (ne, !=); + +#undef UNOP +#undef BINOP + + case DW_OP_shr: + { + POP (b); + POP (a); + push ((Dwarf_Word) stack[a] >> (Dwarf_Word) stack[b]); + break; + } + + /* Simple addition we may be able to handle relative to + the starting register name. */ + case DW_OP_minus: + { + POP (tos); + value = -stack[tos]; + goto plus; + } + case DW_OP_plus: + { + POP (tos); + value = stack[tos]; + goto plus; + } + case DW_OP_plus_uconst: + value = expr[i].number; + plus: + if (stack_depth > 0) + { + /* It's just private diddling after all. */ + POP (a); + push (stack[a] + value); + break; + } + if (*input == NULL) + goto underflow; + + /* This is the primary real-world case: the expression takes + the input address and adds a constant offset. */ + + while ((*input)->type == loc_noncontiguous) + { + /* We are starting from a noncontiguous object (DW_OP_piece). + Find the piece we want. */ + + struct location *piece = (*input)->pieces; + while (piece != NULL && value >= piece->byte_size) + { + value -= piece->byte_size; + piece = piece->next; + } + if (piece == NULL) + DIE ("offset outside available pieces"); + + *input = piece; + } + + switch ((*input)->type) + { + case loc_address: + { + /* The piece we want is actually in memory. Use the same + program to compute the address from the preceding input. */ + + struct location *loc = obstack_alloc (pool, sizeof *loc); + *loc = **input; + if (head == NULL) + head = loc; + (*input)->next = loc; + if (value == 0) + { + /* The piece addresses exactly where we want to go. */ + loc->next = NULL; + *input = loc; + } + else + { + /* Add a second fragment to offset the piece address. */ + obstack_printf (pool, "%*saddr += " SFORMAT "\n", + indent * 2, "", value); + *input = loc->next = new_synthetic_loc (pool, *input, + false); + } + + if (i + 1 < len) + { + /* This expression keeps going, but further + computations now have an address to start with. + So we can punt to the address computation generator. */ + loc = location_from_address (pool, indent, dwbias, + &expr[i + 1], len - i - 1, + address, input, fb_attr); + if (loc == NULL) + return NULL; + } + + /* That's all she wrote. */ + return head; + } + + case loc_register: + // XXX + + default: + abort (); + } + break; + + /* Direct register contents. */ + case DW_OP_reg0 ... DW_OP_reg31: + case DW_OP_regx: + DIE ("register"); + break; + + /* Special magic. */ + case DW_OP_piece: + DIE ("DW_OP_piece"); + break; + + case DW_OP_push_object_address: + DIE ("XXX DW_OP_push_object_address"); + break; + + default: + DIE ("unrecognized operation"); + break; + } + } + + if (stack_depth > 1) + DIE ("multiple values left on stack"); + + if (stack_depth > 0) /* stack_depth == 1 */ + { + if (*input != NULL) + DIE ("multiple values left on stack"); + + /* Could handle this if it ever actually happened. */ + DIE ("relative expression computed constant"); + } + + return head; + + underflow: + if (*input == NULL) + DIE ("stack underflow"); + else + DIE ("cannot handle location expression"); + + fail: + return lose (failure, expr, i); +} + + +/* Translate a C fragment for the location expression, using *INPUT + as the starting location, begin from scratch if *INPUT is null. + If DW_OP_fbreg is used, it may have a subfragment computing from + the FB_ATTR location expression. + + On errors, exit and never return (XXX ?). On success, return the + first fragment created, which is also chained onto (*INPUT)->next. + *INPUT is then updated with the new tail of that chain. */ + +struct location * +c_translate_location (struct obstack *pool, + int indent, Dwarf_Addr dwbias, + Dwarf_Attribute *loc_attr, Dwarf_Addr address, + struct location **input, Dwarf_Attribute *fb_attr) +{ + Dwarf_Loc *expr; + size_t len; + switch (dwarf_addrloclists (loc_attr, address - dwbias, &expr, &len, 1)) + { + case 1: /* Should always happen. */ + if (len == 0) + goto inaccessible; + break; + + default: /* Shouldn't happen. */ + case -1: + error (2, 0, "dwarf_addrloclists (form %#x): %s", + dwarf_whatform (fb_attr), dwarf_errmsg (-1)); + return NULL; + + case 0: /* Shouldn't happen. */ + inaccessible: + error (2, 0, "not accessible at this address"); + return NULL; + } + + ++indent; + switch (*input == NULL ? loc_address : (*input)->type) + { + case loc_address: + /* We have a previous address computation. + This expression will compute starting with that on the stack. */ + return location_from_address (pool, indent, dwbias, expr, len, address, + input, fb_attr); + + case loc_noncontiguous: + case loc_register: + /* The starting point is not an address computation, but a + register. We can only handle limited computations from here. */ + return location_relative (pool, indent, dwbias, expr, len, address, + input, fb_attr); + + case loc_final: /* Bogus caller. */ + default: + abort (); + break; + } + + return NULL; +} + + +/* Emit "uintNN_t TARGET = ...;". */ +static bool +emit_base_fetch (struct obstack *pool, Dwarf_Word byte_size, + const char *target, bool decl, struct location *loc) +{ + if (decl) + obstack_printf (pool, "uint%" PRIu64 "_t ", byte_size * 8); + + switch (loc->type) + { + case loc_address: + if (byte_size != 0 && byte_size != (Dwarf_Word) -1) + obstack_printf (pool, "%s = deref (%" PRIu64 ", addr); ", + target, byte_size); + else + obstack_printf (pool, "%s = deref (sizeof %s, addr); ", + target, target); + return true; + + case loc_register: + obstack_printf (pool, "%s = fetch_register (%u);", target, loc->regno); + break; + + case loc_noncontiguous: + /* Could be handled if it ever happened. */ + error (2, 0, _("noncontiguous locations not supported")); + break; + + default: + abort (); + break; + } + + return false; +} + +/* Translate a fragment to dereference the given pointer type, + where *INPUT is the location of the pointer with that type. + + We chain on a loc_address program that yields this pointer value + (i.e. the location of what it points to). */ + +void +c_translate_pointer (struct obstack *pool, int indent, + Dwarf_Addr dwbias __attribute__ ((unused)), + Dwarf_Die *typedie, struct location **input) +{ + obstack_printf (pool, "%*s{ ", (indent + 2) * 2, ""); + + bool deref = false; + Dwarf_Attribute attr_mem; + Dwarf_Word byte_size; + if (dwarf_attr_integrate (typedie, DW_AT_byte_size, &attr_mem) == NULL) + { + obstack_printf (pool, "uintptr_t "); + emit_base_fetch (pool, 0, "tmp", false, *input); + } + else if (dwarf_formudata (&attr_mem, &byte_size) != 0) + error (2, 0, + _("cannot get byte_size attribute for type %s: %s"), + dwarf_diename_integrate (typedie) ?: "<anonymous>", + dwarf_errmsg (-1)); + else + deref = emit_base_fetch (pool, byte_size, "tmp", true, *input); + + obstack_printf (pool, " addr = tmp; }\n"); + + struct location *loc = new_synthetic_loc (pool, *input, deref); + (*input)->next = loc; + *input = loc; +} + +/* Determine the byte size of a base type. */ +static Dwarf_Word +base_byte_size (Dwarf_Die *typedie) +{ + Dwarf_Attribute attr_mem; + Dwarf_Word size; + if (dwarf_attr_integrate (typedie, DW_AT_byte_size, &attr_mem) != NULL + && dwarf_formudata (&attr_mem, &size) == 0) + return size; + + error (2, 0, + _("cannot get byte_size attribute for type %s: %s"), + dwarf_diename_integrate (typedie) ?: "<anonymous>", + dwarf_errmsg (-1)); + return -1; +} + +/* Emit a code fragment like: + { uintNN_t tmp = ...; S1 S2 S3, tmp, B0, Bn); } +*/ +static void +emit_bitfield (struct obstack *pool, int indent, + Dwarf_Die *die, Dwarf_Word byte_size, struct location *loc, + const char *s1, const char *s2, const char *s3) +{ + Dwarf_Word bit_offset, bit_size; + Dwarf_Attribute attr_mem; + if (dwarf_attr_integrate (die, DW_AT_bit_offset, &attr_mem) == NULL + || dwarf_formudata (&attr_mem, &bit_offset) != 0 + || dwarf_attr_integrate (die, DW_AT_bit_size, &attr_mem) == NULL + || dwarf_formudata (&attr_mem, &bit_size) != 0) + error (2, 0, _("cannot get bit field parameters: %s"), + dwarf_errmsg (-1)); + + /* Emit "{ uintNN_t tmp = ...;" to fetch the base type. */ + + obstack_printf (pool, "%*s{ ", indent * 2, ""); + emit_base_fetch (pool, byte_size, "tmp", true, loc); + + obstack_printf (pool, "%s%s%s, tmp, %" PRIu64 ", %" PRIu64 "); }\n", + s1, s2, s3, bit_offset, bit_size); +} + +/* Translate a fragment to fetch the value of variable or member DIE + at the *INPUT location and store it in variable TARGET. */ + +void +c_translate_fetch (struct obstack *pool, int indent, + Dwarf_Addr dwbias __attribute__ ((unused)), + Dwarf_Die *die, Dwarf_Attribute *typeattr, + struct location **input, const char *target) +{ + ++indent; + + Dwarf_Attribute size_attr; + Dwarf_Word byte_size; + if (dwarf_attr_integrate (die, DW_AT_byte_size, &size_attr) == NULL + || dwarf_formudata (&size_attr, &byte_size) != 0) + { + Dwarf_Die basedie; + if (dwarf_formref_die (typeattr, &basedie) == NULL) + error (2, 0, _("cannot get type of field: %s"), dwarf_errmsg (-1)); + byte_size = base_byte_size (&basedie); + } + + bool deref = false; + if (dwarf_hasattr_integrate (die, DW_AT_bit_offset)) + /* This is a bit field. */ + emit_bitfield (pool, indent, die, byte_size, *input, + "fetch_bitfield (", target, ""); + else + switch (byte_size) + { + case 1: + case 2: + case 4: + case 8: + obstack_printf (pool, "%*s", indent * 2, ""); + deref = emit_base_fetch (pool, byte_size, target, false, *input); + obstack_printf (pool, "\n"); + break; + + default: + /* Could handle this generating call to memcpy equivalent. */ + error (2, 0, _("fetch is larger than base integer types")); + break; + } + + struct location *loc = new_synthetic_loc (pool, *input, deref); + loc->type = loc_final; + (*input)->next = loc; + *input = loc; +} + +void +c_translate_addressof (struct obstack *pool, int indent, + Dwarf_Addr dwbias __attribute__ ((unused)), + Dwarf_Die *die, + Dwarf_Attribute *typeattr __attribute__ ((unused)), + struct location **input, const char *target) +{ + ++indent; + + if (dwarf_hasattr_integrate (die, DW_AT_bit_offset)) + error (2, 0, _("cannot take the address of a bit field")); + + switch ((*input)->type) + { + case loc_address: + obstack_printf (pool, "%*s%s = addr;\n", indent * 2, "", target); + (*input)->next = new_synthetic_loc (pool, *input, false); + (*input)->next->type = loc_final; + break; + + case loc_register: + error (2, 0, _("cannot take address of object in register")); + break; + case loc_noncontiguous: + error (2, 0, _("cannot take address of noncontiguous object")); + break; + + default: + abort(); + break; + } +} + + +/* Determine the element stride of an array type. */ +static Dwarf_Word +array_stride (Dwarf_Die *typedie) +{ + Dwarf_Attribute attr_mem; + if (dwarf_attr_integrate (typedie, DW_AT_stride_size, &attr_mem) != NULL) + { + Dwarf_Word stride; + if (dwarf_formudata (&attr_mem, &stride) == 0) + return stride; + error (2, 0, _("cannot get stride_size attribute array type %s: %s"), + dwarf_diename_integrate (typedie) ?: "<anonymous>", + dwarf_errmsg (-1)); + } + + Dwarf_Die die_mem; + if (dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem) == NULL + || dwarf_formref_die (&attr_mem, &die_mem) == NULL) + error (2, 0, _("cannot get element type of array type %s: %s"), + dwarf_diename_integrate (typedie) ?: "<anonymous>", + dwarf_errmsg (-1)); + + if (dwarf_attr_integrate (&die_mem, DW_AT_byte_size, &attr_mem) != NULL) + { + Dwarf_Word stride; + if (dwarf_formudata (&attr_mem, &stride) == 0) + return stride; + error (2, 0, + _("cannot get byte_size attribute for array element type %s: %s"), + dwarf_diename_integrate (&die_mem) ?: "<anonymous>", + dwarf_errmsg (-1)); + } + + error (2, 0, _("confused about array element size")); + return 0; +} + +void +c_translate_array (struct obstack *pool, int indent, + Dwarf_Addr dwbias __attribute__ ((unused)), + Dwarf_Die *typedie, struct location **input, + const char *idx, Dwarf_Word const_idx) +{ + ++indent; + + Dwarf_Word stride = array_stride (typedie); + + struct location *loc = *input; + while (loc->type == loc_noncontiguous) + { + if (idx != NULL) + error (2, 0, _("cannot dynamically index noncontiguous array")); + else + { + Dwarf_Word offset = const_idx * stride; + struct location *piece = loc->pieces; + while (piece != NULL && offset >= piece->byte_size) + { + offset -= piece->byte_size; + piece = piece->next; + } + if (piece == NULL) + error (2, 0, _("constant index is outside noncontiguous array")); + if (offset % stride != 0) + error (2, 0, _("noncontiguous array splits elements")); + const_idx = offset / stride; + loc = piece; + } + } + + switch (loc->type) + { + case loc_address: + ++indent; + if (idx != NULL) + obstack_printf (pool, "%*saddr += %s * " UFORMAT ";\n", + indent * 2, "", idx, stride); + else + obstack_printf (pool, "%*saddr += " UFORMAT " * " UFORMAT ";\n", + indent * 2, "", const_idx, stride); + loc = new_synthetic_loc (pool, loc, false); + break; + + case loc_register: + error (2, 0, _("cannot index array stored in a register")); + break; + + default: + abort(); + break; + } + + (*input)->next = loc; + *input = (*input)->next; +} + + +/* Emitting C code for finalized fragments. */ + + +#define emit(fmt, ...) fprintf (out, fmt, ## __VA_ARGS__) + +/* Open a block with a comment giving the original DWARF expression. */ +static void +emit_header (FILE *out, struct location *loc, unsigned int hindent) +{ + if (loc->ops == NULL) + emit ("%*s{ // synthesized\n", hindent * 2, ""); + else + { + emit ("%*s{ // DWARF expression:", hindent * 2, ""); + for (size_t i = 0; i < loc->nops; ++i) + { + emit (" %#x", loc->ops[i].atom); + if (loc->ops[i].number2 == 0) + { + if (loc->ops[i].number != 0) + emit ("(%" PRId64 ")", loc->ops[i].number); + } + else + emit ("(%" PRId64 ",%" PRId64 ")", + loc->ops[i].number, loc->ops[i].number2); + } + emit ("\n"); + } +} + +/* Emit a code fragment to assign the target variable to a register value. */ +static void +emit_loc_register (FILE *out, struct location *loc, unsigned int indent, + const char *target) +{ + assert (loc->type == loc_register); + + emit ("%*s%s = fetch_register (%u);\n", + indent * 2, "", target, loc->regno); +} + +/* Emit a code fragment to assign the target variable to an address. */ +static void +emit_loc_address (FILE *out, struct location *loc, unsigned int indent, + const char *target) +{ + assert (loc->type == loc_address); + + if (loc->address.stack_depth == 0) + /* Synthetic program. */ + emit ("%s", loc->address.program); + else + { + emit ("%*s%s " STACKFMT, (indent + 1) * 2, "", STACK_TYPE, 0); + for (unsigned int i = 1; i < loc->address.stack_depth; ++i) + emit (", " STACKFMT, i); + emit (";\n"); + + emit ("%s%*s%s = " STACKFMT ";\n", loc->address.program, + (indent + 1) * 2, "", target, 0); + } +} + +/* Emit a code fragment to declare the target variable and + assign it to an address-sized value. */ +static void +emit_loc_value (FILE *out, struct location *loc, unsigned int indent, + const char *target, bool declare) +{ + if (declare) + emit ("%*s%s %s;\n", indent * 2, "", STACK_TYPE, target); + + emit_header (out, loc, indent); + + switch (loc->type) + { + default: + abort (); + break; + + case loc_register: + emit_loc_register (out, loc, indent, target); + break; + + case loc_address: + if (loc->address.frame_base != NULL) + emit_loc_value (out, loc->address.frame_base, indent, + "frame_base", true); + emit_loc_address (out, loc, indent, target); + break; + } + + emit ("%*s}\n", indent * 2, ""); +} + +bool +c_emit_location (FILE *out, struct location *loc, int indent) +{ + emit ("%*s{\n", indent * 2, ""); + + bool deref = false; + for (bool declare_addr = true; loc->next != NULL; loc = loc->next) + switch (loc->type) + { + case loc_address: + /* Emit the program fragment to calculate the address. */ + emit_loc_value (out, loc, indent + 1, "addr", declare_addr); + declare_addr = false; + deref = deref || loc->address.used_deref; + break; + + case loc_register: + case loc_noncontiguous: + /* These don't produce any code directly. + The next address/final record incorporates the value. */ + break; + + case loc_final: /* Should be last in chain! */ + default: + abort (); + break; + } + + if (loc->type != loc_final) /* Unfinished chain. */ + abort (); + + emit ("%s%*s}\n", loc->address.program, indent * 2, ""); + + return deref; +} + +#undef emit diff --git a/loc2c.h b/loc2c.h new file mode 100644 index 00000000..15d89280 --- /dev/null +++ b/loc2c.h @@ -0,0 +1,63 @@ +#include <libdw.h> + +struct obstack; /* Use <obstack.h> */ +struct location; /* Opaque */ + + +/* Translate a C fragment for the location expression, using *INPUT + as the starting location, begin from scratch if *INPUT is null. + If DW_OP_fbreg is used, it may have a subfragment computing from + the FB_ATTR location expression. + + On errors, exit and never return (XXX ?). On success, return the + first fragment created, which is also chained onto (*INPUT)->next. + *INPUT is then updated with the new tail of that chain. + *USED_DEREF is set to true iff the "deref" runtime operation + was used, otherwise it is not modified. */ +struct location *c_translate_location (struct obstack *, int indent, + Dwarf_Addr bias, + Dwarf_Attribute *loc_attr, + Dwarf_Addr address, + struct location **input, + Dwarf_Attribute *fb_attr); + +/* Translate a fragment to dereference the given pointer type, + where *INPUT is the location of the pointer with that type. */ +void c_translate_pointer (struct obstack *pool, int indent, + Dwarf_Addr dwbias, Dwarf_Die *typedie, + struct location **input); + +/* Translate a fragment to index an array (turning the location + of the array into the location of an element). If IDX is non-null, + it's a string of C code to emit in the fragment as the array index. + If the index is a known constant, IDX should be null and CONST_IDX + is used instead (this case can handle local arrays in registers). */ +void c_translate_array (struct obstack *pool, int indent, + Dwarf_Addr dwbias, Dwarf_Die *typedie, + struct location **input, + const char *idx, Dwarf_Word const_idx); + +/* Translate a fragment to compute the address of the input location + and assign it to the variable TARGET. This doesn't really do anything + (it always emits "TARGET = addr;"), but it will barf if the location + is a register or noncontiguous object. */ +void c_translate_addressof (struct obstack *pool, int indent, + Dwarf_Addr dwbias, Dwarf_Die *die, + Dwarf_Attribute *typeattr, + struct location **input, const char *target); + +/* Translate a fragment to fetch the value of variable or member DIE + at the *INPUT location and store it in variable TARGET. + This handles base integer types and bit fields. */ +void c_translate_fetch (struct obstack *pool, int indent, + Dwarf_Addr dwbias __attribute__ ((unused)), + Dwarf_Die *die, Dwarf_Attribute *typeattr, + struct location **input, const char *target); + +/* Emit the C fragment built up at LOC (i.e., the return value from the + first c_translate_location call made). INDENT should match that + passed to c_translate_* previously. + + Writes complete lines of C99, code forming a complete C block, to STREAM. + Return value is true iff that code uses the `deref' runtime macros. */ +bool c_emit_location (FILE *stream, struct location *loc, int indent); diff --git a/runtime/ChangeLog b/runtime/ChangeLog index e5050775..34615687 100644 --- a/runtime/ChangeLog +++ b/runtime/ChangeLog @@ -1,3 +1,7 @@ +2005-07-28 Graydon Hoare <graydon@redhat.com> + + * loc2c-runtime.h: New file from elfutils CVS. + 2005-07-20 Martin Hunt <hunt@redhat.com> * io.c (_stp_vlog): Don't count transport failures for diff --git a/runtime/loc2c-runtime.h b/runtime/loc2c-runtime.h new file mode 100644 index 00000000..6385ec83 --- /dev/null +++ b/runtime/loc2c-runtime.h @@ -0,0 +1,125 @@ +/* target operations */ + +#include <linux/types.h> +#define intptr_t long +#define uintptr_t unsigned long + + +/* These three macro definitions are generic, just shorthands + used by the generated code. */ + +#define op_abs(x) (x < 0 ? -x : x) + +#define fetch_bitfield(target, base, higherbits, nbits) \ + target = (((base) >> (sizeof (base) * 8 - (higherbits) - (nbits))) \ + & (((__typeof (base)) 1 << (nbits)) - 1)) + +#define store_bitfield(target, base, higherbits, nbits) \ + target = (target \ + &~ ((((__typeof (base)) 1 << (nbits)) - 1) \ + << (sizeof (base) * 8 - (higherbits) - (nbits))) \ + | ((__typeof (base)) (value) \ + << (sizeof (base) * 8 - (higherbits) - (nbits)))) + + +/* These operations are target-specific. */ +#include <asm/uaccess.h> + +#define fetch_register(regno) ((intptr_t) c->regs->dwarf_register_##regno) + +#if defined __i386__ + +#define dwarf_register_0 eax +#define dwarf_register_1 ecx +#define dwarf_register_2 edx +#define dwarf_register_3 ebx +#define dwarf_register_4 esp +#define dwarf_register_5 ebp +#define dwarf_register_6 esi +#define dwarf_register_7 edi + +#elif defined __x86_64__ + +#define dwarf_register_0 eax +#define dwarf_register_1 edx +#define dwarf_register_2 ecx +#define dwarf_register_3 ebx +#define dwarf_register_4 esi +#define dwarf_register_5 edi +#define dwarf_register_6 ebp +#define dwarf_register_7 esp +#define dwarf_register_8 r8 +#define dwarf_register_9 r9 +#define dwarf_register_10 r10 +#define dwarf_register_11 r11 +#define dwarf_register_12 r12 +#define dwarf_register_13 r13 +#define dwarf_register_14 r14 +#define dwarf_register_15 r15 + +#elif defined __powerpc__ + +#undef fetch_register +#define fetch_register(regno) ((intptr_t) c->regs->gpr[regno]) + +#endif + +#if defined __i386__ || defined __x86_64__ + +#define deref(size, addr) \ + ({ \ + int _bad = 0; \ + u8 _b; u16 _w; u32 _l; u64 _q; \ + intptr_t _v; \ + switch (size) \ + { \ + case 1: __get_user_asm(_b,addr,_bad,"b","b","=q",1); _v = _b; break; \ + case 2: __get_user_asm(_w,addr,_bad,"w","w","=r",1); _v = _w; break; \ + case 4: __get_user_asm(_l,addr,_bad,"l","","=r",1); _v = _l; break; \ + case 8: __get_user_asm(_q,addr,_bad,"q","","=r",1); _v = _q; break; \ + default: _v = __get_user_bad(); \ + } \ + if (_bad) \ + goto deref_fault; \ + _v; \ + }) + +#elif defined __powerpc64__ + +#define deref(size, addr) \ + ({ \ + int _bad = 0; \ + intptr_t _v; \ + switch (size) \ + { \ + case 1: __get_user_asm(_v,addr,_bad,"lbz",1); break; \ + case 2: __get_user_asm(_v,addr,_bad,"lhz",1); break; \ + case 4: __get_user_asm(_v,addr,_bad,"lwz",1); break; \ + case 8: __get_user_asm(_v,addr,_bad,"ld",1); break; \ + default: _v = __get_user_bad(); \ + } \ + if (_bad) \ + goto deref_fault; \ + _v; \ + }) + +#elif defined __powerpc__ + +#define deref(size, addr) \ + ({ \ + int _bad = 0; \ + intptr_t _v; \ + switch (size) \ + { \ + case 1: __get_user_asm(_v,addr,_bad,"lbz"); break; \ + case 2: __get_user_asm(_v,addr,_bad,"lhz"); break; \ + case 4: __get_user_asm(_v,addr,_bad,"lwz"); break; \ + case 8: __get_user_asm(_v,addr,_bad,"ld"); break; \ + default: _v = __get_user_bad(); \ + } \ + if (_bad) \ + goto deref_fault; \ + _v; \ + }) + +#endif diff --git a/staptree.cxx b/staptree.cxx index fb7c56aa..7f672c38 100644 --- a/staptree.cxx +++ b/staptree.cxx @@ -1020,25 +1020,6 @@ throwing_visitor::visit_functioncall (functioncall* e) // ------------------------------------------------------------------------ -template <typename T> static void -require (deep_copy_visitor* v, T* dst, T src) -{ - *dst = NULL; - if (src != NULL) - { - v->targets.push(static_cast<void* >(dst)); - src->visit(v); - v->targets.pop(); - assert(*dst); - } -} - -template <typename T> static void -provide (deep_copy_visitor* v, T src) -{ - assert(!v->targets.empty()); - *(static_cast<T*>(v->targets.top())) = src; -} void deep_copy_visitor::visit_block (block* s) @@ -22,7 +22,7 @@ struct semantic_error: public std::runtime_error const token* tok1; const std::string msg2; const token* tok2; - + ~semantic_error () throw () {} semantic_error (const std::string& msg): runtime_error (msg), tok1 (0), tok2 (0) {} @@ -544,37 +544,55 @@ struct deep_copy_visitor: public visitor static statement *deep_copy (statement *s); static block *deep_copy (block *s); - - void visit_block (block *s); - void visit_embeddedcode (embeddedcode *s); - void visit_null_statement (null_statement *s); - void visit_expr_statement (expr_statement *s); - void visit_if_statement (if_statement* s); - void visit_for_loop (for_loop* s); - void visit_foreach_loop (foreach_loop* s); - void visit_return_statement (return_statement* s); - void visit_delete_statement (delete_statement* s); - void visit_next_statement (next_statement* s); - void visit_break_statement (break_statement* s); - void visit_continue_statement (continue_statement* s); - void visit_literal_string (literal_string* e); - void visit_literal_number (literal_number* e); - void visit_binary_expression (binary_expression* e); - void visit_unary_expression (unary_expression* e); - void visit_pre_crement (pre_crement* e); - void visit_post_crement (post_crement* e); - void visit_logical_or_expr (logical_or_expr* e); - void visit_logical_and_expr (logical_and_expr* e); - void visit_array_in (array_in* e); - void visit_comparison (comparison* e); - void visit_concatenation (concatenation* e); - void visit_ternary_expression (ternary_expression* e); - void visit_assignment (assignment* e); - void visit_symbol (symbol* e); - void visit_arrayindex (arrayindex* e); - void visit_functioncall (functioncall* e); -}; - - + + virtual void visit_block (block *s); + virtual void visit_embeddedcode (embeddedcode *s); + virtual void visit_null_statement (null_statement *s); + virtual void visit_expr_statement (expr_statement *s); + virtual void visit_if_statement (if_statement* s); + virtual void visit_for_loop (for_loop* s); + virtual void visit_foreach_loop (foreach_loop* s); + virtual void visit_return_statement (return_statement* s); + virtual void visit_delete_statement (delete_statement* s); + virtual void visit_next_statement (next_statement* s); + virtual void visit_break_statement (break_statement* s); + virtual void visit_continue_statement (continue_statement* s); + virtual void visit_literal_string (literal_string* e); + virtual void visit_literal_number (literal_number* e); + virtual void visit_binary_expression (binary_expression* e); + virtual void visit_unary_expression (unary_expression* e); + virtual void visit_pre_crement (pre_crement* e); + virtual void visit_post_crement (post_crement* e); + virtual void visit_logical_or_expr (logical_or_expr* e); + virtual void visit_logical_and_expr (logical_and_expr* e); + virtual void visit_array_in (array_in* e); + virtual void visit_comparison (comparison* e); + virtual void visit_concatenation (concatenation* e); + virtual void visit_ternary_expression (ternary_expression* e); + virtual void visit_assignment (assignment* e); + virtual void visit_symbol (symbol* e); + virtual void visit_arrayindex (arrayindex* e); + virtual void visit_functioncall (functioncall* e); +}; + +template <typename T> static void +require (deep_copy_visitor* v, T* dst, T src) +{ + *dst = NULL; + if (src != NULL) + { + v->targets.push(static_cast<void* >(dst)); + src->visit(v); + v->targets.pop(); + assert(*dst); + } +} + +template <typename T> static void +provide (deep_copy_visitor* v, T src) +{ + assert(!v->targets.empty()); + *(static_cast<T*>(v->targets.top())) = src; +} #endif // STAPTREE_H diff --git a/tapsets.cxx b/tapsets.cxx index bbe0481c..d60cb480 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -21,9 +21,16 @@ #include <vector> #ifdef HAVE_ELFUTILS_LIBDWFL_H + extern "C" { #include <elfutils/libdwfl.h> +#include <libdw.h> +#include <dwarf.h> +#include <elf.h> +#include <obstack.h> +#include "loc2c.h" } + #endif #include <fnmatch.h> @@ -142,6 +149,16 @@ be_derived_probe::emit_probe_entries (translator_output* o, unsigned j) // Dwarf derived probes. // ------------------------------------------------------------------------ +template <typename OUT, typename IN> inline OUT +lex_cast(IN const & in) +{ + stringstream ss; + OUT out; + if (!(ss << in && ss >> out)) + throw runtime_error("bad lexical cast"); + return out; +} + // Helper for dealing with selected portions of libdwfl in a more readable // fashion, and with specific cleanup / checking / logging options. @@ -362,7 +379,7 @@ dwflpp void iterate_over_modules(int (* callback)(Dwfl_Module *, void **, const char *, Dwarf_Addr, - Dwarf *, Dwarf_Addr, void *), + void *), void * data) { if (false && sess.verbose) @@ -370,7 +387,7 @@ dwflpp ptrdiff_t off = 0; do { - off = dwfl_getdwarf(dwfl, callback, data, off); + off = dwfl_getmodules (dwfl, callback, data, off); } while (off > 0); if (false && sess.verbose) @@ -476,6 +493,106 @@ dwflpp } + string literal_stmt_for_local(Dwarf_Addr pc, + string const & local) + { + assert (cu); + + Dwarf_Die *scopes; + Dwarf_Die vardie; + + int nscopes = dwarf_getscopes (cu, pc, &scopes); + if (nscopes == 0) + { + throw semantic_error ("unable to find any scopes containing " + + lex_cast<string>(pc) + + " while searching for local '" + local + "'"); + } + + int declaring_scope = dwarf_getscopevar (scopes, nscopes, + local.c_str(), + 0, NULL, 0, 0, + &vardie); + if (declaring_scope < 0) + { + throw semantic_error ("unable to find local '" + local + "'" + + " near pc " + lex_cast<string>(pc)); + } + + Dwarf_Attribute fb_attr_mem, *fb_attr = NULL; + for (int inner = 0; inner < nscopes; ++inner) + { + switch (dwarf_tag (&scopes[inner])) + { + default: + continue; + case DW_TAG_subprogram: + case DW_TAG_entry_point: + case DW_TAG_inlined_subroutine: /* XXX */ + if (inner >= declaring_scope) + fb_attr = dwarf_attr_integrate (&scopes[inner], + DW_AT_frame_base, + &fb_attr_mem); + break; + } + } + + if (sess.verbose) + clog << "finding location for local '" << local + << "' near address " << hex << "0x" << pc + << ", module bias " << hex << "0x" << module_bias + << endl; + + Dwarf_Attribute attr_mem; + if (dwarf_attr_integrate (&vardie, DW_AT_location, &attr_mem) == NULL) + throw semantic_error("failed to retrieve location " + "attribute for local '" + local + "'"); + +#define obstack_chunk_alloc malloc +#define obstack_chunk_free free + + struct obstack pool; + obstack_init (&pool); + struct location *tail = NULL; + struct location *head = c_translate_location (&pool, 1, module_bias, + &attr_mem, pc, + &tail, fb_attr); + + if (dwarf_attr_integrate (&vardie, DW_AT_type, &attr_mem) == NULL) + throw semantic_error("failed to retrieve type " + "attribute for local '" + local + "'"); + + c_translate_fetch (&pool, 1, module_bias, &vardie, + &attr_mem, &tail, + "THIS->__retvalue"); + + + size_t bufsz = 1024; + char *buf = static_cast<char*>(malloc(bufsz)); + assert(buf); + + FILE *memstream = open_memstream (&buf, &bufsz); + assert(memstream); + + bool deref = c_emit_location (memstream, head, 1); + + fprintf(memstream, "goto out;\n"); + if (deref) + { + fprintf(memstream, + "deref_fault:\n" + " c->errorcount++; \n" + " goto out;\n\n"); + } + + fclose (memstream); + string result(buf); + free (buf); + return result; + } + + + ~dwflpp() { if (dwfl) @@ -512,10 +629,11 @@ dwarf_probe_type }; struct dwarf_builder; +struct dwarf_query; + struct dwarf_derived_probe : public derived_probe { - dwarf_derived_probe (probe * p, - probe_point * l, + dwarf_derived_probe (dwarf_query & q, string const & module_name, dwarf_probe_type type, Dwarf_Addr addr); @@ -562,6 +680,8 @@ dwarf_query static bool get_number_param(map<string, literal *> const & params, string const & k, long & v); + string pt_regs_member_for_regnum(uint8_t dwarf_regnum); + vector<derived_probe *> & results; void add_kernel_probe(dwarf_probe_type type, Dwarf_Addr addr); void add_module_probe(string const & module, @@ -659,12 +779,12 @@ dwarf_query::get_number_param(map<string, literal *> const & params, return true; } + void dwarf_query::add_kernel_probe(dwarf_probe_type type, Dwarf_Addr addr) { - results.push_back(new dwarf_derived_probe(base_probe, base_loc, - "", type, addr)); + results.push_back(new dwarf_derived_probe(*this, "", type, addr)); } void @@ -672,8 +792,7 @@ dwarf_query::add_module_probe(string const & module, dwarf_probe_type type, Dwarf_Addr addr) { - results.push_back(new dwarf_derived_probe(base_probe, base_loc, - module, type, addr)); + results.push_back(new dwarf_derived_probe(*this, module, type, addr)); } @@ -719,16 +838,6 @@ dwarf_query::dwarf_query(systemtap_session & sess, } -template <typename OUT, typename IN> inline OUT -lex_cast(IN const & in) -{ - stringstream ss; - OUT out; - if (!(ss << in && ss >> out)) - throw runtime_error("bad lexical cast"); - return out; -} - function_spec_type dwarf_query::parse_function_spec(string & spec) { @@ -830,43 +939,61 @@ query_function(Dwarf_Func * func, void * arg) q->dw.focus_on_function(func); - // XXX: We assume addr is a global address here. Is it? - Dwarf_Addr addr; - if (!q->dw.function_entrypc(&addr)) - { - if (false && q->sess.verbose) - clog << "WARNING: cannot find entry PC for function " - << q->dw.function_name << endl; - return DWARF_CB_OK; - } - - if ((q->has_statement_str || q->has_function_str) - && q->dw.function_name_matches(q->function)) - { - // If this function's name matches a function or statement - // pattern, we use its entry pc, but we do not abort iteration - // since there might be other functions matching the pattern. - query_statement(addr, q); + if (q->has_statement_str || q->has_function_str) + { + if (q->dw.function_name_matches(q->function)) + { + // XXX: We assume addr is a global address here. Is it? + // XXX: This code is duplicated below, but it's important + // for performance reasons to test things in this order. + + Dwarf_Addr addr; + if (!q->dw.function_entrypc(&addr)) + { + if (q->sess.verbose) + clog << "WARNING: cannot find entry PC for function " + << q->dw.function_name << endl; + return DWARF_CB_OK; + } + + // If this function's name matches a function or statement + // pattern, we use its entry pc, but we do not abort iteration + // since there might be other functions matching the pattern. + query_statement(addr, q); + } } - else if (q->has_kernel - && q->has_function_num - && q->dw.function_includes_global_addr(q->function_num_val)) - { - // If this function's address range matches a kernel-relative - // function address, we use its entry pc and break out of the - // iteration, since there can only be one such function. - query_statement(addr, q); - return DWARF_CB_ABORT; - } - else if (q->has_module - && q->has_function_num - && q->dw.function_includes_module_addr(q->function_num_val)) + else { - // If this function's address range matches a module-relative - // function address, we use its entry pc and break out of the - // iteration, since there can only be one such function. - query_statement(addr, q); - return DWARF_CB_ABORT; + // XXX: We assume addr is a global address here. Is it? + Dwarf_Addr addr; + if (!q->dw.function_entrypc(&addr)) + { + if (false && q->sess.verbose) + clog << "WARNING: cannot find entry PC for function " + << q->dw.function_name << endl; + return DWARF_CB_OK; + } + + if (q->has_kernel + && q->has_function_num + && q->dw.function_includes_global_addr(q->function_num_val)) + { + // If this function's address range matches a kernel-relative + // function address, we use its entry pc and break out of the + // iteration, since there can only be one such function. + query_statement(addr, q); + return DWARF_CB_ABORT; + } + else if (q->has_module + && q->has_function_num + && q->dw.function_includes_module_addr(q->function_num_val)) + { + // If this function's address range matches a module-relative + // function address, we use its entry pc and break out of the + // iteration, since there can only be one such function. + query_statement(addr, q); + return DWARF_CB_ABORT; + } } return DWARF_CB_OK; @@ -910,7 +1037,6 @@ static int query_module (Dwfl_Module *mod __attribute__ ((unused)), void **userdata __attribute__ ((unused)), const char *name, Dwarf_Addr base, - Dwarf *dw, Dwarf_Addr bias, void *arg __attribute__ ((unused))) { @@ -961,16 +1087,114 @@ query_module (Dwfl_Module *mod __attribute__ ((unused)), return DWARF_CB_OK; } -dwarf_derived_probe::dwarf_derived_probe (probe * p, - probe_point * l, +struct +var_expanding_copy_visitor + : public deep_copy_visitor +{ + // Alas, we cannot easily mixin lvalue_aware_traversing_visitor. + // But behold the *awesome power* of copy and paste. + + static unsigned tick; + + dwarf_query & q; + unsigned lval_depth; + Dwarf_Addr addr; + + var_expanding_copy_visitor(dwarf_query & q, Dwarf_Addr a) + : q(q), lval_depth(0), addr(a) + {} + + bool is_in_lvalue() + { + return lval_depth > 0; + } + + void visit_pre_crement (pre_crement* e) + { + ++lval_depth; + e->operand->visit (this); + --lval_depth; + } + + void visit_post_crement (post_crement* e) + { + ++lval_depth; + e->operand->visit (this); + --lval_depth; + } + + void visit_assignment (assignment* e) + { + ++lval_depth; + e->left->visit (this); + --lval_depth; + e->right->visit (this); + } + + void visit_delete_statement (delete_statement* s) + { + ++lval_depth; + s->value->visit (this); + --lval_depth; + } + + void visit_symbol (symbol* e); +}; + + +unsigned var_expanding_copy_visitor::tick = 0; + + +void +var_expanding_copy_visitor::visit_symbol (symbol *e) +{ + if (e->name.size() > 0 && + e->name[0] == '$') + { + if (is_in_lvalue()) + { + throw semantic_error("read-only special variable " + + e->name + " used in lvalue", e->tok); + } + + string fname = "get_" + e->name.substr(1) + "_" + lex_cast<string>(tick++); + + // synthesize a function + functiondecl *fdecl = new functiondecl; + embeddedcode *ec = new embeddedcode; + ec->code = q.dw.literal_stmt_for_local(addr, e->name.substr(1)); + fdecl->name = fname; + fdecl->body = ec; + fdecl->type = pe_long; + q.sess.functions.push_back(fdecl); + + // synthesize a call + functioncall* n = new functioncall; + n->tok = e->tok; + n->function = fname; + n->referent = NULL; + provide <functioncall*> (this, n); + + } + else + { + deep_copy_visitor::visit_symbol (e); + } +} + + +dwarf_derived_probe::dwarf_derived_probe (dwarf_query & q, string const & module_name, dwarf_probe_type type, Dwarf_Addr addr) - : derived_probe (p, l), + : derived_probe (NULL, q.base_loc), module_name(module_name), type(type), addr(addr) { + var_expanding_copy_visitor v (q, addr); + require <block*> (&v, &(this->body), q.base_probe->body); + this->tok = q.base_probe->tok; } void diff --git a/translate.cxx b/translate.cxx index 2667d7ab..78a75d91 100644 --- a/translate.cxx +++ b/translate.cxx @@ -492,6 +492,7 @@ c_unparser::emit_common_header () { // XXX: tapsets.cxx should be able to add additional definitions + o->newline() << "#include \"loc2c-runtime.h\" "; o->newline() << "#define MAXNESTING 30"; o->newline() << "#define MAXCONCURRENCY NR_CPUS"; o->newline() << "#define MAXSTRINGLEN 128"; |