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