summaryrefslogtreecommitdiffstats
path: root/libdw/dwarf_nextcu.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdw/dwarf_nextcu.c')
-rw-r--r--libdw/dwarf_nextcu.c196
1 files changed, 154 insertions, 42 deletions
diff --git a/libdw/dwarf_nextcu.c b/libdw/dwarf_nextcu.c
index fa9b0af3..be113270 100644
--- a/libdw/dwarf_nextcu.c
+++ b/libdw/dwarf_nextcu.c
@@ -1,5 +1,5 @@
/* Advance to next CU header.
- Copyright (C) 2002-2010 Red Hat, Inc.
+ Copyright (C) 2002-2010, 2016, 2017 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper@redhat.com>, 2002.
@@ -39,11 +39,31 @@ int
dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off,
size_t *header_sizep, Dwarf_Half *versionp,
Dwarf_Off *abbrev_offsetp, uint8_t *address_sizep,
- uint8_t *offset_sizep, uint64_t *type_signaturep,
- Dwarf_Off *type_offsetp)
+ uint8_t *offset_sizep, uint64_t *v4_type_signaturep,
+ Dwarf_Off *v4_type_offsetp)
{
- const bool debug_types = type_signaturep != NULL;
- const size_t sec_idx = debug_types ? IDX_debug_types : IDX_debug_info;
+ const bool v4_debug_types = v4_type_signaturep != NULL;
+ return __libdw_next_unit (dwarf, v4_debug_types, off, next_off,
+ header_sizep, versionp, NULL,
+ abbrev_offsetp, address_sizep, offset_sizep,
+ v4_type_signaturep, v4_type_offsetp);
+}
+INTDEF(dwarf_next_unit)
+
+int
+internal_function
+__libdw_next_unit (Dwarf *dwarf, bool v4_debug_types, Dwarf_Off off,
+ Dwarf_Off *next_off, size_t *header_sizep,
+ Dwarf_Half *versionp, uint8_t *unit_typep,
+ Dwarf_Off *abbrev_offsetp, uint8_t *address_sizep,
+ uint8_t *offset_sizep, uint64_t *unit_id8p,
+ Dwarf_Off *subdie_offsetp)
+{
+ /* Note that debug_type units come from .debug_types in DWARF < 5 and
+ from .debug_info in DWARF >= 5. If the user requested the
+ v4_type_signature we return from .debug_types always. If no signature
+ is requested we return units (any type) from .debug_info. */
+ const size_t sec_idx = v4_debug_types ? IDX_debug_types : IDX_debug_info;
/* Maybe there has been an error before. */
if (dwarf == NULL)
@@ -61,12 +81,14 @@ dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off,
return 1;
}
- /* This points into the .debug_info section to the beginning of the
- CU entry. */
+ /* This points into the .debug_info or .debug_types section to the
+ beginning of the CU entry. */
const unsigned char *data = dwarf->sectiondata[sec_idx]->d_buf;
const unsigned char *bytes = data + off;
- /* The format of the CU header is described in dwarf2p1 7.5.1:
+ /* The format of the CU header is described in dwarf2p1 7.5.1 and
+ changed in DWARFv5 (to include unit type, switch location of some
+ fields and add some optional fields).
1. A 4-byte or 12-byte unsigned integer representing the length
of the .debug_info contribution for that compilation unit, not
@@ -74,23 +96,58 @@ dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off,
this is a 4-byte unsigned integer (which must be less than
0xfffffff0); in the 64-bit DWARF format, this consists of the
4-byte value 0xffffffff followed by an 8-byte unsigned integer
- that gives the actual length (see Section 7.2.2).
+ that gives the actual length (see Section 7.2.2). This field
+ indicates whether this unit is 32-bit of 64-bit DWARF, which
+ affects all other offset fields in this header.
2. A 2-byte unsigned integer representing the version of the
DWARF information for that compilation unit. For DWARF Version
- 2.1, the value in this field is 2.
+ 2.1, the value in this field is 2 (3 for v3, 4 for v4, 5 for v5).
+ This fields determines the order of the next fields and whether
+ there are any optional fields in this header.
- 3. A 4-byte or 8-byte unsigned offset into the .debug_abbrev
+ 3. For DWARF 2, 3 and 4 (including v4 type units):
+ A 4-byte or 8-byte unsigned offset into the .debug_abbrev
section. This offset associates the compilation unit with a
particular set of debugging information entry abbreviations. In
the 32-bit DWARF format, this is a 4-byte unsigned length; in
the 64-bit DWARF format, this is an 8-byte unsigned length (see
Section 7.4).
- 4. A 1-byte unsigned integer representing the size in bytes of
+ For DWARF 5:
+ A 1-byte unsigned integer representing the unit (header) type.
+ This field determines what the optional fields in the header
+ represent. If this is an unknown unit type then we cannot
+ assume anything about the rest of the unit (header).
+
+ 4. For all DWARF versions (including v4 type units):
+ A 1-byte unsigned integer representing the size in bytes of
an address on the target architecture. If the system uses
segmented addressing, this value represents the size of the
- offset portion of an address. */
+ offset portion of an address. This is the last field in the header
+ for DWARF versions 2, 3 and 4 (except for v4 type units).
+
+ 5. For DWARF 5 only (this is field 3 for DWARF 2, 3, 4 and v4 types):
+ A 4-byte or 8-byte unsigned offset into the .debug_abbrev
+ section. This offset associates the compilation unit with a
+ particular set of debugging information entry abbreviations. In
+ the 32-bit DWARF format, this is a 4-byte unsigned length; in
+ the 64-bit DWARF format, this is an 8-byte unsigned length.
+
+ 6. For v4 type units (this is really field 5 for v4 types) and
+ DWARF 5 optional (skeleton, split_compile, type and
+ split_type): An 8 byte (opaque) integer constant value. For
+ v4 and v5 type units this is the type signature. For skeleton
+ and split compile units this is the compilation ID.
+
+ 7. For v4 type units (this is really field 6 for v4 types) and
+ DWARF 5 optional (type and split_type) and v4 type units:
+ A 4-byte or 8-byte unsigned offset. In the 32-bit DWARF format,
+ this is a 4-byte unsigned length; in the 64-bit DWARF format,
+ this is an 8-byte unsigned length. This is the type DIE offset
+ (which is not necessarily the first DIE in the unit).
+ */
+
uint64_t length = read_4ubyte_unaligned_inc (dwarf, bytes);
size_t offset_size = 4;
/* Lengths of 0xfffffff0 - 0xffffffff are escape codes. Oxffffffff is
@@ -106,14 +163,6 @@ dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off,
return -1;
}
- /* Now we know how large the header is. */
- if (unlikely (DIE_OFFSET_FROM_CU_OFFSET (off, offset_size, debug_types)
- >= dwarf->sectiondata[sec_idx]->d_size))
- {
- *next_off = -1;
- return 1;
- }
-
if (length == DWARF3_LENGTH_64_BIT)
/* This is a 64-bit DWARF format. */
length = read_8ubyte_unaligned_inc (dwarf, bytes);
@@ -121,41 +170,99 @@ dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off,
/* Read the version stamp. Always a 16-bit value. */
uint_fast16_t version = read_2ubyte_unaligned_inc (dwarf, bytes);
+ /* We keep unit_type at zero for older DWARF since we cannot
+ easily guess whether it is a compile or partial unit. */
+ uint8_t unit_type = 0;
+ if (version >= 5)
+ unit_type = *bytes++;
+
+ /* All these are optional. */
+ Dwarf_Off subdie_off = 0;
+ uint64_t sig_id = 0;
+ Dwarf_Off abbrev_offset = 0;
+ uint8_t address_size = 0;
+
+ if (version < 2 || version > 5
+ || (version == 5 && ! (unit_type == DW_UT_compile
+ || unit_type == DW_UT_partial
+ || unit_type == DW_UT_skeleton
+ || unit_type == DW_UT_split_compile
+ || unit_type == DW_UT_type
+ || unit_type == DW_UT_split_type)))
+ {
+ /* We cannot really know more about the header. Just report
+ the length of the unit, version and unit type. */
+ goto done;
+ }
+
+ /* We have to guess the unit_type. But we don't have a real CUDIE. */
+ if (version < 5)
+ unit_type = v4_debug_types ? DW_UT_type : DW_UT_compile;
+
+ /* Now we know how large the header is (should be). */
+ if (unlikely (__libdw_first_die_from_cu_start (off, offset_size, version,
+ unit_type)
+ >= dwarf->sectiondata[sec_idx]->d_size))
+ {
+ *next_off = -1;
+ return 1;
+ }
+
+ /* The address size. Always an 8-bit value.
+ Comes after abbrev_offset for version < 5, otherwise unit type
+ and address size (if a known unit type) comes before abbrev_offset. */
+ if (version >= 5)
+ address_size = *bytes++;
+
/* Get offset in .debug_abbrev. Note that the size of the entry
depends on whether this is a 32-bit or 64-bit DWARF definition. */
- uint64_t abbrev_offset;
if (__libdw_read_offset_inc (dwarf, sec_idx, &bytes, offset_size,
&abbrev_offset, IDX_debug_abbrev, 0))
return -1;
- /* The address size. Always an 8-bit value. */
- uint8_t address_size = *bytes++;
+ if (version < 5)
+ address_size = *bytes++;
- if (debug_types)
+ /* Extra fields, signature/id and type offset/padding. */
+ if (v4_debug_types
+ || (version >= 5
+ && (unit_type == DW_UT_skeleton || unit_type == DW_UT_split_compile
+ || unit_type == DW_UT_type || unit_type == DW_UT_split_type)))
{
- uint64_t type_sig8 = read_8ubyte_unaligned_inc (dwarf, bytes);
-
- Dwarf_Off type_offset;
- if (__libdw_read_offset_inc (dwarf, sec_idx, &bytes, offset_size,
- &type_offset, sec_idx, 0))
- return -1;
+ sig_id = read_8ubyte_unaligned_inc (dwarf, bytes);
+
+ if ((v4_debug_types
+ || unit_type == DW_UT_type || unit_type == DW_UT_split_type))
+ {
+ if (__libdw_read_offset_inc (dwarf, sec_idx, &bytes, offset_size,
+ &subdie_off, sec_idx, 0))
+ return -1;
+
+ /* Validate that the TYPE_OFFSET points past the header. */
+ if (unlikely (subdie_off < (size_t) (bytes - (data + off))))
+ goto invalid;
+ }
+ }
- /* Validate that the TYPE_OFFSET points past the header. */
- if (unlikely (type_offset < (size_t) (bytes - (data + off))))
- goto invalid;
+ done:
+ if (unit_id8p != NULL)
+ *unit_id8p = sig_id;
- *type_signaturep = type_sig8;
- if (type_offsetp != NULL)
- *type_offsetp = type_offset;
- }
+ if (subdie_offsetp != NULL)
+ *subdie_offsetp = subdie_off;
- /* Store the header length. */
+ /* Store the header length. This is really how much we have read
+ from the header. If we didn't recognize the unit type the
+ header might actually be bigger. */
if (header_sizep != NULL)
*header_sizep = bytes - (data + off);
if (versionp != NULL)
*versionp = version;
+ if (unit_typep != NULL)
+ *unit_typep = unit_type;
+
if (abbrev_offsetp != NULL)
*abbrev_offsetp = abbrev_offset;
@@ -166,13 +273,18 @@ dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off,
if (offset_sizep != NULL)
*offset_sizep = offset_size;
- /* See definition of DIE_OFFSET_FROM_CU_OFFSET macro
- for an explanation of the trick in this expression. */
+ /* The length of the unit doesn't include the length field itself.
+ The length field is either, with offset == 4: 2 * 4 - 4 == 4,
+ or with offset == 8: 2 * 8 - 4 == 12. */
*next_off = off + 2 * offset_size - 4 + length;
+ /* This means that the length field is bogus, but return the CU anyway.
+ We just won't return anything after this. */
+ if (*next_off <= off)
+ *next_off = (Dwarf_Off) -1;
+
return 0;
}
-INTDEF(dwarf_next_unit)
int
dwarf_nextcu (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off,