Blame libdw/dwarf_getmacros.c

Packit Service 97d2fb
/* Get macro information.
Packit Service 97d2fb
   Copyright (C) 2002-2009, 2014, 2017, 2018 Red Hat, Inc.
Packit Service 97d2fb
   This file is part of elfutils.
Packit Service 97d2fb
Packit Service 97d2fb
   This file is free software; you can redistribute it and/or modify
Packit Service 97d2fb
   it under the terms of either
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU Lesser General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 3 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 2 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or both in parallel, as here.
Packit Service 97d2fb
Packit Service 97d2fb
   elfutils is distributed in the hope that it will be useful, but
Packit Service 97d2fb
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 97d2fb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 97d2fb
   General Public License for more details.
Packit Service 97d2fb
Packit Service 97d2fb
   You should have received copies of the GNU General Public License and
Packit Service 97d2fb
   the GNU Lesser General Public License along with this program.  If
Packit Service 97d2fb
   not, see <http://www.gnu.org/licenses/>.  */
Packit Service 97d2fb
Packit Service 97d2fb
#ifdef HAVE_CONFIG_H
Packit Service 97d2fb
# include <config.h>
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
#include <assert.h>
Packit Service 97d2fb
#include <dwarf.h>
Packit Service 97d2fb
#include <search.h>
Packit Service 97d2fb
#include <stdlib.h>
Packit Service 97d2fb
#include <string.h>
Packit Service 97d2fb
Packit Service 97d2fb
#include <libdwP.h>
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
get_offset_from (Dwarf_Die *die, int name, Dwarf_Word *retp)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* Get the appropriate attribute.  */
Packit Service 97d2fb
  Dwarf_Attribute attr;
Packit Service 97d2fb
  if (INTUSE(dwarf_attr) (die, name, &attr) == NULL)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Offset into the corresponding section.  */
Packit Service 97d2fb
  return INTUSE(dwarf_formudata) (&attr, retp);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
macro_op_compare (const void *p1, const void *p2)
Packit Service 97d2fb
{
Packit Service 97d2fb
  const Dwarf_Macro_Op_Table *t1 = (const Dwarf_Macro_Op_Table *) p1;
Packit Service 97d2fb
  const Dwarf_Macro_Op_Table *t2 = (const Dwarf_Macro_Op_Table *) p2;
Packit Service 97d2fb
Packit Service 97d2fb
  if (t1->offset < t2->offset)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
  if (t1->offset > t2->offset)
Packit Service 97d2fb
    return 1;
Packit Service 97d2fb
Packit Service 97d2fb
  if (t1->sec_index < t2->sec_index)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
  if (t1->sec_index > t2->sec_index)
Packit Service 97d2fb
    return 1;
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static void
Packit Service 97d2fb
build_table (Dwarf_Macro_Op_Table *table,
Packit Service 97d2fb
	     Dwarf_Macro_Op_Proto op_protos[static 255])
Packit Service 97d2fb
{
Packit Service 97d2fb
  unsigned ct = 0;
Packit Service 97d2fb
  for (unsigned i = 1; i < 256; ++i)
Packit Service 97d2fb
    if (op_protos[i - 1].forms != NULL)
Packit Service 97d2fb
      table->table[table->opcodes[i - 1] = ct++] = op_protos[i - 1];
Packit Service 97d2fb
    else
Packit Service 97d2fb
      table->opcodes[i - 1] = 0xff;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
#define MACRO_PROTO(NAME, ...)					\
Packit Service 97d2fb
  Dwarf_Macro_Op_Proto NAME = ({				\
Packit Service 97d2fb
      static const uint8_t proto[] = {__VA_ARGS__};		\
Packit Service 97d2fb
      (Dwarf_Macro_Op_Proto) {sizeof proto, proto};		\
Packit Service 97d2fb
    })
Packit Service 97d2fb
Packit Service 97d2fb
enum { macinfo_data_size = offsetof (Dwarf_Macro_Op_Table, table[5]) };
Packit Service 97d2fb
static unsigned char macinfo_data[macinfo_data_size]
Packit Service 97d2fb
	__attribute__ ((aligned (__alignof (Dwarf_Macro_Op_Table))));
Packit Service 97d2fb
Packit Service 97d2fb
static __attribute__ ((constructor)) void
Packit Service 97d2fb
init_macinfo_table (void)
Packit Service 97d2fb
{
Packit Service 97d2fb
  MACRO_PROTO (p_udata_str, DW_FORM_udata, DW_FORM_string);
Packit Service 97d2fb
  MACRO_PROTO (p_udata_udata, DW_FORM_udata, DW_FORM_udata);
Packit Service 97d2fb
  MACRO_PROTO (p_none);
Packit Service 97d2fb
Packit Service 97d2fb
  Dwarf_Macro_Op_Proto op_protos[255] =
Packit Service 97d2fb
    {
Packit Service 97d2fb
      [DW_MACINFO_define - 1] = p_udata_str,
Packit Service 97d2fb
      [DW_MACINFO_undef - 1] = p_udata_str,
Packit Service 97d2fb
      [DW_MACINFO_vendor_ext - 1] = p_udata_str,
Packit Service 97d2fb
      [DW_MACINFO_start_file - 1] = p_udata_udata,
Packit Service 97d2fb
      [DW_MACINFO_end_file - 1] = p_none,
Packit Service 97d2fb
      /* If you are adding more elements to this array, increase
Packit Service 97d2fb
	 MACINFO_DATA_SIZE above.  */
Packit Service 97d2fb
    };
Packit Service 97d2fb
Packit Service 97d2fb
  Dwarf_Macro_Op_Table *macinfo_table = (void *) macinfo_data;
Packit Service 97d2fb
  memset (macinfo_table, 0, sizeof macinfo_data);
Packit Service 97d2fb
  build_table (macinfo_table, op_protos);
Packit Service 97d2fb
  macinfo_table->sec_index = IDX_debug_macinfo;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static Dwarf_Macro_Op_Table *
Packit Service 97d2fb
get_macinfo_table (Dwarf *dbg, Dwarf_Word macoff, Dwarf_Die *cudie)
Packit Service 97d2fb
{
Packit Service 97d2fb
  assert (cudie != NULL);
Packit Service 97d2fb
Packit Service 97d2fb
  Dwarf_Attribute attr_mem, *attr
Packit Service 97d2fb
    = INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list, &attr_mem);
Packit Service 97d2fb
  Dwarf_Off line_offset = (Dwarf_Off) -1;
Packit Service 97d2fb
  if (attr != NULL)
Packit Service 97d2fb
    if (unlikely (INTUSE(dwarf_formudata) (attr, &line_offset) != 0))
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  Dwarf_Macro_Op_Table *table = libdw_alloc (dbg, Dwarf_Macro_Op_Table,
Packit Service 97d2fb
					     macinfo_data_size, 1);
Packit Service 97d2fb
  memcpy (table, macinfo_data, macinfo_data_size);
Packit Service 97d2fb
Packit Service 97d2fb
  table->offset = macoff;
Packit Service 97d2fb
  table->sec_index = IDX_debug_macinfo;
Packit Service 97d2fb
  table->line_offset = line_offset;
Packit Service 97d2fb
  table->is_64bit = cudie->cu->address_size == 8;
Packit Service 97d2fb
  table->comp_dir = __libdw_getcompdir (cudie);
Packit Service 97d2fb
Packit Service 97d2fb
  return table;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static Dwarf_Macro_Op_Table *
Packit Service 97d2fb
get_table_for_offset (Dwarf *dbg, Dwarf_Word macoff,
Packit Service 97d2fb
		      const unsigned char *readp,
Packit Service 97d2fb
		      const unsigned char *const endp,
Packit Service 97d2fb
		      Dwarf_Die *cudie)
Packit Service 97d2fb
{
Packit Service 97d2fb
  const unsigned char *startp = readp;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Request at least 3 bytes for header.  */
Packit Service 97d2fb
  if (readp + 3 > endp)
Packit Service 97d2fb
    {
Packit Service 97d2fb
    invalid_dwarf:
Packit Service 97d2fb
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  uint16_t version = read_2ubyte_unaligned_inc (dbg, readp);
Packit Service 97d2fb
  if (version != 4 && version != 5)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdw_seterrno (DWARF_E_INVALID_VERSION);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  uint8_t flags = *readp++;
Packit Service 97d2fb
  bool is_64bit = (flags & 0x1) != 0;
Packit Service 97d2fb
Packit Service 97d2fb
  Dwarf_Off line_offset = (Dwarf_Off) -1;
Packit Service 97d2fb
  if ((flags & 0x2) != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      line_offset = read_addr_unaligned_inc (is_64bit ? 8 : 4, dbg, readp);
Packit Service 97d2fb
      if (readp > endp)
Packit Service 97d2fb
	goto invalid_dwarf;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else if (cudie != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwarf_Attribute attr_mem, *attr
Packit Service 97d2fb
	= INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list, &attr_mem);
Packit Service 97d2fb
      if (attr != NULL)
Packit Service 97d2fb
	if (unlikely (INTUSE(dwarf_formudata) (attr, &line_offset) != 0))
Packit Service 97d2fb
	  return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* """The macinfo entry types defined in this standard may, but
Packit Service 97d2fb
     might not, be described in the table""".
Packit Service 97d2fb
Packit Service 97d2fb
     I.e. these may be present.  It's tempting to simply skip them,
Packit Service 97d2fb
     but it's probably more correct to tolerate that a producer tweaks
Packit Service 97d2fb
     the way certain opcodes are encoded, for whatever reasons.  */
Packit Service 97d2fb
Packit Service 97d2fb
  MACRO_PROTO (p_udata_str, DW_FORM_udata, DW_FORM_string);
Packit Service 97d2fb
  MACRO_PROTO (p_udata_strp, DW_FORM_udata, DW_FORM_strp);
Packit Service 97d2fb
  MACRO_PROTO (p_udata_strsup, DW_FORM_udata, DW_FORM_strp_sup);
Packit Service 97d2fb
  MACRO_PROTO (p_udata_strx, DW_FORM_udata, DW_FORM_strx);
Packit Service 97d2fb
  MACRO_PROTO (p_udata_udata, DW_FORM_udata, DW_FORM_udata);
Packit Service 97d2fb
  MACRO_PROTO (p_secoffset, DW_FORM_sec_offset);
Packit Service 97d2fb
  MACRO_PROTO (p_none);
Packit Service 97d2fb
Packit Service 97d2fb
  Dwarf_Macro_Op_Proto op_protos[255] =
Packit Service 97d2fb
    {
Packit Service 97d2fb
      [DW_MACRO_define - 1] = p_udata_str,
Packit Service 97d2fb
      [DW_MACRO_undef - 1] = p_udata_str,
Packit Service 97d2fb
      [DW_MACRO_define_strp - 1] = p_udata_strp,
Packit Service 97d2fb
      [DW_MACRO_undef_strp - 1] = p_udata_strp,
Packit Service 97d2fb
      [DW_MACRO_start_file - 1] = p_udata_udata,
Packit Service 97d2fb
      [DW_MACRO_end_file - 1] = p_none,
Packit Service 97d2fb
      [DW_MACRO_import - 1] = p_secoffset,
Packit Service 97d2fb
      [DW_MACRO_define_sup - 1] = p_udata_strsup,
Packit Service 97d2fb
      [DW_MACRO_undef_sup - 1] = p_udata_strsup,
Packit Service 97d2fb
      [DW_MACRO_import_sup - 1] = p_secoffset, /* XXX - but in sup!. */
Packit Service 97d2fb
      [DW_MACRO_define_strx - 1] = p_udata_strx,
Packit Service 97d2fb
      [DW_MACRO_undef_strx - 1] = p_udata_strx,
Packit Service 97d2fb
    };
Packit Service 97d2fb
Packit Service 97d2fb
  if ((flags & 0x4) != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      unsigned count = *readp++;
Packit Service 97d2fb
      for (unsigned i = 0; i < count; ++i)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  unsigned opcode = *readp++;
Packit Service 97d2fb
Packit Service 97d2fb
	  Dwarf_Macro_Op_Proto e;
Packit Service 97d2fb
	  if (readp >= endp)
Packit Service 97d2fb
	    goto invalid;
Packit Service 97d2fb
	  get_uleb128 (e.nforms, readp, endp);
Packit Service 97d2fb
	  e.forms = readp;
Packit Service 97d2fb
	  op_protos[opcode - 1] = e;
Packit Service 97d2fb
Packit Service 97d2fb
	  readp += e.nforms;
Packit Service 97d2fb
	  if (readp > endp)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	    invalid:
Packit Service 97d2fb
	      __libdw_seterrno (DWARF_E_INVALID_DWARF);
Packit Service 97d2fb
	      return NULL;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  size_t ct = 0;
Packit Service 97d2fb
  for (unsigned i = 1; i < 256; ++i)
Packit Service 97d2fb
    if (op_protos[i - 1].forms != NULL)
Packit Service 97d2fb
      ++ct;
Packit Service 97d2fb
Packit Service 97d2fb
  /* We support at most 0xfe opcodes defined in the table, as 0xff is
Packit Service 97d2fb
     a value that means that given opcode is not stored at all.  But
Packit Service 97d2fb
     that should be fine, as opcode 0 is not allocated.  */
Packit Service 97d2fb
  assert (ct < 0xff);
Packit Service 97d2fb
Packit Service 97d2fb
  size_t macop_table_size = offsetof (Dwarf_Macro_Op_Table, table[ct]);
Packit Service 97d2fb
Packit Service 97d2fb
  Dwarf_Macro_Op_Table *table = libdw_alloc (dbg, Dwarf_Macro_Op_Table,
Packit Service 97d2fb
					     macop_table_size, 1);
Packit Service 97d2fb
Packit Service 97d2fb
  *table = (Dwarf_Macro_Op_Table) {
Packit Service 97d2fb
    .offset = macoff,
Packit Service 97d2fb
    .sec_index = IDX_debug_macro,
Packit Service 97d2fb
    .line_offset = line_offset,
Packit Service 97d2fb
    .header_len = readp - startp,
Packit Service 97d2fb
    .version = version,
Packit Service 97d2fb
    .is_64bit = is_64bit,
Packit Service 97d2fb
Packit Service 97d2fb
    /* NULL if CUDIE is NULL or DW_AT_comp_dir is absent.  */
Packit Service 97d2fb
    .comp_dir = __libdw_getcompdir (cudie),
Packit Service 97d2fb
  };
Packit Service 97d2fb
  build_table (table, op_protos);
Packit Service 97d2fb
Packit Service 97d2fb
  return table;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static Dwarf_Macro_Op_Table *
Packit Service 97d2fb
cache_op_table (Dwarf *dbg, int sec_index, Dwarf_Off macoff,
Packit Service 97d2fb
		const unsigned char *startp,
Packit Service 97d2fb
		const unsigned char *const endp,
Packit Service 97d2fb
		Dwarf_Die *cudie)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwarf_Macro_Op_Table fake = { .offset = macoff, .sec_index = sec_index };
Packit Service 97d2fb
  Dwarf_Macro_Op_Table **found = tfind (&fake, &dbg->macro_ops,
Packit Service 97d2fb
					macro_op_compare);
Packit Service 97d2fb
  if (found != NULL)
Packit Service 97d2fb
    return *found;
Packit Service 97d2fb
Packit Service 97d2fb
  Dwarf_Macro_Op_Table *table = sec_index == IDX_debug_macro
Packit Service 97d2fb
    ? get_table_for_offset (dbg, macoff, startp, endp, cudie)
Packit Service 97d2fb
    : get_macinfo_table (dbg, macoff, cudie);
Packit Service 97d2fb
Packit Service 97d2fb
  if (table == NULL)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  Dwarf_Macro_Op_Table **ret = tsearch (table, &dbg->macro_ops,
Packit Service 97d2fb
					macro_op_compare);
Packit Service 97d2fb
  if (unlikely (ret == NULL))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdw_seterrno (DWARF_E_NOMEM);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return *ret;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static ptrdiff_t
Packit Service 97d2fb
read_macros (Dwarf *dbg, int sec_index,
Packit Service 97d2fb
	     Dwarf_Off macoff, int (*callback) (Dwarf_Macro *, void *),
Packit Service 97d2fb
	     void *arg, ptrdiff_t offset, bool accept_0xff,
Packit Service 97d2fb
	     Dwarf_Die *cudie)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Elf_Data *d = dbg->sectiondata[sec_index];
Packit Service 97d2fb
  if (unlikely (d == NULL || d->d_buf == NULL))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdw_seterrno (DWARF_E_NO_ENTRY);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely (macoff >= d->d_size))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  const unsigned char *const startp = d->d_buf + macoff;
Packit Service 97d2fb
  const unsigned char *const endp = d->d_buf + d->d_size;
Packit Service 97d2fb
Packit Service 97d2fb
  Dwarf_Macro_Op_Table *table = cache_op_table (dbg, sec_index, macoff,
Packit Service 97d2fb
						startp, endp, cudie);
Packit Service 97d2fb
  if (table == NULL)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  if (offset == 0)
Packit Service 97d2fb
    offset = table->header_len;
Packit Service 97d2fb
Packit Service 97d2fb
  assert (offset >= 0);
Packit Service 97d2fb
  assert (offset < endp - startp);
Packit Service 97d2fb
  const unsigned char *readp = startp + offset;
Packit Service 97d2fb
Packit Service 97d2fb
  while (readp < endp)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      unsigned int opcode = *readp++;
Packit Service 97d2fb
      if (opcode == 0)
Packit Service 97d2fb
	/* Nothing more to do.  */
Packit Service 97d2fb
	return 0;
Packit Service 97d2fb
Packit Service 97d2fb
      if (unlikely (opcode == 0xff && ! accept_0xff))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* See comment below at dwarf_getmacros for explanation of
Packit Service 97d2fb
	     why we are doing this.  */
Packit Service 97d2fb
	  __libdw_seterrno (DWARF_E_INVALID_OPCODE);
Packit Service 97d2fb
	  return -1;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      unsigned int idx = table->opcodes[opcode - 1];
Packit Service 97d2fb
      if (idx == 0xff)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libdw_seterrno (DWARF_E_INVALID_OPCODE);
Packit Service 97d2fb
	  return -1;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      Dwarf_Macro_Op_Proto *proto = &table->table[idx];
Packit Service 97d2fb
Packit Service 97d2fb
      /* A fake CU with bare minimum data to fool dwarf_formX into
Packit Service 97d2fb
	 doing the right thing with the attributes that we put out.
Packit Service 97d2fb
	 We pretend it is the same version as the actual table.
Packit Service 97d2fb
	 Version 4 for the old GNU extension, version 5 for DWARF5.
Packit Service 97d2fb
	 To handle DW_FORM_strx[1234] we set the .str_offsets_base
Packit Service 97d2fb
	 from the given CU.
Packit Service 97d2fb
	 XXX We will need to deal with DW_MACRO_import_sup and change
Packit Service 97d2fb
	 out the dbg somehow for the DW_FORM_sec_offset to make sense.  */
Packit Service 97d2fb
      Dwarf_CU fake_cu = {
Packit Service 97d2fb
	.dbg = dbg,
Packit Service 97d2fb
	.sec_idx = sec_index,
Packit Service 97d2fb
	.version = table->version,
Packit Service 97d2fb
	.offset_size = table->is_64bit ? 8 : 4,
Packit Service 97d2fb
	.str_off_base = str_offsets_base_off (dbg, (cudie != NULL
Packit Service 97d2fb
						    ? cudie->cu: NULL)),
Packit Service 97d2fb
	.startp = (void *) startp + offset,
Packit Service 97d2fb
	.endp = (void *) endp,
Packit Service 97d2fb
      };
Packit Service 97d2fb
Packit Service 97d2fb
      Dwarf_Attribute *attributes;
Packit Service 97d2fb
      Dwarf_Attribute *attributesp = NULL;
Packit Service 97d2fb
      Dwarf_Attribute nattributes[8];
Packit Service 97d2fb
      if (unlikely (proto->nforms > 8))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  attributesp = malloc (sizeof (Dwarf_Attribute) * proto->nforms);
Packit Service 97d2fb
	  if (attributesp == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libdw_seterrno (DWARF_E_NOMEM);
Packit Service 97d2fb
	      return -1;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  attributes = attributesp;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	attributes = &nattributes[0];
Packit Service 97d2fb
Packit Service 97d2fb
      for (Dwarf_Word i = 0; i < proto->nforms; ++i)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* We pretend this is a DW_AT[_GNU]_macros attribute so that
Packit Service 97d2fb
	     DW_FORM_sec_offset forms get correctly interpreted as
Packit Service 97d2fb
	     offset into .debug_macro.  XXX Deal with DW_MACRO_import_sup
Packit Service 97d2fb
	     (swap .dbg) for DW_FORM_sec_offset? */
Packit Service 97d2fb
	  attributes[i].code = (fake_cu.version == 4 ? DW_AT_GNU_macros
Packit Service 97d2fb
						     : DW_AT_macros);
Packit Service 97d2fb
	  attributes[i].form = proto->forms[i];
Packit Service 97d2fb
	  attributes[i].valp = (void *) readp;
Packit Service 97d2fb
	  attributes[i].cu = &fake_cu;
Packit Service 97d2fb
Packit Service 97d2fb
	  /* We don't want forms that aren't allowed because they could
Packit Service 97d2fb
	     read from the "abbrev" like DW_FORM_implicit_const.  */
Packit Service 97d2fb
	  if (! libdw_valid_user_form (attributes[i].form))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libdw_seterrno (DWARF_E_INVALID_DWARF);
Packit Service 97d2fb
	      free (attributesp);
Packit Service 97d2fb
	      return -1;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  size_t len = __libdw_form_val_len (&fake_cu, proto->forms[i], readp);
Packit Service 97d2fb
	  if (unlikely (len == (size_t) -1))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (attributesp);
Packit Service 97d2fb
	      return -1;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  readp += len;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      Dwarf_Macro macro = {
Packit Service 97d2fb
	.table = table,
Packit Service 97d2fb
	.opcode = opcode,
Packit Service 97d2fb
	.attributes = attributes,
Packit Service 97d2fb
      };
Packit Service 97d2fb
Packit Service 97d2fb
      int res = callback (&macro, arg);
Packit Service 97d2fb
      if (unlikely (attributesp != NULL))
Packit Service 97d2fb
	free (attributesp);
Packit Service 97d2fb
Packit Service 97d2fb
      if (res != DWARF_CB_OK)
Packit Service 97d2fb
	return readp - startp;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Token layout:
Packit Service 97d2fb
Packit Service 97d2fb
   - The highest bit is used for distinguishing between callers that
Packit Service 97d2fb
     know that opcode 0xff may have one of two incompatible meanings.
Packit Service 97d2fb
     The mask that we use for selecting this bit is
Packit Service 97d2fb
     DWARF_GETMACROS_START.
Packit Service 97d2fb
Packit Service 97d2fb
   - The rest of the token (31 or 63 bits) encodes address inside the
Packit Service 97d2fb
     macro unit.
Packit Service 97d2fb
Packit Service 97d2fb
   Besides, token value of 0 signals end of iteration and -1 is
Packit Service 97d2fb
   reserved for signaling errors.  That means it's impossible to
Packit Service 97d2fb
   represent maximum offset of a .debug_macro unit to new-style
Packit Service 97d2fb
   callers (which in practice decreases the permissible macro unit
Packit Service 97d2fb
   size by another 1 byte).  */
Packit Service 97d2fb
Packit Service 97d2fb
static ptrdiff_t
Packit Service 97d2fb
token_from_offset (ptrdiff_t offset, bool accept_0xff)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (offset == -1 || offset == 0)
Packit Service 97d2fb
    return offset;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Make sure the offset didn't overflow into the flag bit.  */
Packit Service 97d2fb
  if ((offset & DWARF_GETMACROS_START) != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdw_seterrno (DWARF_E_TOO_BIG);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (accept_0xff)
Packit Service 97d2fb
    offset |= DWARF_GETMACROS_START;
Packit Service 97d2fb
Packit Service 97d2fb
  return offset;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static ptrdiff_t
Packit Service 97d2fb
offset_from_token (ptrdiff_t token, bool *accept_0xffp)
Packit Service 97d2fb
{
Packit Service 97d2fb
  *accept_0xffp = (token & DWARF_GETMACROS_START) != 0;
Packit Service 97d2fb
  token &= ~DWARF_GETMACROS_START;
Packit Service 97d2fb
Packit Service 97d2fb
  return token;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static ptrdiff_t
Packit Service 97d2fb
gnu_macros_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
Packit Service 97d2fb
			  int (*callback) (Dwarf_Macro *, void *),
Packit Service 97d2fb
			  void *arg, ptrdiff_t offset, bool accept_0xff,
Packit Service 97d2fb
			  Dwarf_Die *cudie)
Packit Service 97d2fb
{
Packit Service 97d2fb
  assert (offset >= 0);
Packit Service 97d2fb
Packit Service 97d2fb
  if (macoff >= dbg->sectiondata[IDX_debug_macro]->d_size)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdw_seterrno (DWARF_E_INVALID_OFFSET);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return read_macros (dbg, IDX_debug_macro, macoff,
Packit Service 97d2fb
		      callback, arg, offset, accept_0xff, cudie);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static ptrdiff_t
Packit Service 97d2fb
macro_info_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
Packit Service 97d2fb
			  int (*callback) (Dwarf_Macro *, void *),
Packit Service 97d2fb
			  void *arg, ptrdiff_t offset, Dwarf_Die *cudie)
Packit Service 97d2fb
{
Packit Service 97d2fb
  assert (offset >= 0);
Packit Service 97d2fb
Packit Service 97d2fb
  return read_macros (dbg, IDX_debug_macinfo, macoff,
Packit Service 97d2fb
		      callback, arg, offset, true, cudie);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
ptrdiff_t
Packit Service 97d2fb
dwarf_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
Packit Service 97d2fb
		     int (*callback) (Dwarf_Macro *, void *),
Packit Service 97d2fb
		     void *arg, ptrdiff_t token)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (dbg == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdw_seterrno (DWARF_E_NO_DWARF);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  bool accept_0xff;
Packit Service 97d2fb
  ptrdiff_t offset = offset_from_token (token, &accept_0xff);
Packit Service 97d2fb
  assert (accept_0xff);
Packit Service 97d2fb
Packit Service 97d2fb
  offset = gnu_macros_getmacros_off (dbg, macoff, callback, arg, offset,
Packit Service 97d2fb
				     accept_0xff, NULL);
Packit Service 97d2fb
Packit Service 97d2fb
  return token_from_offset (offset, accept_0xff);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
ptrdiff_t
Packit Service 97d2fb
dwarf_getmacros (Dwarf_Die *cudie, int (*callback) (Dwarf_Macro *, void *),
Packit Service 97d2fb
		 void *arg, ptrdiff_t token)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (cudie == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdw_seterrno (DWARF_E_NO_DWARF);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* This function might be called from a code that expects to see
Packit Service 97d2fb
     DW_MACINFO_* opcodes, not DW_MACRO_{GNU_,}* ones.  It is fine to
Packit Service 97d2fb
     serve most DW_MACRO_{GNU_,}* opcodes to such code, because those
Packit Service 97d2fb
     whose values are the same as DW_MACINFO_* ones also have the same
Packit Service 97d2fb
     behavior.  It is not very likely that a .debug_macro section
Packit Service 97d2fb
     would only use the part of opcode space that it shares with
Packit Service 97d2fb
     .debug_macinfo, but it is possible.  Serving the opcodes that are
Packit Service 97d2fb
     only valid in DW_MACRO_{GNU_,}* domain is OK as well, because
Packit Service 97d2fb
     clients in general need to be ready that newer standards define
Packit Service 97d2fb
     more opcodes, and have coping mechanisms for unfamiliar opcodes.
Packit Service 97d2fb
Packit Service 97d2fb
     The one exception to the above rule is opcode 0xff, which has
Packit Service 97d2fb
     concrete semantics in .debug_macinfo, but falls into vendor block
Packit Service 97d2fb
     in .debug_macro, and can be assigned to do whatever.  There is
Packit Service 97d2fb
     some small probability that the two opcodes would look
Packit Service 97d2fb
     superficially similar enough that a client would be confused and
Packit Service 97d2fb
     misbehave as a result.  For this reason, we refuse to serve
Packit Service 97d2fb
     through this interface 0xff's originating from .debug_macro
Packit Service 97d2fb
     unless the TOKEN that we obtained indicates the call originates
Packit Service 97d2fb
     from a new-style caller.  See above for details on what
Packit Service 97d2fb
     information is encoded into tokens.  */
Packit Service 97d2fb
Packit Service 97d2fb
  bool accept_0xff;
Packit Service 97d2fb
  ptrdiff_t offset = offset_from_token (token, &accept_0xff);
Packit Service 97d2fb
Packit Service 97d2fb
  /* DW_AT_macro_info */
Packit Service 97d2fb
  if (dwarf_hasattr (cudie, DW_AT_macro_info))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwarf_Word macoff;
Packit Service 97d2fb
      if (get_offset_from (cudie, DW_AT_macro_info, &macoff) != 0)
Packit Service 97d2fb
	return -1;
Packit Service 97d2fb
      offset = macro_info_getmacros_off (cudie->cu->dbg, macoff,
Packit Service 97d2fb
					 callback, arg, offset, cudie);
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* DW_AT_GNU_macros, DW_AT_macros */
Packit Service 97d2fb
      Dwarf_Word macoff;
Packit Service 97d2fb
      if (get_offset_from (cudie, DW_AT_GNU_macros, &macoff) != 0
Packit Service 97d2fb
	  && get_offset_from (cudie, DW_AT_macros, &macoff) != 0)
Packit Service 97d2fb
	return -1;
Packit Service 97d2fb
      offset = gnu_macros_getmacros_off (cudie->cu->dbg, macoff,
Packit Service 97d2fb
					 callback, arg, offset, accept_0xff,
Packit Service 97d2fb
					 cudie);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return token_from_offset (offset, accept_0xff);
Packit Service 97d2fb
}