loc2c.c - systemtap
Data types defined
Functions defined
Macros defined
Source code
#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 <elfutils/libdw.h>
#include <elfutils/version.h>
#include <assert.h>
#include "loc2c.h"
#include "config.h"
#if ! _ELFUTILS_PREREQ(0, 153)
#define DW_OP_GNU_entry_value 0xf3
#endif
#define N_(x) x
#define STACK_TYPE "intptr_t" #define UTYPE "uintptr_t" #define SFORMAT "%" PRId64 "L"
#define UFORMAT "%" PRIu64 "UL"
#define AFORMAT "%#" PRIx64 "UL"
#define STACKFMT "s%u"
struct location_context
{
struct obstack *pool;
void (*fail) (void *arg, const char *fmt, ...)
__attribute__ ((noreturn, format (printf, 2, 3)));
void *fail_arg;
void (*emit_address) (void *fail_arg, struct obstack *, Dwarf_Addr);
Dwarf_Attribute *attr;
Dwarf_Addr dwbias;
Dwarf_Addr pc;
Dwarf_Attribute *fb_attr;
const Dwarf_Op *cfa_ops;
};
struct location
{
struct location *next;
struct location_context *context;
const Dwarf_Op *ops;
size_t nops;
Dwarf_Word byte_size;
enum
{
loc_address, loc_register, loc_noncontiguous, loc_unavailable,
loc_value, loc_constant, loc_implicit_pointer,
loc_decl, loc_fragment, loc_final
} type;
struct location *frame_base;
union
{
struct {
const char *declare; char *program; unsigned int stack_depth; bool used_deref; } address;
struct {
unsigned int regno;
Dwarf_Word offset;
} reg;
struct location *pieces; const void *constant_block; struct {
struct location *target;
Dwarf_Sword offset;
} pointer;
};
};
static const char *
stack_slot_type (struct location *context __attribute__ ((unused)), bool sign)
{
return sign ? STACK_TYPE : UTYPE;
}
static struct location *
alloc_location (struct location_context *ctx)
{
struct location *loc = obstack_alloc (ctx->pool, sizeof *loc);
loc->context = ctx;
loc->byte_size = 0;
loc->frame_base = NULL;
loc->ops = NULL;
loc->nops = 0;
return loc;
}
#define FAIL(loc, fmt, ...) \
(*(loc)->context->fail) ((loc)->context->fail_arg, fmt, ## __VA_ARGS__)
static void
default_emit_address (void *fail_arg __attribute__ ((unused)),
struct obstack *pool, Dwarf_Addr address)
{
obstack_printf (pool, AFORMAT, address);
}
static struct location_context *
new_context (struct obstack *pool,
void (*fail) (void *arg, const char *fmt, ...)
__attribute__ ((noreturn, format (printf, 2, 3))),
void *fail_arg,
void (*emit_address) (void *fail_arg,
struct obstack *, Dwarf_Addr),
Dwarf_Addr dwbias, Dwarf_Addr pc_address,
Dwarf_Attribute *attr, Dwarf_Attribute *fb_attr,
const Dwarf_Op *cfa_ops)
{
struct location_context *ctx = obstack_alloc (pool, sizeof *ctx);
ctx->pool = pool;
ctx->fail = fail;
ctx->fail_arg = fail_arg;
ctx->emit_address = emit_address ?: &default_emit_address;
ctx->attr = attr;
ctx->dwbias = dwbias;
ctx->pc = pc_address;
ctx->fb_attr = fb_attr;
ctx->cfa_ops = cfa_ops;
return ctx;
}
static struct location *
translate_constant (struct location_context *ctx, int indent,
Dwarf_Attribute *attr)
{
indent += 2;
struct location *loc = obstack_alloc (ctx->pool, sizeof *loc);
loc->context = ctx;
loc->next = NULL;
loc->byte_size = 0;
loc->frame_base = NULL;
loc->address.stack_depth = 0;
loc->address.declare = NULL;
loc->address.used_deref = false;
loc->ops = NULL;
loc->nops = 0;
switch (dwarf_whatform (attr))
{
case DW_FORM_addr:
{
Dwarf_Addr addr;
if (dwarf_formaddr (attr, &addr) != 0)
{
FAIL (loc, N_("cannot get constant address: %s"),
dwarf_errmsg (-1));
return NULL;
}
loc->type = loc_value;
obstack_printf (ctx->pool, "%*saddr = ", indent * 2, "");
(*ctx->emit_address) (ctx->fail_arg, ctx->pool, ctx->dwbias + addr);
obstack_grow (ctx->pool, ";\n", 3);
loc->address.program = obstack_finish (ctx->pool);
break;
}
case DW_FORM_block:
case DW_FORM_block1:
case DW_FORM_block2:
case DW_FORM_block4:
{
Dwarf_Block block;
if (dwarf_formblock (attr, &block) != 0)
{
FAIL (loc, N_("cannot get constant block: %s"), dwarf_errmsg (-1));
return NULL;
}
loc->type = loc_constant;
loc->byte_size = block.length;
loc->constant_block = block.data;
break;
}
case DW_FORM_string:
case DW_FORM_strp:
{
const char *string = dwarf_formstring (attr);
if (string == NULL)
{
FAIL (loc, N_("cannot get string constant: %s"), dwarf_errmsg (-1));
return NULL;
}
loc->type = loc_constant;
loc->byte_size = strlen (string) + 1;
loc->constant_block = string;
break;
}
default:
{
Dwarf_Sword value;
if (dwarf_formsdata (attr, &value) != 0)
{
FAIL (loc, N_("cannot get constant value: %s"), dwarf_errmsg (-1));
return NULL;
}
loc->type = loc_value;
obstack_printf (ctx->pool, "%*saddr = (" UTYPE ")%#" PRIx64 "ULL;\n",
indent * 2, "", value);
obstack_1grow (ctx->pool, '\0');
loc->address.program = obstack_finish (ctx->pool);
break;
}
}
return loc;
}
struct location *
c_translate_constant (struct obstack *pool,
void (*fail) (void *arg,
const char *fmt, ...)
__attribute__ ((noreturn,
format (printf, 2, 3))),
void *fail_arg,
void (*emit_address) (void *fail_arg,
struct obstack *,
Dwarf_Addr),
int indent, Dwarf_Addr dwbias, Dwarf_Attribute *attr)
{
return translate_constant (new_context (pool, fail, fail_arg, emit_address,
dwbias, 0, attr, NULL, NULL),
indent, attr);
}
#if _ELFUTILS_PREREQ (0, 149)
static struct location *location_from_attr (struct location_context *ctx,
int indent, Dwarf_Attribute *attr);
#endif
static struct location *
new_synthetic_loc (struct location *origin, bool deref)
{
obstack_1grow (origin->context->pool, '\0');
char *program = obstack_finish (origin->context->pool);
struct location *loc = alloc_location (origin->context);
loc->next = NULL;
loc->byte_size = 0;
loc->type = loc_address;
loc->address.program = program;
loc->address.stack_depth = 0;
loc->address.declare = 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;
}
static struct location *
lose (struct location *loc, const Dwarf_Op *lexpr, size_t len,
const char *failure, size_t i)
{
if (lexpr == NULL || i >= len)
FAIL (loc, "%s", failure);
else if (i < len)
FAIL (loc, N_("%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;
}
static const char *
translate (struct location_context *ctx, int indent,
const Dwarf_Op *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, N_(msg))
#define emit(fmt, ...) obstack_printf (ctx->pool, fmt, ## __VA_ARGS__)
unsigned int stack_depth;
unsigned int 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) ({int z = (stack_depth - 1 - (idx)); assert (z >= 0); z;})
#define push(fmt, ...) \
emit ("%*s" STACKFMT " = " fmt ";\n", indent * 2, "", PUSH, ## __VA_ARGS__)
int tos_register;
inline void fetch_tos_register (void)
{
deepen ();
emit ("%*s" STACKFMT " = fetch_register (%d);\n",
indent * 2, "", stack_depth, tos_register);
tos_register = -1;
}
bool tos_value;
Dwarf_Block implicit_value;
bool used_deref;
const Dwarf_Op *implicit_pointer;
inline void reset ()
{
stack_depth = 0;
tos_register = -1;
tos_value = false;
implicit_value.data = NULL;
implicit_pointer = NULL;
used_deref = false;
if (input != NULL)
switch (input->type)
{
case loc_address:
push ("addr");
break;
case loc_value:
push ("addr");
tos_value = true;
break;
case loc_register:
tos_register = input->reg.regno;
break;
default:
abort ();
break;
}
}
size_t i;
inline const char *finish (struct location *piece)
{
if (piece->nops == 0)
{
assert (stack_depth == 0);
assert (tos_register == -1);
assert (obstack_object_size (ctx->pool) == 0);
piece->type = loc_unavailable;
}
else if (stack_depth >= 1)
{
obstack_1grow (ctx->pool, '\0');
char *program = obstack_finish (ctx->pool);
if (implicit_pointer != NULL)
{
piece->type = loc_implicit_pointer;
piece->pointer.offset = implicit_pointer->number2;
#if !_ELFUTILS_PREREQ (0, 149)
abort ();
#else
Dwarf_Attribute target;
if (dwarf_getlocation_implicit_pointer (ctx->attr,
implicit_pointer,
&target) != 0)
DIE ("invalid implicit pointer");
switch (dwarf_whatattr (&target))
{
case DW_AT_const_value:
piece->pointer.target = translate_constant (ctx, indent,
&target);
break;
case DW_AT_location:
piece->pointer.target = location_from_attr (ctx, indent,
&target);
break;
default:
DIE ("unexpected implicit pointer attribute!");
break;
}
#endif
}
else if (implicit_value.data == NULL)
{
piece->type = tos_value ? loc_value : loc_address;
piece->address.declare = NULL;
piece->address.program = program;
piece->address.stack_depth = max_stack;
piece->address.used_deref = used_deref;
}
else
{
piece->type = loc_constant;
piece->byte_size = implicit_value.length;
piece->constant_block = implicit_value.data;
}
}
else if (tos_register == -1)
DIE ("stack underflow");
else if (obstack_object_size (ctx->pool) != 0)
DIE ("register value must stand alone in location expression");
else
{
piece->type = loc_register;
piece->reg.regno = tos_register;
piece->reg.offset = 0;
}
return NULL;
}
reset ();
struct location *pieces = NULL, **tailpiece = &pieces;
size_t piece_expr_start = 0;
Dwarf_Word piece_total_bytes = 0;
for (i = 0; i < len; ++i)
{
unsigned int reg;
uint_fast8_t sp;
Dwarf_Word value;
inline bool more_ops (void)
{
return (expr[i].atom != DW_OP_nop
&& expr[i].atom != DW_OP_piece
&& expr[i].atom != DW_OP_bit_piece);
}
if (tos_value && more_ops ())
DIE ("operations follow DW_OP_stack_value");
if (implicit_value.data != NULL && more_ops ())
DIE ("operations follow DW_OP_implicit_value");
if (implicit_pointer != NULL && more_ops ())
DIE ("operations follow DW_OP_GNU_implicit_pointer");
switch (expr[i].atom)
{
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 (); 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 (); 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 (2), STACK (-1));
break;
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;
case DW_OP_deref:
{
POP (addr);
push ("deref (sizeof (void *), " STACKFMT ")", addr);
used_deref = true;
}
break;
case DW_OP_deref_size:
{
POP (addr);
push ("deref (" UFORMAT ", " STACKFMT ")",
expr[i].number, addr);
used_deref = true;
}
break;
case DW_OP_xderef:
{
POP (addr);
POP (as);
push ("xderef (sizeof (void *), " STACKFMT ", " STACKFMT ")",
addr, as);
used_deref = true;
}
break;
case DW_OP_xderef_size:
{
POP (addr);
POP (as);
push ("xderef (" UFORMAT ", " STACKFMT ", " STACKFMT ")",
expr[i].number, addr, as);
used_deref = true;
}
break;
case DW_OP_addr:
emit ("%*s" STACKFMT " = ", indent * 2, "", PUSH);
(*ctx->emit_address) (ctx->fail_arg, ctx->pool,
ctx->dwbias + expr[i].number);
emit (";\n");
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;
#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 (minus, -);
BINOP (mul, *);
UNOP (neg, -);
UNOP (not, ~);
BINOP (or, |);
BINOP (plus, +);
BINOP (shl, <<);
BINOP (shr, >>);
BINOP (xor, ^);
BINOP (le, <=);
BINOP (ge, >=);
BINOP (eq, ==);
BINOP (lt, <);
BINOP (gt, >);
BINOP (ne, !=);
#undef UNOP
#undef BINOP
case DW_OP_shra:
{
POP (b);
POP (a);
push ("(%s) " STACKFMT " >> (%s)" STACKFMT,
stack_slot_type (loc, true), a,
stack_slot_type (loc, true), b);
break;
}
case DW_OP_div:
{
POP (b);
POP (a);
push ("dwarf_div_op((%s) " STACKFMT ", (%s) " STACKFMT ")",
stack_slot_type (loc, true), a,
stack_slot_type (loc, true), b);
used_deref = true;
break;
}
case DW_OP_mod:
{
POP (b);
POP (a);
push ("dwarf_mod_op((%s) " STACKFMT ", (%s) " STACKFMT ")",
stack_slot_type (loc, false), a,
stack_slot_type (loc, false), b);
used_deref = true;
break;
}
case DW_OP_plus_uconst:
{
POP (x);
push (STACKFMT " + " UFORMAT, x, expr[i].number);
}
break;
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;
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;
case DW_OP_piece:
if (stack_depth > 1)
DIE ("DW_OP_piece left multiple values on stack");
else
{
struct location temp_piece =
{
.context = loc->context,
.frame_base = NULL,
.ops = &expr[piece_expr_start],
.nops = i - piece_expr_start,
};
const char *failure = finish (&temp_piece);
if (failure != NULL)
return failure;
struct location *piece = obstack_alloc (ctx->pool, sizeof *piece);
*piece = temp_piece;
piece_expr_start = i + 1;
piece_total_bytes += piece->byte_size = expr[i].number;
*tailpiece = piece;
tailpiece = &piece->next;
piece->next = NULL;
reset ();
}
break;
case DW_OP_stack_value:
if (stack_depth > 1)
DIE ("DW_OP_stack_value left multiple values on stack");
else
{
POP (tos);
assert (tos == 0);
PUSH;
tos_value = true;
}
break;
case DW_OP_implicit_value:
if (ctx->attr == NULL)
DIE ("DW_OP_implicit_value used in invalid context"
" (no DWARF attribute, ABI return value location?)");
if (stack_depth != 0)
DIE ("DW_OP_implicit_value follows stack operations");
#if _ELFUTILS_PREREQ (0, 143)
if (dwarf_getlocation_implicit_value (ctx->attr,
(Dwarf_Op *) &expr[i],
&implicit_value) != 0)
DIE ("dwarf_getlocation_implicit_value failed");
PUSH;
break;
#endif
DIE ("DW_OP_implicit_value not supported");
break;
#if _ELFUTILS_PREREQ (0, 149)
case DW_OP_GNU_implicit_pointer:
implicit_pointer = &expr[i];
PUSH;
break;
#endif
case DW_OP_call_frame_cfa:
if (need_fb == NULL)
DIE ("DW_OP_call_frame_cfa while processing frame base");
else
DIE ("DW_OP_call_frame_cfa not expected outside DW_AT_frame_base");
break;
case DW_OP_push_object_address:
DIE ("unhandled DW_OP_push_object_address");
break;
case DW_OP_GNU_entry_value:
DIE ("unhandled DW_OP_GNU_entry_value");
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;
loc->byte_size = piece_total_bytes;
return NULL;
underflow:
DIE ("stack underflow");
#undef emit
#undef push
#undef PUSH
#undef POP
#undef STACK
#undef DIE
}
static struct location *
location_from_address (struct location_context *ctx, int indent,
const Dwarf_Op *expr, size_t len,
struct location **input)
{
struct location *loc = obstack_alloc (ctx->pool, sizeof *loc);
loc->context = ctx;
loc->byte_size = 0;
loc->frame_base = NULL;
loc->ops = NULL;
loc->nops = 0;
bool need_fb = false;
size_t loser;
const char *failure = translate (ctx, indent + 1, expr, len,
*input, &need_fb, &loser, loc);
if (failure != NULL)
return lose (loc, expr, len, failure, loser);
loc->next = NULL;
if (need_fb)
{
if (ctx->fb_attr == NULL)
FAIL (loc, N_("required DW_AT_frame_base attribute not supplied"));
Dwarf_Op *fb_expr;
size_t fb_len;
switch (dwarf_getlocation_addr (ctx->fb_attr, ctx->pc,
&fb_expr, &fb_len, 1))
{
case 1: if (fb_len == 0)
goto fb_inaccessible;
break;
default: case -1:
FAIL (loc, N_("dwarf_getlocation_addr (form %#x): %s"),
dwarf_whatform (ctx->fb_attr), dwarf_errmsg (-1));
return NULL;
case 0: fb_inaccessible:
FAIL (loc, N_("DW_AT_frame_base not accessible at this address"));
return NULL;
}
const Dwarf_Op * fb_ops;
if (fb_len == 1 && fb_expr[0].atom == DW_OP_call_frame_cfa)
{
if (ctx->cfa_ops == NULL)
FAIL (loc, N_("No cfa_ops supplied, but needed by DW_OP_call_frame_cfa"));
fb_ops = ctx->cfa_ops;
}
else
fb_ops = fb_expr;
loc->frame_base = alloc_location (ctx);
failure = translate (ctx, indent + 1, fb_ops, fb_len, NULL,
NULL, &loser, loc->frame_base);
if (failure != NULL)
return lose (loc, fb_ops, fb_len, failure, loser);
}
if (*input != NULL)
(*input)->next = loc;
*input = loc;
return loc;
}
#if _ELFUTILS_PREREQ (0, 149)
static struct location *
location_from_attr (struct location_context *ctx, int indent,
Dwarf_Attribute *attr)
{
Dwarf_Op *expr;
size_t len;
switch (dwarf_getlocation_addr (attr, ctx->pc, &expr, &len, 1))
{
case 1: if (len > 0)
break;
case 0: (*ctx->fail) (ctx->fail_arg, N_("not accessible at this address (%#" PRIx64 ")"), ctx->pc);
return NULL;
default: case -1:
(*ctx->fail) (ctx->fail_arg, "dwarf_getlocation_addr: %s",
dwarf_errmsg (-1));
return NULL;
}
struct location *input = NULL;
return location_from_address (ctx, indent, expr, len, &input);
}
#endif
static struct location *
translate_offset (int indent, const Dwarf_Op *expr, size_t len, size_t i,
struct location *head, struct location **input,
Dwarf_Word offset)
{
struct location_context *const ctx = (*input)->context;
#define DIE(msg) return lose (*input, expr, len, N_(msg), i)
while ((*input)->type == loc_noncontiguous)
{
struct location *piece = (*input)->pieces;
while (piece != NULL && offset >= piece->byte_size)
{
offset -= piece->byte_size;
piece = piece->next;
}
if (piece == NULL)
DIE ("offset outside available pieces");
assert ((*input)->next == NULL);
(*input)->next = piece;
*input = piece;
}
switch ((*input)->type)
{
case loc_address:
{
struct location *loc = obstack_alloc (ctx->pool, sizeof *loc);
*loc = **input;
if (head == NULL)
head = loc;
(*input)->next = loc;
if (offset == 0)
{
loc->next = NULL;
*input = loc;
}
else
{
obstack_printf (ctx->pool, "%*saddr += " SFORMAT "\n",
indent * 2, "", offset);
*input = loc->next = new_synthetic_loc (*input, false);
}
return head;
}
case loc_register:
(*input)->reg.offset += offset;
return head ?: *input;
case loc_constant:
if (offset >= (*input)->byte_size)
DIE ("offset outside available constant block");
(*input)->constant_block += offset;
(*input)->byte_size -= offset;
return head ?: *input;
case loc_implicit_pointer:
(*input)->pointer.offset += offset;
return head ?: *input;
case loc_unavailable:
return head ?: *input;
case loc_value:
if (offset == 0)
return head ?: *input;
DIE ("XXX extract partial rematerialized value");
break;
default:
abort ();
}
#undef DIE
}
static struct location *
location_relative (struct location_context *ctx, int indent,
const Dwarf_Op *expr, size_t len,
struct location **input)
{
Dwarf_Sword *stack = NULL;
unsigned int stack_depth = 0, max_stack = 0;
inline void deepen (void)
{
if (stack_depth == max_stack)
{
++max_stack;
obstack_blank (ctx->pool, sizeof stack[0]);
stack = (void *) obstack_base (ctx->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)]
#define push(value) (stack[PUSH] = (value))
const char *failure = NULL;
#define DIE(msg) do { failure = N_(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)
{
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)
*input = NULL;
else
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 (); STACKWORD (-1) = STACKWORD (0);
STACKWORD (0) = STACKWORD (1);
STACKWORD (1) = STACKWORD (-1);
break;
case DW_OP_rot:
if (stack_depth < 3)
goto underflow;
deepen (); STACKWORD (-1) = STACKWORD (0);
STACKWORD (0) = STACKWORD (1);
STACKWORD (2) = STACKWORD (2);
STACKWORD (2) = STACKWORD (-1);
break;
case DW_OP_bra:
{
POP (taken);
if (stack[taken] == 0)
break;
}
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_deref:
case DW_OP_deref_size:
case DW_OP_xderef:
case DW_OP_xderef_size:
case DW_OP_breg0 ... DW_OP_breg31:
case DW_OP_bregx:
case DW_OP_fbreg:
return location_from_address (ctx, indent, expr, len, input);
case DW_OP_addr:
DIE ("static calculation depends on load-time address");
push (ctx->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;
#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, ^);
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;
}
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)
{
POP (a);
push (stack[a] + value);
break;
}
if (*input == NULL)
goto underflow;
head = translate_offset (indent, expr, len, i, head, input, value);
if (head != NULL && i + 1 < len)
{
if ((*input)->type != loc_address)
DIE ("too much computation for non-address location");
struct location *loc = location_from_address
(ctx, indent, &expr[i + 1], len - i - 1, input);
if (loc == NULL)
head = NULL;
}
return head;
case DW_OP_reg0 ... DW_OP_reg31:
case DW_OP_regx:
DIE ("register");
break;
case DW_OP_piece:
DIE ("DW_OP_piece");
break;
case DW_OP_push_object_address:
DIE ("unhandled DW_OP_push_object_address");
break;
case DW_OP_GNU_entry_value:
DIE ("unhandled DW_OP_GNU_entry_value");
break;
default:
DIE ("unrecognized operation");
break;
}
}
if (stack_depth > 1)
DIE ("multiple values left on stack");
if (stack_depth > 0) {
if (*input != NULL)
DIE ("multiple values left on stack");
DIE ("relative expression computed constant");
}
return head;
underflow:
if (*input == NULL)
DIE ("stack underflow");
else
DIE ("cannot handle location expression");
fail:
return lose (*input, expr, len, failure, i);
}
struct location *
c_translate_location (struct obstack *pool,
void (*fail) (void *arg, const char *fmt, ...)
__attribute__ ((noreturn, format (printf, 2, 3))),
void *fail_arg,
void (*emit_address) (void *fail_arg,
struct obstack *, Dwarf_Addr),
int indent, Dwarf_Addr dwbias, Dwarf_Addr pc_address,
Dwarf_Attribute *attr,
const Dwarf_Op *expr, size_t len,
struct location **input, Dwarf_Attribute *fb_attr,
const Dwarf_Op *cfa_ops)
{
indent += 2;
struct location_context *ctx;
if (*input == NULL)
ctx = new_context (pool, fail, fail_arg, emit_address, dwbias, pc_address,
attr, fb_attr, cfa_ops);
else
{
ctx = (*input)->context;
assert (ctx->pool == pool);
if (pc_address == 0)
pc_address = ctx->pc;
else if (ctx->pc == 0)
ctx->pc = pc_address;
}
switch (*input == NULL ? loc_address : (*input)->type)
{
case loc_address:
return location_from_address (ctx, indent, expr, len, input);
case loc_noncontiguous:
case loc_register:
case loc_value:
case loc_constant:
case loc_unavailable:
case loc_implicit_pointer:
return location_relative (ctx, indent, expr, len, input);
default:
abort ();
break;
}
return NULL;
}
struct location *
c_translate_argument (struct obstack *pool,
void (*fail) (void *arg, const char *fmt, ...)
__attribute__ ((noreturn, format (printf, 2, 3))),
void *fail_arg,
void (*emit_address) (void *fail_arg,
struct obstack *, Dwarf_Addr),
int indent, const char *value)
{
indent += 2;
obstack_printf (pool, "%*saddr = %s;\n", indent * 2, "", value);
obstack_1grow (pool, '\0');
char *program = obstack_finish (pool);
struct location *loc = obstack_alloc (pool, sizeof *loc);
loc->context = new_context (pool, fail, fail_arg, emit_address, 0,
0, NULL, NULL, NULL);
loc->next = NULL;
loc->ops = NULL;
loc->nops = 0;
loc->byte_size = 0;
loc->type = loc_address;
loc->frame_base = NULL;
loc->address.declare = NULL;
loc->address.program = program;
loc->address.stack_depth = 0;
loc->address.used_deref = false;
return loc;
}
static bool
emit_base_fetch (struct obstack *pool, Dwarf_Word byte_size,
bool signed_p, const char *target, struct location *loc)
{
bool deref = false;
obstack_printf (pool, "{ ");
obstack_printf (pool, "%sint%u_t value = ",
(signed_p ? "" : "u"), (unsigned)(byte_size * 8));
switch (loc->type)
{
case loc_value:
obstack_printf (pool, "addr;");
break;
case loc_address:
if (byte_size != 0 && byte_size != (Dwarf_Word) -1)
obstack_printf (pool, "deref (%" PRIu64 ", addr);", byte_size);
else
obstack_printf (pool, "deref (sizeof %s, addr);", target);
deref = true;
break;
case loc_register:
if (loc->reg.offset != 0)
FAIL (loc, N_("cannot handle offset into register in fetch"));
obstack_printf (pool, "fetch_register (%u);", loc->reg.regno);
break;
case loc_noncontiguous:
FAIL (loc, N_("noncontiguous location for base fetch"));
break;
case loc_implicit_pointer:
FAIL (loc, N_("pointer has been optimized out"));
break;
case loc_unavailable:
FAIL (loc, N_("location not available"));
break;
default:
abort ();
break;
}
obstack_printf (pool, " %s = value; }", target);
return deref;
}
static bool
emit_base_store (struct obstack *pool, Dwarf_Word byte_size,
const char *rvalue, struct location *loc)
{
switch (loc->type)
{
case loc_address:
if (byte_size != 0 && byte_size != (Dwarf_Word) -1)
obstack_printf (pool, "store_deref (%" PRIu64 ", addr, %s); ",
byte_size, rvalue);
else
obstack_printf (pool, "store_deref (sizeof %s, addr, %s); ",
rvalue, rvalue);
return true;
case loc_register:
if (loc->reg.offset != 0)
FAIL (loc, N_("cannot handle offset into register in store"));
obstack_printf (pool, "store_register (%u, %s);", loc->reg.regno, rvalue);
break;
case loc_noncontiguous:
FAIL (loc, N_("noncontiguous location for base store"));
break;
case loc_implicit_pointer:
FAIL (loc, N_("pointer has been optimized out"));
break;
case loc_value:
FAIL (loc, N_("location is computed value, cannot store"));
break;
case loc_constant:
FAIL (loc, N_("location is constant value, cannot store"));
break;
case loc_unavailable:
FAIL (loc, N_("location is not available, cannot store"));
break;
default:
abort ();
break;
}
return false;
}
static struct location *
discontiguify (struct location_context *ctx, int indent, struct location *loc,
Dwarf_Word total_bytes, Dwarf_Word max_piece_bytes)
{
inline bool pieces_small_enough (void)
{
if (loc->type != loc_noncontiguous)
return total_bytes <= max_piece_bytes;
struct location *p;
for (p = loc->pieces; p != NULL; p = p->next)
if (p->byte_size > max_piece_bytes)
return false;
return true;
}
if (loc->type == loc_constant)
{
if (loc->byte_size > total_bytes)
loc->byte_size = total_bytes;
return loc;
}
if (pieces_small_enough ())
return loc;
struct location *noncontig = alloc_location (ctx);
noncontig->next = NULL;
noncontig->type = loc_noncontiguous;
noncontig->byte_size = total_bytes;
noncontig->pieces = NULL;
struct location **tailpiece = &noncontig->pieces;
inline void add (struct location *piece)
{
*tailpiece = piece;
tailpiece = &piece->next;
}
switch (loc->type)
{
case loc_address:
{
obstack_printf (ctx->pool, "%*scontainer_addr = addr;\n",
indent++ * 2, "");
loc->next = new_synthetic_loc (loc, false);
loc->next->byte_size = loc->byte_size;
loc->next->type = loc_fragment;
loc->next->address.declare = "container_addr";
loc = loc->next;
Dwarf_Word offset = 0;
while (total_bytes - offset > 0)
{
Dwarf_Word size = total_bytes - offset;
if (size > max_piece_bytes)
size = max_piece_bytes;
obstack_printf (ctx->pool,
"%*saddr = container_addr + " UFORMAT ";\n",
indent * 2, "", offset);
struct location *piece = new_synthetic_loc (loc, false);
piece->byte_size = size;
add (piece);
offset += size;
}
--indent;
break;
}
case loc_value:
FAIL (loc, N_("stack value too big for fetch ???"));
break;
case loc_register:
FAIL (loc, N_("single register too big for fetch/store ???"));
break;
case loc_implicit_pointer:
FAIL (loc, N_("implicit pointer too big for fetch/store ???"));
break;
case loc_noncontiguous:
FAIL (loc, N_("cannot support noncontiguous location"));
break;
default:
abort ();
break;
}
loc->next = noncontig;
return noncontig;
}
static void
declare_noncontig_union (struct obstack *pool, int indent,
struct location **input, struct location *loc,
int depth)
{
if (depth > 9)
FAIL (loc, N_("declaring noncontig union for depth > 9, too many pieces"));
obstack_printf (pool, "%*sunion {\n", indent++ * 2, "");
obstack_printf (pool, "%*schar bytes[%" PRIu64 "];\n",
indent * 2, "", loc->byte_size);
if (loc->type == loc_noncontiguous)
{
Dwarf_Word offset = 0;
struct location *p;
obstack_printf (pool, "%*sstruct {\n", indent++ * 2, "");
for (p = loc->pieces; p != NULL; p = p->next)
{
obstack_printf (pool, "%*suint%" PRIu64 "_t p%" PRIu64 ";\n",
indent * 2, "", p->byte_size * 8, offset);
offset += p->byte_size;
}
obstack_printf (pool, "%*s} pieces __attribute__ ((packed));\n",
--indent * 2, "");
}
obstack_printf (pool, "%*suint%" PRIu64 "_t whole;\n",
indent * 2, "", loc->byte_size * 8);
obstack_printf (pool, "%*s} u_pieces%d;\n", --indent * 2, "", depth);
loc = new_synthetic_loc (*input, false);
loc->type = loc_decl;
(*input)->next = loc;
*input = loc;
}
static Dwarf_Word
base_byte_size (Dwarf_Die *typedie, struct location *origin)
{
assert (dwarf_tag (typedie) == DW_TAG_base_type ||
dwarf_tag (typedie) == DW_TAG_enumeration_type);
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;
FAIL (origin,
N_("cannot get byte_size attribute for type %s: %s"),
dwarf_diename (typedie) ?: "<anonymous>",
dwarf_errmsg (-1));
return -1;
}
static Dwarf_Word
base_encoding (Dwarf_Die *typedie, struct location *origin)
{
if (! (dwarf_tag (typedie) == DW_TAG_base_type ||
dwarf_tag (typedie) == DW_TAG_enumeration_type))
return -1;
Dwarf_Attribute attr_mem;
Dwarf_Word encoding;
if (dwarf_attr_integrate (typedie, DW_AT_encoding, &attr_mem) != NULL
&& dwarf_formudata (&attr_mem, &encoding) == 0)
return encoding;
(void) origin;
return -1;
}
static void
get_bitfield (struct location *loc,
Dwarf_Die *die, Dwarf_Word *bit_offset, Dwarf_Word *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)
FAIL (loc, N_("cannot get bit field parameters: %s"), dwarf_errmsg (-1));
}
static void
translate_base_fetch (struct obstack *pool, int indent,
Dwarf_Word byte_size, bool signed_p,
struct location **input, const char *target,
int depth)
{
bool deref = false;
if ((*input)->type == loc_noncontiguous)
{
struct location *p = (*input)->pieces;
declare_noncontig_union (pool, indent, input, *input, depth);
Dwarf_Word offset = 0;
char piece[sizeof "u_pieces?.pieces.p" + 20] = "u_pieces?.pieces.p";
piece[8] = (char) ('0' + depth);
int pdepth = depth + 1;
while (p != NULL)
{
struct location *newp = obstack_alloc (pool, sizeof *newp);
*newp = *p;
newp->next = NULL;
(*input)->next = newp;
*input = newp;
snprintf (&piece[sizeof "u_pieces?.pieces.p" - 1], 20,
"%" PRIu64, offset);
translate_base_fetch (pool, indent, p->byte_size, signed_p ,
input, piece, pdepth);
(*input)->type = loc_fragment;
offset += p->byte_size;
p = p->next;
pdepth++;
}
obstack_printf (pool, "%*s%s = u_pieces%d.whole;\n", indent * 2,
"", target, depth);
}
else if ((*input)->type == loc_constant)
{
const unsigned char *constant_block = (*input)->constant_block;
const size_t byte_size = (*input)->byte_size;
size_t i;
declare_noncontig_union (pool, indent, input, *input, depth);
for (i = 0; i < byte_size; ++i)
obstack_printf (pool, "%*su_pieces%d.bytes[%zu] = %#x;\n", indent * 2,
"", depth, i, constant_block[i]);
obstack_printf (pool, "%*s%s = u_pieces%d.whole;\n", indent * 2,
"", target, depth);
}
else
switch (byte_size)
{
case 0: case 1:
case 2:
case 4:
case 8:
obstack_printf (pool, "%*s", indent * 2, "");
deref = emit_base_fetch (pool, byte_size, signed_p, target, *input);
obstack_printf (pool, "\n");
break;
default:
FAIL (*input, N_("fetch is larger than base integer types"));
break;
}
struct location *loc = new_synthetic_loc (*input, deref);
loc->byte_size = byte_size;
loc->type = loc_final;
(*input)->next = loc;
*input = loc;
}
static Dwarf_Word
max_fetch_size (struct location *loc, Dwarf_Die *die)
{
Dwarf_Die cu_mem;
uint8_t address_size;
Dwarf_Die *cu = dwarf_diecu (die, &cu_mem, &address_size, NULL);
if (cu == NULL)
FAIL (loc, N_("cannot determine CU address size from %s: %s"),
dwarf_diename (die), dwarf_errmsg (-1));
return address_size;
}
void
c_translate_fetch (struct obstack *pool, int indent,
Dwarf_Addr dwbias __attribute__ ((unused)),
Dwarf_Die *die, Dwarf_Die *typedie,
struct location **input, const char *target)
{
struct location_context *const ctx = (*input)->context;
assert (ctx->pool == pool);
++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)
byte_size = base_byte_size (typedie, *input);
Dwarf_Attribute encoding_attr;
Dwarf_Word encoding;
if (dwarf_attr_integrate (die, DW_AT_encoding, &encoding_attr) == NULL
|| dwarf_formudata (&encoding_attr, &encoding) != 0)
encoding = base_encoding (typedie, *input);
bool signed_p = encoding == DW_ATE_signed || encoding == DW_ATE_signed_char;
*input = discontiguify (ctx, indent, *input, byte_size,
max_fetch_size (*input, die));
if (dwarf_hasattr_integrate (die, DW_AT_bit_offset))
{
translate_base_fetch (pool, indent, byte_size, signed_p, input, "tmp", 0);
(*input)->type = loc_fragment;
(*input)->address.declare = "tmp";
Dwarf_Word bit_offset = 0;
Dwarf_Word bit_size = 0;
get_bitfield (*input, die, &bit_offset, &bit_size);
obstack_printf (pool, "%*s"
"fetch_bitfield (%s, tmp, %" PRIu64 ", %" PRIu64 ");\n",
indent *2, "", target, bit_offset, bit_size);
struct location *loc = new_synthetic_loc (*input, false);
loc->type = loc_final;
(*input)->next = loc;
*input = loc;
}
else
translate_base_fetch (pool, indent, byte_size, signed_p, input, target, 0);
}
static void
translate_base_store (struct obstack *pool, int indent, Dwarf_Word byte_size,
struct location **input, struct location *store_loc,
const char *rvalue, int depth)
{
bool deref = false;
if (store_loc->type == loc_noncontiguous)
{
declare_noncontig_union (pool, indent, input, store_loc, depth);
obstack_printf (pool, "%*su_pieces%d.whole = %s;\n", indent * 2,
"", depth, rvalue);
struct location *loc = new_synthetic_loc (*input, deref);
loc->type = loc_fragment;
(*input)->next = loc;
*input = loc;
Dwarf_Word offset = 0;
char piece[sizeof "u_pieces?.pieces.p" + 20] = "u_pieces?.pieces.p";
piece[8] = (char) ('0' + depth);
struct location *p;
int pdepth = depth + 1;
for (p = store_loc->pieces; p != NULL; p = p->next)
{
struct location *newp = obstack_alloc (pool, sizeof *newp);
*newp = *p;
newp->next = NULL;
(*input)->next = newp;
*input = newp;
snprintf (&piece[sizeof "u_pieces?.pieces.p" - 1], 20, "%" PRIu64,
offset);
translate_base_store (pool, indent,
p->byte_size, input, *input, piece, pdepth++);
(*input)->type = loc_fragment;
offset += p->byte_size;
}
(*input)->type = loc_final;
}
else
{
switch (byte_size)
{
case 1:
case 2:
case 4:
case 8:
obstack_printf (pool, "%*s", indent * 2, "");
deref = emit_base_store (pool, byte_size, rvalue, store_loc);
obstack_printf (pool, "\n");
break;
default:
FAIL (*input, N_("store is larger than base integer types"));
break;
}
struct location *loc = new_synthetic_loc (*input, deref);
loc->type = loc_final;
(*input)->next = loc;
*input = loc;
}
}
void
c_translate_store (struct obstack *pool, int indent,
Dwarf_Addr dwbias __attribute__ ((unused)),
Dwarf_Die *die, Dwarf_Die *typedie,
struct location **input, const char *rvalue)
{
struct location_context *const ctx = (*input)->context;
assert (ctx->pool == pool);
++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)
byte_size = base_byte_size (typedie, *input);
Dwarf_Attribute encoding_attr;
Dwarf_Word encoding;
if (dwarf_attr_integrate (die, DW_AT_encoding, &encoding_attr) == NULL
|| dwarf_formudata (&encoding_attr, &encoding) != 0)
encoding = base_encoding (typedie, *input);
bool signed_p = (encoding == DW_ATE_signed
|| encoding == DW_ATE_signed_char);
*input = discontiguify (ctx, indent, *input, byte_size,
max_fetch_size (*input, die));
struct location *store_loc = *input;
if (dwarf_hasattr_integrate (die, DW_AT_bit_offset))
{
translate_base_fetch (pool, indent, byte_size, signed_p, input, "tmp", 0);
(*input)->type = loc_fragment;
(*input)->address.declare = "tmp";
Dwarf_Word bit_offset = 0;
Dwarf_Word bit_size = 0;
get_bitfield (*input, die, &bit_offset, &bit_size);
obstack_printf (pool, "%*s"
"store_bitfield (tmp, %s, %" PRIu64 ", %" PRIu64 ");\n",
indent * 2, "", rvalue, bit_offset, bit_size);
struct location *loc = new_synthetic_loc (*input, false);
loc->type = loc_fragment;
(*input)->next = loc;
*input = loc;
rvalue = "tmp";
}
translate_base_store (pool, indent, byte_size, input, store_loc, rvalue, 0);
}
void
c_translate_pointer (struct obstack *pool, int indent,
Dwarf_Addr dwbias __attribute__ ((unused)),
Dwarf_Die *typedie, struct location **input)
{
assert (dwarf_tag (typedie) == DW_TAG_pointer_type ||
dwarf_tag (typedie) == DW_TAG_reference_type ||
dwarf_tag (typedie) == DW_TAG_rvalue_reference_type);
Dwarf_Attribute attr_mem;
Dwarf_Word byte_size;
if (dwarf_attr_integrate (typedie, DW_AT_byte_size, &attr_mem) == NULL)
byte_size = 0;
else if (dwarf_formudata (&attr_mem, &byte_size) != 0)
FAIL (*input,
N_("cannot get byte_size attribute for type %s: %s"),
dwarf_diename (typedie) ?: "<anonymous>",
dwarf_errmsg (-1));
if ((*input)->type == loc_implicit_pointer)
{
struct location *const target = (*input)->pointer.target;
const Dwarf_Sword offset = (*input)->pointer.offset;
(*input)->next = target;
if (offset != 0)
translate_offset (indent, NULL, 0, 0, NULL, &(*input)->next, offset);
*input = (*input)->next;
}
else
{
bool signed_p = false; XXX
translate_base_fetch (pool, indent + 1, byte_size, signed_p, input,
"addr", 0);
(*input)->type = loc_address;
}
}
void
c_translate_addressof (struct obstack *pool, int indent,
Dwarf_Addr dwbias __attribute__ ((unused)),
Dwarf_Die *die,
Dwarf_Die *typedie __attribute__ ((unused)),
struct location **input, const char *target)
{
++indent;
if (dwarf_hasattr_integrate (die, DW_AT_bit_offset)
|| dwarf_hasattr_integrate (die, DW_AT_data_bit_offset))
FAIL (*input, N_("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 (*input, false);
(*input)->next->type = loc_final;
break;
case loc_register:
FAIL (*input, N_("cannot take address of object in register"));
break;
case loc_noncontiguous:
FAIL (*input, N_("cannot take address of noncontiguous object"));
break;
case loc_value:
FAIL (*input, N_("cannot take address of computed value"));
break;
case loc_constant:
FAIL (*input, N_("cannot take address of constant value"));
break;
case loc_unavailable:
FAIL (*input, N_("cannot take address of unavailable value"));
break;
case loc_implicit_pointer:
FAIL (*input, N_("cannot take address of implicit pointer"));
break;
default:
abort ();
break;
}
}
void
c_translate_pointer_store (struct obstack *pool, int indent,
Dwarf_Addr dwbias __attribute__ ((unused)),
Dwarf_Die *typedie, struct location **input,
const char *rvalue)
{
assert (dwarf_tag (typedie) == DW_TAG_pointer_type);
Dwarf_Attribute attr_mem;
Dwarf_Word byte_size;
if (dwarf_attr_integrate (typedie, DW_AT_byte_size, &attr_mem) == NULL)
byte_size = 0;
else if (dwarf_formudata (&attr_mem, &byte_size) != 0)
FAIL (*input,
N_("cannot get byte_size attribute for type %s: %s"),
dwarf_diename (typedie) ?: "<anonymous>",
dwarf_errmsg (-1));
translate_base_store (pool, indent + 1, byte_size,
input, *input, rvalue, 0);
XXX}
static Dwarf_Word
pointer_stride (Dwarf_Die *typedie, struct location *origin)
{
Dwarf_Attribute attr_mem;
Dwarf_Die die_mem = *typedie;
int typetag = dwarf_tag (&die_mem);
while (typetag == DW_TAG_typedef ||
typetag == DW_TAG_const_type ||
typetag == DW_TAG_volatile_type ||
typetag == DW_TAG_restrict_type)
{
if (dwarf_attr_integrate (&die_mem, DW_AT_type, &attr_mem) == NULL
|| dwarf_formref_die (&attr_mem, &die_mem) == NULL)
FAIL (origin, N_("cannot get inner type of type %s: %s"),
dwarf_diename (&die_mem) ?: "<anonymous>",
dwarf_errmsg (-1));
typetag = dwarf_tag (&die_mem);
}
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;
FAIL (origin,
N_("cannot get byte_size attribute for array element type %s: %s"),
dwarf_diename (&die_mem) ?: "<anonymous>",
dwarf_errmsg (-1));
}
FAIL (origin, N_("confused about array element size"));
return 0;
}
static Dwarf_Word
array_stride (Dwarf_Die *typedie, struct location *origin)
{
Dwarf_Attribute attr_mem;
if (dwarf_attr_integrate (typedie, DW_AT_byte_stride, &attr_mem) != NULL)
{
Dwarf_Word stride;
if (dwarf_formudata (&attr_mem, &stride) == 0)
return stride;
FAIL (origin, N_("cannot get byte_stride attribute array type %s: %s"),
dwarf_diename (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)
FAIL (origin, N_("cannot get element type of array type %s: %s"),
dwarf_diename (typedie) ?: "<anonymous>",
dwarf_errmsg (-1));
return pointer_stride (&die_mem, origin);
}
static void
translate_array (struct obstack *pool, int indent,
Dwarf_Die *anydie, Dwarf_Word stride,
struct location **input,
const char *idx, Dwarf_Word const_idx)
{
++indent;
struct location *loc = *input;
while (loc->type == loc_noncontiguous)
{
if (idx != NULL)
FAIL (*input, N_("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)
FAIL (*input, N_("constant index is outside noncontiguous array"));
if (offset % stride != 0 || piece->byte_size < stride)
FAIL (*input, N_("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 (loc, false);
break;
case loc_register:
if (idx != NULL)
FAIL (*input, N_("cannot index array stored in a register"));
else if (const_idx > max_fetch_size (loc, anydie) / stride)
FAIL (*input, N_("constant index is outside array held in register"));
else
{
loc->reg.offset += const_idx * stride;
return;
}
break;
case loc_constant:
if (idx != NULL)
FAIL (*input, N_("cannot index into constant value"));
else if (const_idx > loc->byte_size / stride)
FAIL (*input, N_("constant index is outside constant array value"));
else
{
loc->byte_size = stride;
loc->constant_block += const_idx * stride;
return;
};
break;
case loc_implicit_pointer:
if (idx != NULL)
FAIL (*input, N_("cannot index into implicit pointer"));
else
loc->pointer.offset += const_idx * stride;
break;
case loc_value:
if (idx != NULL || const_idx != 0)
FAIL (*input, N_("cannot index into computed value"));
break;
case loc_unavailable:
if (idx != NULL || const_idx != 0)
FAIL (*input, N_("cannot index into unavailable value"));
break;
default:
abort ();
break;
}
(*input)->next = loc;
*input = (*input)->next;
}
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)
{
assert (dwarf_tag (typedie) == DW_TAG_array_type ||
dwarf_tag (typedie) == DW_TAG_pointer_type);
return translate_array (pool, indent, typedie,
array_stride (typedie, *input),
input, idx, const_idx);
}
void
c_translate_array_pointer (struct obstack *pool, int indent,
Dwarf_Die *typedie, struct location **input,
const char *idx, Dwarf_Word const_idx)
{
return translate_array (pool, indent, typedie,
pointer_stride (typedie, *input),
input, idx, const_idx);
}
#define emit(fmt, ...) fprintf (out, fmt, ## __VA_ARGS__)
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, "");
size_t i;
for (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");
}
}
static void
emit_loc_register (FILE *out, struct location *loc, unsigned int indent,
const char *target)
{
assert (loc->type == loc_register);
if (loc->reg.offset != 0)
FAIL (loc, N_("cannot handle offset into register in fetch"));
emit ("%*s%s = fetch_register (%u);\n",
indent * 2, "", target, loc->reg.regno);
}
static void
emit_loc_address (FILE *out, struct location *loc, unsigned int indent,
const char *target)
{
assert (loc->type == loc_address || loc->type == loc_value);
if (loc->address.stack_depth == 0)
emit ("%s", loc->address.program);
else
{
emit ("%*s{\n", indent * 2, "");
emit ("%*s%s " STACKFMT, (indent + 1) * 2, "",
stack_slot_type (loc, false), 0);
unsigned i;
for (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 ("%*s}\n", indent * 2, "");
}
}
static void
emit_loc_value (FILE *out, struct location *loc, unsigned int indent,
const char *target, bool declare,
bool *used_deref, unsigned int *max_stack)
{
if (declare)
emit ("%*s%s %s;\n", indent * 2, "", stack_slot_type (loc, false), 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:
case loc_value:
emit_loc_address (out, loc, indent, target);
*used_deref = *used_deref || loc->address.used_deref;
if (loc->address.stack_depth > *max_stack)
*max_stack = loc->address.stack_depth;
break;
}
emit ("%*s}\n", --indent * 2, "");
}
bool
c_emit_location (FILE *out, struct location *loc, int indent,
unsigned int *max_stack)
{
emit ("%*s{\n", indent * 2, "");
bool declared_addr = false;
struct location *l;
for (l = loc; l != NULL; l = l->next)
switch (l->type)
{
case loc_decl:
emit ("%s", l->address.program);
break;
case loc_address:
case loc_value:
if (declared_addr)
break;
declared_addr = true;
l->address.declare = "addr";
case loc_fragment:
case loc_final:
if (l->address.declare != NULL)
{
if (l->byte_size == 0 || l->byte_size == (Dwarf_Word) -1)
emit ("%*s%s %s;\n", (indent + 1) * 2, "",
stack_slot_type (l, false), l->address.declare);
else
emit ("%*suint%" PRIu64 "_t %s;\n", (indent + 1) * 2, "",
l->byte_size * 8, l->address.declare);
}
default:
break;
}
bool deref = false;
*max_stack = 0;
for (l = loc; l != NULL; l = l->next)
if (l->frame_base != NULL)
{
emit_loc_value (out, l->frame_base, indent, "frame_base", true,
&deref, max_stack);
break;
}
for (; loc->next != NULL; loc = loc->next)
switch (loc->type)
{
case loc_address:
case loc_value:
emit_loc_value (out, loc, indent + 1, "addr", false, &deref, max_stack);
break;
case loc_fragment:
emit ("%s", loc->address.program);
deref = deref || loc->address.used_deref;
break;
case loc_decl:
case loc_register:
case loc_noncontiguous:
case loc_constant:
case loc_implicit_pointer:
break;
case loc_final: default:
abort ();
break;
}
if (loc->type != loc_final) abort ();
emit ("%s%*s}\n", loc->address.program, indent * 2, "");
if (loc->address.stack_depth > *max_stack)
*max_stack = loc->address.stack_depth;
return deref || loc->address.used_deref;
}
#undef emit