/* Low-level checking of .debug_aranges.
- Copyright (C) 2009 Red Hat, Inc.
+ Copyright (C) 2009, 2010 Red Hat, Inc.
This file is part of Red Hat elfutils.
Red Hat elfutils is free software; you can redistribute it and/or modify
Network licensing program, please visit www.openinventionnetwork.com
<http://www.openinventionnetwork.com>. */
+// xxx drop as soon as not necessary
+#define __STDC_FORMAT_MACROS
+
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
+#include <inttypes.h>
+
#include "low.h"
#include "sections.hh"
#include "check_debug_aranges.hh"
return &cd;
}
+static struct cu *
+cu_find_cu (struct cu *cu_chain, uint64_t offset)
+{
+ for (struct cu *it = cu_chain; it != NULL; it = it->next)
+ if (it->head->offset == offset)
+ return it;
+ return NULL;
+}
+
+#define PRI_CU "CU 0x%" PRIx64
+
+namespace
+{
+ struct hole_user
+ {
+ elf_file *elf;
+ section_id id;
+ char const *what;
+
+ hole_user (elf_file *a_elf, section_id a_id,
+ char const *a_what)
+ : elf (a_elf)
+ , id (a_id)
+ , what (a_what)
+ {}
+ };
+}
+
+static bool
+hole (uint64_t start, uint64_t length, void *user)
+{
+ /* We need to check alignment vs. the covered section. Find
+ where the hole lies. */
+ ::hole_user &info = *static_cast< ::hole_user *> (user);
+ struct elf_file *elf = info.elf;
+ struct sec *sec = NULL;
+ for (size_t i = 1; i < elf->size; ++i)
+ {
+ struct sec *it = elf->sec + i;
+ GElf_Shdr *shdr = &it->shdr;
+ Elf64_Addr s_end = shdr->sh_addr + shdr->sh_size;
+ if (start >= shdr->sh_addr && start + length < s_end)
+ {
+ sec = it;
+ /* Simply assume the first section that the hole
+ intersects. */
+ break;
+ }
+ }
+
+ if (sec == NULL
+ || !necessary_alignment (start, length, sec->shdr.sh_addralign))
+ {
+ char buf[128];
+ where where = WHERE (info.id, NULL);
+ wr_message (mc_aranges | mc_impact_3, &where,
+ ": addresses %s are covered with CUs, but not with %s.\n",
+ range_fmt (buf, sizeof buf, start, start + length),
+ info.what);
+ }
+
+ if (sec == NULL)
+ wr_error (NULL, "Couldn't find the section containing the above hole.\n");
+
+ return true;
+}
+
+static void
+compare_coverage (struct elf_file *file,
+ struct coverage *coverage, struct coverage *other,
+ enum section_id id, char const *what)
+{
+ struct coverage *cov = coverage_clone (coverage);
+ coverage_remove_all (cov, other);
+
+ hole_user info (file, id, what);
+ coverage_find_ranges (cov, hole, &info);
+ coverage_free (cov);
+ free (cov);
+}
+
+inline static void
+aranges_coverage_add (struct coverage *aranges_coverage,
+ uint64_t begin, uint64_t length,
+ struct where *where)
+{
+ if (coverage_is_overlap (aranges_coverage, begin, length))
+ {
+ char buf[128];
+ /* Not a show stopper, this shouldn't derail high-level. */
+ wr_message (mc_aranges | mc_impact_2 | mc_error, where,
+ ": the range %s overlaps with another one.\n",
+ range_fmt (buf, sizeof buf, begin, begin + length));
+ }
+
+ coverage_add (aranges_coverage, begin, length);
+}
+
+/* COVERAGE is portion of address space covered by CUs (either via
+ low_pc/high_pc pairs, or via DW_AT_ranges references). If
+ non-NULL, analysis of arange coverage is done against that set. */
+static bool
+check_aranges_structural (struct elf_file *file,
+ struct sec *sec,
+ struct cu *cu_chain,
+ struct coverage *coverage)
+{
+ struct read_ctx ctx;
+ read_ctx_init (&ctx, sec->data, file->other_byte_order);
+
+ bool retval = true;
+
+ struct coverage *aranges_coverage
+ = coverage != NULL
+ ? (struct coverage *)calloc (1, sizeof (struct coverage))
+ : NULL;
+
+ while (!read_ctx_eof (&ctx))
+ {
+ struct where where = WHERE (sec_aranges, NULL);
+ where_reset_1 (&where, read_ctx_get_offset (&ctx));
+ const unsigned char *atab_begin = ctx.ptr;
+
+ /* Size. */
+ uint32_t size32;
+ uint64_t size;
+ int offset_size;
+ if (!read_ctx_read_4ubyte (&ctx, &size32))
+ {
+ wr_error (&where, ": can't read table length.\n");
+ return false;
+ }
+ if (!read_size_extra (&ctx, size32, &size, &offset_size, &where))
+ return false;
+
+ struct read_ctx sub_ctx;
+ const unsigned char *atab_end = ctx.ptr + size;
+ if (false)
+ {
+ next:
+ if (!read_ctx_skip (&ctx, size))
+ /* A "can't happen" error. */
+ goto not_enough;
+ continue;
+ }
+ if (!read_ctx_init_sub (&sub_ctx, &ctx, atab_begin, atab_end))
+ {
+ not_enough:
+ wr_error (&where, PRI_NOT_ENOUGH, "next table");
+ return false;
+ }
+
+ sub_ctx.ptr = ctx.ptr;
+
+ /* Version. */
+ uint16_t version;
+ if (!read_ctx_read_2ubyte (&sub_ctx, &version))
+ {
+ wr_error (&where, ": can't read version.\n");
+ retval = false;
+ goto next;
+ }
+ if (!supported_version (version, 1, &where, 2))
+ {
+ retval = false;
+ goto next;
+ }
+
+ /* CU offset. */
+ uint64_t cu_offset;
+ uint64_t ctx_offset = sub_ctx.ptr - ctx.begin;
+ if (!read_ctx_read_offset (&sub_ctx, offset_size == 8, &cu_offset))
+ {
+ wr_error (&where, ": can't read debug info offset.\n");
+ retval = false;
+ goto next;
+ }
+
+ struct relocation *rel;
+ if ((rel = relocation_next (&sec->rel, ctx_offset,
+ &where, skip_mismatched)))
+ relocate_one (file, &sec->rel, rel, offset_size,
+ &cu_offset, &where, sec_info, NULL);
+ else if (file->ehdr.e_type == ET_REL)
+ wr_message (mc_impact_2 | mc_aranges | mc_reloc | mc_header, &where,
+ PRI_LACK_RELOCATION, "debug info offset");
+
+ struct cu *cu = NULL;
+ if (cu_chain != NULL && (cu = cu_find_cu (cu_chain, cu_offset)) == NULL)
+ wr_error (&where, ": unresolved reference to " PRI_CU ".\n", cu_offset);
+
+ struct where where_cudie;
+ if (cu != NULL)
+ {
+ where_cudie = WHERE (sec_info, NULL);
+ where_reset_1 (&where_cudie, cu->cudie_offset);
+ where.ref = &where_cudie;
+ where_cudie.formatting = wf_cudie;
+ if (cu->has_arange)
+ wr_message (mc_impact_2 | mc_aranges | mc_header, &where,
+ ": there has already been arange section for this CU.\n");
+ else
+ cu->has_arange = true;
+ }
+
+ /* Address size. */
+ int address_size;
+ if (!read_address_size (&sub_ctx, file->addr_64, &address_size, &where))
+ {
+ retval = false;
+ goto next;
+ }
+
+ /* Segment size. */
+ uint8_t segment_size;
+ if (!read_ctx_read_ubyte (&sub_ctx, &segment_size))
+ {
+ wr_error (&where, ": can't read unit segment size.\n");
+ retval = false;
+ goto next;
+ }
+ if (segment_size != 0)
+ {
+ wr_warning (&where, ": dwarflint can't handle segment_size != 0.\n");
+ retval = false;
+ goto next;
+ }
+
+
+ /* 7.20: The first tuple following the header in each set begins
+ at an offset that is a multiple of the size of a single tuple
+ (that is, twice the size of an address). The header is
+ padded, if necessary, to the appropriate boundary. */
+ const uint8_t tuple_size = 2 * address_size;
+ uint64_t off = read_ctx_get_offset (&sub_ctx);
+ if ((off % tuple_size) != 0)
+ {
+ uint64_t noff = ((off / tuple_size) + 1) * tuple_size;
+ for (uint64_t i = off; i < noff; ++i)
+ {
+ uint8_t c;
+ if (!read_ctx_read_ubyte (&sub_ctx, &c))
+ {
+ wr_error (&where,
+ ": section ends after the header, "
+ "but before the first entry.\n");
+ retval = false;
+ goto next;
+ }
+ if (c != 0)
+ wr_message (mc_impact_2 | mc_aranges | mc_header, &where,
+ ": non-zero byte at 0x%" PRIx64
+ " in padding before the first entry.\n",
+ read_ctx_get_offset (&sub_ctx));
+ }
+ }
+ assert ((read_ctx_get_offset (&sub_ctx) % tuple_size) == 0);
+
+ while (!read_ctx_eof (&sub_ctx))
+ {
+ /* We would like to report aranges the same way that readelf
+ does. But readelf uses index of the arange in the array
+ as returned by dwarf_getaranges, which sorts the aranges
+ beforehand. We don't want to disturb the memory this
+ way, the better to catch structural errors accurately.
+ So report arange offset instead. If this becomes a
+ problem, we will achieve this by two-pass analysis. */
+ where_reset_2 (&where, read_ctx_get_offset (&sub_ctx));
+
+ /* Record address. */
+ uint64_t address;
+ ctx_offset = sub_ctx.ptr - ctx.begin;
+ bool address_relocated = false;
+ if (!read_ctx_read_var (&sub_ctx, address_size, &address))
+ {
+ wr_error (&where, ": can't read address field.\n");
+ retval = false;
+ goto next;
+ }
+
+ if ((rel = relocation_next (&sec->rel, ctx_offset,
+ &where, skip_mismatched)))
+ {
+ address_relocated = true;
+ relocate_one (file, &sec->rel, rel, address_size,
+ &address, &where, rel_address, NULL);
+ }
+ else if (file->ehdr.e_type == ET_REL && address != 0)
+ wr_message (mc_impact_2 | mc_aranges | mc_reloc, &where,
+ PRI_LACK_RELOCATION, "address field");
+
+ /* Record length. */
+ uint64_t length;
+ if (!read_ctx_read_var (&sub_ctx, address_size, &length))
+ {
+ wr_error (&where, ": can't read length field.\n");
+ retval = false;
+ goto next;
+ }
+
+ if (address == 0 && length == 0 && !address_relocated)
+ break;
+
+ if (length == 0)
+ /* DWARF 3 spec, 6.1.2 Lookup by Address: Each descriptor
+ is a pair consisting of the beginning address [...],
+ followed by the _non-zero_ length of that range. */
+ wr_error (&where, ": zero-length address range.\n");
+ /* Skip coverage analysis if we have errors. */
+ else if (retval && aranges_coverage != NULL)
+ aranges_coverage_add (aranges_coverage, address, length, &where);
+ }
+
+ if (sub_ctx.ptr != sub_ctx.end)
+ {
+ uint64_t start, end;
+ struct where wh = WHERE (where.section, NULL);
+ if (read_check_zero_padding (&sub_ctx, &start, &end))
+ wr_message_padding_0 (mc_aranges, &wh, start, end);
+ else
+ {
+ wr_message_padding_n0 (mc_aranges | mc_error, &wh,
+ start, start + size);
+ retval = false;
+ }
+ }
+
+ goto next;
+ }
+
+ if (aranges_coverage != NULL)
+ {
+ compare_coverage (file, coverage, aranges_coverage,
+ sec_aranges, "aranges");
+ coverage_free (aranges_coverage);
+ free (aranges_coverage);
+ }
+
+ return retval;
+}
+
check_debug_aranges::check_debug_aranges (checkstack &stack, dwarflint &lint)
: _m_sec_aranges (lint.check (stack, _m_sec_aranges))
, _m_info (lint.toplev_check (stack, _m_info))
#include "readctx.h"
#include "tables.h"
-#define PRI_CU "CU 0x%" PRIx64
-
-static struct cu *cu_find_cu (struct cu *cu_chain, uint64_t offset);
-
bool
address_aligned (uint64_t addr, uint64_t align)
{
}
}
-static struct cu *
-cu_find_cu (struct cu *cu_chain, uint64_t offset)
-{
- for (struct cu *it = cu_chain; it != NULL; it = it->next)
- if (it->head->offset == offset)
- return it;
- return NULL;
-}
-
bool
supported_version (unsigned version,
size_t num_supported, struct where *where, ...)
file->sec[end_symbol->st_shndx].name);
}
-static void
-compare_coverage (struct elf_file *file,
- struct coverage *coverage, struct coverage *other,
- enum section_id id, char const *what)
-{
- struct coverage *cov = coverage_clone (coverage);
- coverage_remove_all (cov, other);
-
- bool hole (uint64_t start, uint64_t length, void *user)
- {
- /* We need to check alignment vs. the covered section. Find
- where the hole lies. */
- struct elf_file *elf = user;
- struct sec *sec = NULL;
- for (size_t i = 1; i < elf->size; ++i)
- {
- struct sec *it = elf->sec + i;
- GElf_Shdr *shdr = &it->shdr;
- Elf64_Addr s_end = shdr->sh_addr + shdr->sh_size;
- if (start >= shdr->sh_addr && start + length < s_end)
- {
- sec = it;
- /* Simply assume the first section that the hole
- intersects. */
- break;
- }
- }
-
- if (sec == NULL
- || !necessary_alignment (start, length, sec->shdr.sh_addralign))
- {
- char buf[128];
- wr_message (mc_aranges | mc_impact_3, &WHERE (id, NULL),
- ": addresses %s are covered with CUs, but not with %s.\n",
- range_fmt (buf, sizeof buf, start, start + length), what);
- }
-
- if (sec == NULL)
- wr_error (NULL, "Couldn't find the section containing the above hole.\n");
-
- return true;
- }
-
- coverage_find_ranges (cov, &hole, file);
- coverage_free (cov);
- free (cov);
-}
-
-/* COVERAGE is portion of address space covered by CUs (either via
- low_pc/high_pc pairs, or via DW_AT_ranges references). If
- non-NULL, analysis of arange coverage is done against that set. */
-bool
-check_aranges_structural (struct elf_file *file,
- struct sec *sec,
- struct cu *cu_chain,
- struct coverage *coverage)
-{
- struct read_ctx ctx;
- read_ctx_init (&ctx, sec->data, file->other_byte_order);
-
- bool retval = true;
-
- struct coverage *aranges_coverage
- = coverage != NULL ? calloc (1, sizeof (struct coverage)) : NULL;
-
- while (!read_ctx_eof (&ctx))
- {
- struct where where = WHERE (sec_aranges, NULL);
- where_reset_1 (&where, read_ctx_get_offset (&ctx));
- const unsigned char *atab_begin = ctx.ptr;
-
- inline void aranges_coverage_add (uint64_t begin, uint64_t length)
- {
- if (coverage_is_overlap (aranges_coverage, begin, length))
- {
- char buf[128];
- /* Not a show stopper, this shouldn't derail high-level. */
- wr_message (mc_aranges | mc_impact_2 | mc_error, &where,
- ": the range %s overlaps with another one.\n",
- range_fmt (buf, sizeof buf, begin, begin + length));
- }
-
- coverage_add (aranges_coverage, begin, length);
- }
-
- /* Size. */
- uint32_t size32;
- uint64_t size;
- int offset_size;
- if (!read_ctx_read_4ubyte (&ctx, &size32))
- {
- wr_error (&where, ": can't read table length.\n");
- return false;
- }
- if (!read_size_extra (&ctx, size32, &size, &offset_size, &where))
- return false;
-
- struct read_ctx sub_ctx;
- const unsigned char *atab_end = ctx.ptr + size;
- if (!read_ctx_init_sub (&sub_ctx, &ctx, atab_begin, atab_end))
- {
- not_enough:
- wr_error (&where, PRI_NOT_ENOUGH, "next table");
- return false;
- }
-
- sub_ctx.ptr = ctx.ptr;
-
- /* Version. */
- uint16_t version;
- if (!read_ctx_read_2ubyte (&sub_ctx, &version))
- {
- wr_error (&where, ": can't read version.\n");
- retval = false;
- goto next;
- }
- if (!supported_version (version, 1, &where, 2))
- {
- retval = false;
- goto next;
- }
-
- /* CU offset. */
- uint64_t cu_offset;
- uint64_t ctx_offset = sub_ctx.ptr - ctx.begin;
- if (!read_ctx_read_offset (&sub_ctx, offset_size == 8, &cu_offset))
- {
- wr_error (&where, ": can't read debug info offset.\n");
- retval = false;
- goto next;
- }
-
- struct relocation *rel;
- if ((rel = relocation_next (&sec->rel, ctx_offset,
- &where, skip_mismatched)))
- relocate_one (file, &sec->rel, rel, offset_size,
- &cu_offset, &where, sec_info, NULL);
- else if (file->ehdr.e_type == ET_REL)
- wr_message (mc_impact_2 | mc_aranges | mc_reloc | mc_header, &where,
- PRI_LACK_RELOCATION, "debug info offset");
-
- struct cu *cu = NULL;
- if (cu_chain != NULL && (cu = cu_find_cu (cu_chain, cu_offset)) == NULL)
- wr_error (&where, ": unresolved reference to " PRI_CU ".\n", cu_offset);
-
- struct where where_cudie;
- if (cu != NULL)
- {
- where_cudie = WHERE (sec_info, NULL);
- where_reset_1 (&where_cudie, cu->cudie_offset);
- where.ref = &where_cudie;
- where_cudie.formatting = wf_cudie;
- if (cu->has_arange)
- wr_message (mc_impact_2 | mc_aranges | mc_header, &where,
- ": there has already been arange section for this CU.\n");
- else
- cu->has_arange = true;
- }
-
- /* Address size. */
- int address_size;
- if (!read_address_size (&sub_ctx, file->addr_64, &address_size, &where))
- {
- retval = false;
- goto next;
- }
-
- /* Segment size. */
- uint8_t segment_size;
- if (!read_ctx_read_ubyte (&sub_ctx, &segment_size))
- {
- wr_error (&where, ": can't read unit segment size.\n");
- retval = false;
- goto next;
- }
- if (segment_size != 0)
- {
- wr_warning (&where, ": dwarflint can't handle segment_size != 0.\n");
- retval = false;
- goto next;
- }
-
-
- /* 7.20: The first tuple following the header in each set begins
- at an offset that is a multiple of the size of a single tuple
- (that is, twice the size of an address). The header is
- padded, if necessary, to the appropriate boundary. */
- const uint8_t tuple_size = 2 * address_size;
- uint64_t off = read_ctx_get_offset (&sub_ctx);
- if ((off % tuple_size) != 0)
- {
- uint64_t noff = ((off / tuple_size) + 1) * tuple_size;
- for (uint64_t i = off; i < noff; ++i)
- {
- uint8_t c;
- if (!read_ctx_read_ubyte (&sub_ctx, &c))
- {
- wr_error (&where,
- ": section ends after the header, "
- "but before the first entry.\n");
- retval = false;
- goto next;
- }
- if (c != 0)
- wr_message (mc_impact_2 | mc_aranges | mc_header, &where,
- ": non-zero byte at 0x%" PRIx64
- " in padding before the first entry.\n",
- read_ctx_get_offset (&sub_ctx));
- }
- }
- assert ((read_ctx_get_offset (&sub_ctx) % tuple_size) == 0);
-
- while (!read_ctx_eof (&sub_ctx))
- {
- /* We would like to report aranges the same way that readelf
- does. But readelf uses index of the arange in the array
- as returned by dwarf_getaranges, which sorts the aranges
- beforehand. We don't want to disturb the memory this
- way, the better to catch structural errors accurately.
- So report arange offset instead. If this becomes a
- problem, we will achieve this by two-pass analysis. */
- where_reset_2 (&where, read_ctx_get_offset (&sub_ctx));
-
- /* Record address. */
- uint64_t address;
- ctx_offset = sub_ctx.ptr - ctx.begin;
- bool address_relocated = false;
- if (!read_ctx_read_var (&sub_ctx, address_size, &address))
- {
- wr_error (&where, ": can't read address field.\n");
- retval = false;
- goto next;
- }
-
- if ((rel = relocation_next (&sec->rel, ctx_offset,
- &where, skip_mismatched)))
- {
- address_relocated = true;
- relocate_one (file, &sec->rel, rel, address_size,
- &address, &where, rel_address, NULL);
- }
- else if (file->ehdr.e_type == ET_REL && address != 0)
- wr_message (mc_impact_2 | mc_aranges | mc_reloc, &where,
- PRI_LACK_RELOCATION, "address field");
-
- /* Record length. */
- uint64_t length;
- if (!read_ctx_read_var (&sub_ctx, address_size, &length))
- {
- wr_error (&where, ": can't read length field.\n");
- retval = false;
- goto next;
- }
-
- if (address == 0 && length == 0 && !address_relocated)
- break;
-
- if (length == 0)
- /* DWARF 3 spec, 6.1.2 Lookup by Address: Each descriptor
- is a pair consisting of the beginning address [...],
- followed by the _non-zero_ length of that range. */
- wr_error (&where, ": zero-length address range.\n");
- /* Skip coverage analysis if we have errors. */
- else if (retval && aranges_coverage != NULL)
- aranges_coverage_add (address, length);
- }
-
- if (sub_ctx.ptr != sub_ctx.end)
- {
- uint64_t start, end;
- if (read_check_zero_padding (&sub_ctx, &start, &end))
- wr_message_padding_0 (mc_aranges, &WHERE (where.section, NULL),
- start, end);
- else
- {
- wr_message_padding_n0 (mc_aranges | mc_error,
- &WHERE (where.section, NULL),
- start, start + size);
- retval = false;
- }
- }
-
- next:
- if (!read_ctx_skip (&ctx, size))
- /* A "can't happen" error. */
- goto not_enough;
- }
-
- if (aranges_coverage != NULL)
- {
- compare_coverage (file, coverage, aranges_coverage,
- sec_aranges, "aranges");
- coverage_free (aranges_coverage);
- free (aranges_coverage);
- }
-
- return retval;
-}
-
static GElf_Rela *
get_rel_or_rela (Elf_Data *data, int ndx,
GElf_Rela *dst, size_t type)