1da0c48c4Sopenharmony_ci/* Get abbreviation at given offset.
2da0c48c4Sopenharmony_ci   Copyright (C) 2003, 2004, 2005, 2006, 2014, 2017 Red Hat, Inc.
3da0c48c4Sopenharmony_ci   This file is part of elfutils.
4da0c48c4Sopenharmony_ci   Written by Ulrich Drepper <drepper@redhat.com>, 2003.
5da0c48c4Sopenharmony_ci
6da0c48c4Sopenharmony_ci   This file is free software; you can redistribute it and/or modify
7da0c48c4Sopenharmony_ci   it under the terms of either
8da0c48c4Sopenharmony_ci
9da0c48c4Sopenharmony_ci     * the GNU Lesser General Public License as published by the Free
10da0c48c4Sopenharmony_ci       Software Foundation; either version 3 of the License, or (at
11da0c48c4Sopenharmony_ci       your option) any later version
12da0c48c4Sopenharmony_ci
13da0c48c4Sopenharmony_ci   or
14da0c48c4Sopenharmony_ci
15da0c48c4Sopenharmony_ci     * the GNU General Public License as published by the Free
16da0c48c4Sopenharmony_ci       Software Foundation; either version 2 of the License, or (at
17da0c48c4Sopenharmony_ci       your option) any later version
18da0c48c4Sopenharmony_ci
19da0c48c4Sopenharmony_ci   or both in parallel, as here.
20da0c48c4Sopenharmony_ci
21da0c48c4Sopenharmony_ci   elfutils is distributed in the hope that it will be useful, but
22da0c48c4Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
23da0c48c4Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24da0c48c4Sopenharmony_ci   General Public License for more details.
25da0c48c4Sopenharmony_ci
26da0c48c4Sopenharmony_ci   You should have received copies of the GNU General Public License and
27da0c48c4Sopenharmony_ci   the GNU Lesser General Public License along with this program.  If
28da0c48c4Sopenharmony_ci   not, see <http://www.gnu.org/licenses/>.  */
29da0c48c4Sopenharmony_ci
30da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H
31da0c48c4Sopenharmony_ci# include <config.h>
32da0c48c4Sopenharmony_ci#endif
33da0c48c4Sopenharmony_ci
34da0c48c4Sopenharmony_ci#include <dwarf.h>
35da0c48c4Sopenharmony_ci#include "libdwP.h"
36da0c48c4Sopenharmony_ci
37da0c48c4Sopenharmony_ci
38da0c48c4Sopenharmony_ciDwarf_Abbrev *
39da0c48c4Sopenharmony_ciinternal_function
40da0c48c4Sopenharmony_ci__libdw_getabbrev (Dwarf *dbg, struct Dwarf_CU *cu, Dwarf_Off offset,
41da0c48c4Sopenharmony_ci		   size_t *lengthp, Dwarf_Abbrev *result)
42da0c48c4Sopenharmony_ci{
43da0c48c4Sopenharmony_ci  /* Don't fail if there is not .debug_abbrev section.  */
44da0c48c4Sopenharmony_ci  if (dbg->sectiondata[IDX_debug_abbrev] == NULL)
45da0c48c4Sopenharmony_ci    return NULL;
46da0c48c4Sopenharmony_ci
47da0c48c4Sopenharmony_ci  if (offset >= dbg->sectiondata[IDX_debug_abbrev]->d_size)
48da0c48c4Sopenharmony_ci    {
49da0c48c4Sopenharmony_ci      __libdw_seterrno (DWARF_E_INVALID_OFFSET);
50da0c48c4Sopenharmony_ci      return NULL;
51da0c48c4Sopenharmony_ci    }
52da0c48c4Sopenharmony_ci
53da0c48c4Sopenharmony_ci  const unsigned char *abbrevp
54da0c48c4Sopenharmony_ci    = (unsigned char *) dbg->sectiondata[IDX_debug_abbrev]->d_buf + offset;
55da0c48c4Sopenharmony_ci
56da0c48c4Sopenharmony_ci  if (*abbrevp == '\0')
57da0c48c4Sopenharmony_ci    /* We are past the last entry.  */
58da0c48c4Sopenharmony_ci    return DWARF_END_ABBREV;
59da0c48c4Sopenharmony_ci
60da0c48c4Sopenharmony_ci  /* 7.5.3 Abbreviations Tables
61da0c48c4Sopenharmony_ci
62da0c48c4Sopenharmony_ci     [...] Each declaration begins with an unsigned LEB128 number
63da0c48c4Sopenharmony_ci     representing the abbreviation code itself.  [...]  The
64da0c48c4Sopenharmony_ci     abbreviation code is followed by another unsigned LEB128
65da0c48c4Sopenharmony_ci     number that encodes the entry's tag.  [...]
66da0c48c4Sopenharmony_ci
67da0c48c4Sopenharmony_ci     [...] Following the tag encoding is a 1-byte value that
68da0c48c4Sopenharmony_ci     determines whether a debugging information entry using this
69da0c48c4Sopenharmony_ci     abbreviation has child entries or not. [...]
70da0c48c4Sopenharmony_ci
71da0c48c4Sopenharmony_ci     [...] Finally, the child encoding is followed by a series of
72da0c48c4Sopenharmony_ci     attribute specifications. Each attribute specification
73da0c48c4Sopenharmony_ci     consists of two parts. The first part is an unsigned LEB128
74da0c48c4Sopenharmony_ci     number representing the attribute's name. The second part is
75da0c48c4Sopenharmony_ci     an unsigned LEB128 number representing the attribute's form.  */
76da0c48c4Sopenharmony_ci  const unsigned char *end = (dbg->sectiondata[IDX_debug_abbrev]->d_buf
77da0c48c4Sopenharmony_ci			      + dbg->sectiondata[IDX_debug_abbrev]->d_size);
78da0c48c4Sopenharmony_ci  const unsigned char *start_abbrevp = abbrevp;
79da0c48c4Sopenharmony_ci  unsigned int code;
80da0c48c4Sopenharmony_ci  get_uleb128 (code, abbrevp, end);
81da0c48c4Sopenharmony_ci
82da0c48c4Sopenharmony_ci  /* Check whether this code is already in the hash table.  */
83da0c48c4Sopenharmony_ci  bool foundit = false;
84da0c48c4Sopenharmony_ci  Dwarf_Abbrev *abb = NULL;
85da0c48c4Sopenharmony_ci  if (cu == NULL
86da0c48c4Sopenharmony_ci      || (abb = Dwarf_Abbrev_Hash_find (&cu->abbrev_hash, code)) == NULL)
87da0c48c4Sopenharmony_ci    {
88da0c48c4Sopenharmony_ci      if (result == NULL)
89da0c48c4Sopenharmony_ci	abb = libdw_typed_alloc (dbg, Dwarf_Abbrev);
90da0c48c4Sopenharmony_ci      else
91da0c48c4Sopenharmony_ci	abb = result;
92da0c48c4Sopenharmony_ci    }
93da0c48c4Sopenharmony_ci  else
94da0c48c4Sopenharmony_ci    {
95da0c48c4Sopenharmony_ci      foundit = true;
96da0c48c4Sopenharmony_ci
97da0c48c4Sopenharmony_ci      if (unlikely (abb->offset != offset))
98da0c48c4Sopenharmony_ci	{
99da0c48c4Sopenharmony_ci	  /* A duplicate abbrev code at a different offset,
100da0c48c4Sopenharmony_ci	     that should never happen.  */
101da0c48c4Sopenharmony_ci	invalid:
102da0c48c4Sopenharmony_ci	  if (! foundit)
103da0c48c4Sopenharmony_ci	    libdw_typed_unalloc (dbg, Dwarf_Abbrev);
104da0c48c4Sopenharmony_ci	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
105da0c48c4Sopenharmony_ci	  return NULL;
106da0c48c4Sopenharmony_ci	}
107da0c48c4Sopenharmony_ci
108da0c48c4Sopenharmony_ci      /* If the caller doesn't need the length we are done.  */
109da0c48c4Sopenharmony_ci      if (lengthp == NULL)
110da0c48c4Sopenharmony_ci	goto out;
111da0c48c4Sopenharmony_ci    }
112da0c48c4Sopenharmony_ci
113da0c48c4Sopenharmony_ci  /* If there is already a value in the hash table we are going to
114da0c48c4Sopenharmony_ci     overwrite its content.  This must not be a problem, since the
115da0c48c4Sopenharmony_ci     content better be the same.  */
116da0c48c4Sopenharmony_ci  abb->code = code;
117da0c48c4Sopenharmony_ci  if (abbrevp >= end)
118da0c48c4Sopenharmony_ci    goto invalid;
119da0c48c4Sopenharmony_ci  get_uleb128 (abb->tag, abbrevp, end);
120da0c48c4Sopenharmony_ci  if (abbrevp + 1 >= end)
121da0c48c4Sopenharmony_ci    goto invalid;
122da0c48c4Sopenharmony_ci  abb->has_children = *abbrevp++ == DW_CHILDREN_yes;
123da0c48c4Sopenharmony_ci  abb->attrp = (unsigned char *) abbrevp;
124da0c48c4Sopenharmony_ci  abb->offset = offset;
125da0c48c4Sopenharmony_ci
126da0c48c4Sopenharmony_ci  /* Skip over all the attributes and check rest of the abbrev is valid.  */
127da0c48c4Sopenharmony_ci  unsigned int attrname;
128da0c48c4Sopenharmony_ci  unsigned int attrform;
129da0c48c4Sopenharmony_ci  do
130da0c48c4Sopenharmony_ci    {
131da0c48c4Sopenharmony_ci      if (abbrevp >= end)
132da0c48c4Sopenharmony_ci	goto invalid;
133da0c48c4Sopenharmony_ci      get_uleb128 (attrname, abbrevp, end);
134da0c48c4Sopenharmony_ci      if (abbrevp >= end)
135da0c48c4Sopenharmony_ci	goto invalid;
136da0c48c4Sopenharmony_ci      get_uleb128 (attrform, abbrevp, end);
137da0c48c4Sopenharmony_ci      if (attrform == DW_FORM_implicit_const)
138da0c48c4Sopenharmony_ci	{
139da0c48c4Sopenharmony_ci	  int64_t formval __attribute__((__unused__));
140da0c48c4Sopenharmony_ci	  if (abbrevp >= end)
141da0c48c4Sopenharmony_ci	    goto invalid;
142da0c48c4Sopenharmony_ci	  get_sleb128 (formval, abbrevp, end);
143da0c48c4Sopenharmony_ci	}
144da0c48c4Sopenharmony_ci    }
145da0c48c4Sopenharmony_ci  while (attrname != 0 || attrform != 0);
146da0c48c4Sopenharmony_ci
147da0c48c4Sopenharmony_ci  /* Return the length to the caller if she asked for it.  */
148da0c48c4Sopenharmony_ci  if (lengthp != NULL)
149da0c48c4Sopenharmony_ci    *lengthp = abbrevp - start_abbrevp;
150da0c48c4Sopenharmony_ci
151da0c48c4Sopenharmony_ci  /* Add the entry to the hash table.  */
152da0c48c4Sopenharmony_ci  if (cu != NULL && ! foundit)
153da0c48c4Sopenharmony_ci    if (Dwarf_Abbrev_Hash_insert (&cu->abbrev_hash, abb->code, abb) == -1)
154da0c48c4Sopenharmony_ci      {
155da0c48c4Sopenharmony_ci	/* The entry was already in the table, remove the one we just
156da0c48c4Sopenharmony_ci	   created and get the one already inserted.  */
157da0c48c4Sopenharmony_ci	libdw_typed_unalloc (dbg, Dwarf_Abbrev);
158da0c48c4Sopenharmony_ci	abb = Dwarf_Abbrev_Hash_find (&cu->abbrev_hash, code);
159da0c48c4Sopenharmony_ci      }
160da0c48c4Sopenharmony_ci
161da0c48c4Sopenharmony_ci out:
162da0c48c4Sopenharmony_ci  return abb;
163da0c48c4Sopenharmony_ci}
164da0c48c4Sopenharmony_ci
165da0c48c4Sopenharmony_ci
166da0c48c4Sopenharmony_ciDwarf_Abbrev *
167da0c48c4Sopenharmony_cidwarf_getabbrev (Dwarf_Die *die, Dwarf_Off offset, size_t *lengthp)
168da0c48c4Sopenharmony_ci{
169da0c48c4Sopenharmony_ci  if (die == NULL || die->cu == NULL)
170da0c48c4Sopenharmony_ci    return NULL;
171da0c48c4Sopenharmony_ci
172da0c48c4Sopenharmony_ci  Dwarf_CU *cu = die->cu;
173da0c48c4Sopenharmony_ci  Dwarf *dbg = cu->dbg;
174da0c48c4Sopenharmony_ci  Dwarf_Off abbrev_offset = cu->orig_abbrev_offset;
175da0c48c4Sopenharmony_ci  Elf_Data *data = dbg->sectiondata[IDX_debug_abbrev];
176da0c48c4Sopenharmony_ci  if (data == NULL)
177da0c48c4Sopenharmony_ci    return NULL;
178da0c48c4Sopenharmony_ci
179da0c48c4Sopenharmony_ci  if (offset >= data->d_size - abbrev_offset)
180da0c48c4Sopenharmony_ci    {
181da0c48c4Sopenharmony_ci      __libdw_seterrno (DWARF_E_INVALID_OFFSET);
182da0c48c4Sopenharmony_ci      return NULL;
183da0c48c4Sopenharmony_ci    }
184da0c48c4Sopenharmony_ci
185da0c48c4Sopenharmony_ci  return __libdw_getabbrev (dbg, cu, abbrev_offset + offset, lengthp, NULL);
186da0c48c4Sopenharmony_ci}
187