dwarflint/checks.cc dwarflint/checks.hh \
dwarflint/checks-low.cc dwarflint/checks-low.hh \
dwarflint/addr-record.cc dwarflint/addr-record.h \
+ dwarflint/reloc.cc dwarflint/reloc.h \
dwarflint/checks-high.hh \
dwarflint/check_debug_abbrev.cc \
+ dwarflint/check_debug_line.cc \
dwarflint/check_matching_ranges.cc \
dwarflint/check_range_out_of_scope.cc \
dwarflint/check_expected_trees.cc
--- /dev/null
+#include "checks-low.hh"
+
+#include <dwarf.h>
+#include "../libdw/known-dwarf.h"
+#include "dwarfstrings.h"
+
+#include <sstream>
+
+namespace
+{
+ class check_debug_line
+ : public check<check_debug_line>
+ {
+ section<sec_line> *_m_sec;
+
+ struct include_directory_t
+ {
+ std::string name;
+ bool used;
+ };
+ typedef std::vector<include_directory_t> include_directories_t;
+ include_directories_t _m_include_directories;
+
+ struct file_t
+ {
+ const char *name;
+ uint64_t dir_idx;
+ bool used;
+ };
+ typedef std::vector<file_t> files_t;
+ files_t _m_files;
+
+ public:
+ explicit check_debug_line (dwarflint &lint)
+ : _m_sec (lint.check (_m_sec))
+ {
+ addr_record line_tables;
+ WIPE (line_tables);
+ if (!check_line_structural (&_m_sec->file,
+ &_m_sec->sect,
+ &line_tables))
+ throw check_base::failed (""); //xxx
+
+ check_debug_info *info = NULL;
+ info = toplev_check (lint, info);
+ if (info != NULL)
+ for (std::vector<cu>::iterator it = info->cus.begin ();
+ it != info->cus.end (); ++it)
+ for (size_t i = 0; i < it->line_refs.size; ++i)
+ {
+ struct ref *ref = it->line_refs.refs + i;
+ if (!addr_record_has_addr (&line_tables, ref->addr))
+ {
+ std::stringstream ss;
+ ss << ": unresolved reference to .debug_line table "
+ << "0x" << std::hex << ref->addr << ".";
+ wr_error (&ref->who, "%s\n", ss.str ().c_str ());
+ }
+ }
+ addr_record_free (&line_tables);
+ }
+
+ /* Directory index. */
+ bool read_directory_index (read_ctx *ctx,
+ const char *name, uint64_t *ptr,
+ where *where, bool &retval)
+ {
+ size_t nfile = _m_files.size () + 1;
+ if (!checked_read_uleb128 (ctx, ptr,
+ where, "directory index"))
+ return false;
+ if (*name == '/' && *ptr != 0)
+ wr_message (mc_impact_2 | mc_line | mc_header, where,
+ ": file #%zd has absolute pathname,"
+ " but refers to directory != 0.\n", nfile);
+ if (*ptr > _m_include_directories.size ())
+ /* Not >=, dirs are indexed from 1. */
+ {
+ std::stringstream ss;
+ ss << ": file #" << nfile
+ << " refers to directory #" << *ptr
+ << ", which wasn't defined.\n";
+ wr_message (mc_impact_4 | mc_line | mc_header, where,
+ "%s\n", ss.str ().c_str ());
+ /* Consumer might choke on that. */
+ retval = false;
+ }
+ else if (*ptr != 0)
+ _m_include_directories[*ptr - 1].used = true;
+ return true;
+ }
+
+ bool
+ use_file (uint64_t file_idx, where *where)
+ {
+ if (file_idx == 0 || file_idx > _m_files.size ())
+ {
+ std::stringstream ss;
+ ss << ": DW_LNS_set_file: invalid file index " << file_idx << '.';
+ wr_error (where, "%s\n", ss.str ().c_str ());
+ return false;
+ }
+ else
+ _m_files[file_idx - 1].used = true;
+ return true;
+ }
+
+ bool
+ check_line_structural (struct elf_file *file,
+ struct sec *sec,
+ struct addr_record *line_tables);
+ };
+
+ reg<check_debug_line> reg_debug_line;
+}
+
+bool
+check_debug_line::check_line_structural (struct elf_file *file,
+ struct sec *sec,
+ struct addr_record *line_tables)
+{
+ struct read_ctx ctx;
+ read_ctx_init (&ctx, sec->data, file->other_byte_order);
+ bool retval = true;
+
+ while (!read_ctx_eof (&ctx))
+ {
+ struct where where = WHERE (sec->id, NULL);
+ uint64_t set_offset = read_ctx_get_offset (&ctx);
+ where_reset_1 (&where, set_offset);
+ addr_record_add (line_tables, set_offset);
+ const unsigned char *set_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 *set_end = ctx.ptr + size;
+ if (!read_ctx_init_sub (&sub_ctx, &ctx, set_begin, set_end))
+ {
+ not_enough:
+ wr_error (&where, PRI_NOT_ENOUGH, "next unit");
+ return false;
+ }
+ sub_ctx.ptr = ctx.ptr;
+ sub_ctx.begin = ctx.begin;
+
+ {
+ /* Version. */
+ uint16_t version;
+ if (!read_ctx_read_2ubyte (&sub_ctx, &version))
+ {
+ wr_error (&where, ": can't read set version.\n");
+ skip:
+ retval = false;
+ goto next;
+ }
+ if (!supported_version (version, 2, &where, 2, 3))
+ goto skip;
+
+ /* Header length. */
+ uint64_t header_length;
+ if (!read_ctx_read_offset (&sub_ctx, offset_size == 8, &header_length))
+ {
+ wr_error (&where, ": can't read attribute value.\n");
+ goto skip;
+ }
+ const unsigned char *program_start = sub_ctx.ptr + header_length;
+
+ /* Minimum instruction length. */
+ uint8_t minimum_i_length;
+ if (!read_ctx_read_ubyte (&sub_ctx, &minimum_i_length))
+ {
+ wr_error (&where, ": can't read minimum instruction length.\n");
+ goto skip;
+ }
+
+ /* Default value of is_stmt. */
+ uint8_t default_is_stmt;
+ if (!read_ctx_read_ubyte (&sub_ctx, &default_is_stmt))
+ {
+ wr_error (&where, ": can't read default_is_stmt.\n");
+ goto skip;
+ }
+ /* 7.21: The boolean values "true" and "false" used by the line
+ number information program are encoded as a single byte
+ containing the value 0 for "false," and a non-zero value for
+ "true." [But give a notice if it's not 0 or 1.] */
+ if (default_is_stmt != 0
+ && default_is_stmt != 1)
+ wr_message (mc_line | mc_impact_2 | mc_header, &where,
+ ": default_is_stmt should be 0 or 1, not %ud\n",
+ default_is_stmt);
+
+ /* Line base. */
+ int8_t line_base;
+ if (!read_ctx_read_ubyte (&sub_ctx, (uint8_t *)&line_base))
+ {
+ wr_error (&where, ": can't read line_base.\n");
+ goto skip;
+ }
+
+ /* Line range. */
+ uint8_t line_range;
+ if (!read_ctx_read_ubyte (&sub_ctx, &line_range))
+ {
+ wr_error (&where, ": can't read line_range.\n");
+ goto skip;
+ }
+
+ /* Opcode base. */
+ uint8_t opcode_base;
+ if (!read_ctx_read_ubyte (&sub_ctx, &opcode_base))
+ {
+ wr_error (&where, ": can't read opcode_base.\n");
+ goto skip;
+ }
+
+ /* Standard opcode lengths. */
+ if (opcode_base == 0)
+ {
+ wr_error (&where, ": opcode base set to 0.\n");
+ opcode_base = 1; // so that in following, our -1s don't underrun
+ }
+ uint8_t std_opc_lengths[opcode_base - 1]; /* -1, opcodes go from 1. */
+ for (unsigned i = 0; i < (unsigned)(opcode_base - 1); ++i)
+ if (!read_ctx_read_ubyte (&sub_ctx, std_opc_lengths + i))
+ {
+ wr_error (&where,
+ ": can't read length of standard opcode #%d.\n", i);
+ goto skip;
+ }
+
+ while (!read_ctx_eof (&sub_ctx))
+ {
+ const char *name = read_ctx_read_str (&sub_ctx);
+ if (name == NULL)
+ {
+ wr_error (&where,
+ ": can't read name of include directory #%zd.\n",
+ /* Numbered from 1. */
+ _m_include_directories.size () + 1);
+ goto skip;
+ }
+ if (*name == 0)
+ break;
+
+ _m_include_directories.push_back ((include_directory_t){name, false});
+ }
+
+ /* File names. */
+ while (1)
+ {
+ const char *name = read_ctx_read_str (&sub_ctx);
+ if (name == NULL)
+ {
+ wr_error (&where,
+ ": can't read name of file #%zd.\n",
+ _m_files.size () + 1); /* Numbered from 1. */
+ goto skip;
+ }
+ if (*name == 0)
+ break;
+
+ uint64_t dir_idx;
+ if (!read_directory_index (&sub_ctx, name, &dir_idx, &where, retval))
+ goto skip;
+
+ /* Time of last modification. */
+ uint64_t timestamp;
+ if (!checked_read_uleb128 (&sub_ctx, ×tamp,
+ &where, "timestamp of file entry"))
+ goto skip;
+
+ /* Size of the file. */
+ uint64_t file_size;
+ if (!checked_read_uleb128 (&sub_ctx, &file_size,
+ &where, "file size of file entry"))
+ goto skip;
+
+ _m_files.push_back ((struct file_t){name, dir_idx, false});
+ }
+
+ /* Skip the rest of the header. */
+ if (sub_ctx.ptr > program_start)
+ {
+ std::stringstream ss;
+ ss << ": header claims that it has a size of 0x"
+ << std::hex << header_length
+ << ", but in fact it has a size of 0x"
+ << sub_ctx.ptr - program_start + header_length << '.';
+
+ wr_error (&where, "%s\n", ss.str ().c_str ());
+ /* Assume that the header lies, and what follows is in
+ fact line number program. */
+ retval = false;
+ }
+ else if (sub_ctx.ptr < program_start)
+ {
+ struct where wh = WHERE (sec_line, NULL);
+ if (!check_zero_padding (&sub_ctx, cat (mc_line, mc_header), &where))
+ wr_message_padding_n0 (cat (mc_line, mc_header), &wh,
+ read_ctx_get_offset (&sub_ctx),
+ program_start - sub_ctx.begin);
+ sub_ctx.ptr = program_start;
+ }
+
+ bool terminated = false;
+ bool first_file = true;
+ bool seen_opcode = false;
+ while (!read_ctx_eof (&sub_ctx))
+ {
+ where_reset_2 (&where, read_ctx_get_offset (&sub_ctx));
+ uint8_t opcode;
+ if (!read_ctx_read_ubyte (&sub_ctx, &opcode))
+ {
+ wr_error (&where, ": can't read opcode.\n");
+ goto skip;
+ }
+
+ unsigned operands = 0;
+ uint8_t extended = 0;
+ switch (opcode)
+ {
+ /* Extended opcodes. */
+ case 0:
+ {
+ uint64_t skip_len;
+ if (!checked_read_uleb128 (&sub_ctx, &skip_len, &where,
+ "length of extended opcode"))
+ goto skip;
+ const unsigned char *next = sub_ctx.ptr + skip_len;
+ if (!read_ctx_read_ubyte (&sub_ctx, &extended))
+ {
+ wr_error (&where, ": can't read extended opcode.\n");
+ goto skip;
+ }
+
+ bool handled = true;
+ switch (extended)
+ {
+ case DW_LNE_end_sequence:
+ terminated = true;
+ break;
+
+ case DW_LNE_set_address:
+ {
+ uint64_t ctx_offset = read_ctx_get_offset (&sub_ctx);
+ uint64_t addr;
+ if (!read_ctx_read_offset (&sub_ctx,
+ file->addr_64, &addr))
+ {
+ wr_error (&where, ": can't read operand of DW_LNE_set_address.\n");
+ goto skip;
+ }
+
+ struct relocation *rel;
+ if ((rel = relocation_next (&sec->rel, ctx_offset,
+ &where, skip_mismatched)))
+ relocate_one (file, &sec->rel, rel,
+ file->addr_64 ? 8 : 4,
+ &addr, &where, rel_address, NULL);
+ else if (file->ehdr.e_type == ET_REL)
+ wr_message (mc_impact_2 | mc_line | mc_reloc, &where,
+ PRI_LACK_RELOCATION, "DW_LNE_set_address");
+ break;
+ }
+
+ case DW_LNE_define_file:
+ {
+ const char *name;
+ if ((name = read_ctx_read_str (&sub_ctx)) == NULL)
+ {
+ wr_error (&where,
+ ": can't read filename operand of DW_LNE_define_file.\n");
+ goto skip;
+ }
+ uint64_t dir_idx;
+ if (!read_directory_index (&sub_ctx, name, &dir_idx,
+ &where, retval))
+ goto skip;
+ _m_files.push_back
+ ((struct file_t){name, dir_idx, false});
+ operands = 2; /* Skip mtime & size of the file. */
+ }
+
+ /* See if we know about any other standard opcodes. */
+ default:
+ handled = false;
+ switch (extended)
+ {
+#define ONE_KNOWN_DW_LNE(NAME, CODE) case CODE: break;
+ ALL_KNOWN_DW_LNE
+#undef ONE_KNOWN_DW_LNE
+ default:
+ /* No we don't, emit a warning. */
+ wr_message (mc_impact_2 | mc_line, &where,
+ ": unknown extended opcode #%d.\n", extended);
+ };
+ };
+
+ if (sub_ctx.ptr > next)
+ {
+ std::stringstream ss;
+ ss << ": opcode claims that it has a size of 0x"
+ << std::hex << skip_len
+ << ", but in fact it has a size of 0x"
+ << (skip_len + (next - sub_ctx.ptr)) << '.';
+ wr_error (&where, "%s\n", ss.str ().c_str ());
+ retval = false;
+ }
+ else if (sub_ctx.ptr < next)
+ {
+ if (handled
+ && !check_zero_padding (&sub_ctx, mc_line, &where))
+ {
+ struct where wh = WHERE (sec_line, NULL);
+ wr_message_padding_n0 (mc_line, &wh,
+ read_ctx_get_offset (&sub_ctx),
+ next - sub_ctx.begin);
+ }
+ sub_ctx.ptr = next;
+ }
+ break;
+ }
+
+ /* Standard opcodes that need validation or have
+ non-ULEB operands. */
+ case DW_LNS_fixed_advance_pc:
+ {
+ uint16_t a;
+ if (!read_ctx_read_2ubyte (&sub_ctx, &a))
+ {
+ wr_error (&where, ": can't read operand of DW_LNS_fixed_advance_pc.\n");
+ goto skip;
+ }
+ break;
+ }
+
+ case DW_LNS_set_file:
+ {
+ uint64_t file_idx;
+ if (!checked_read_uleb128 (&sub_ctx, &file_idx, &where,
+ "DW_LNS_set_file operand"))
+ goto skip;
+ if (!use_file (file_idx, &where))
+ retval = false;
+ first_file = false;
+ }
+ break;
+
+ case DW_LNS_set_isa:
+ // XXX is it possible to validate this?
+ operands = 1;
+ break;
+
+ /* All the other opcodes. */
+ default:
+ if (opcode < opcode_base)
+ operands = std_opc_lengths[opcode - 1];
+
+ switch (opcode)
+ {
+#define ONE_KNOWN_DW_LNS(NAME, CODE) case CODE: break;
+ ALL_KNOWN_DW_LNS
+#undef ONE_KNOWN_DW_LNS
+
+ default:
+ if (opcode < opcode_base)
+ wr_message (mc_impact_2 | mc_line, &where,
+ ": unknown standard opcode #%d.\n", opcode);
+ };
+ };
+
+ for (unsigned i = 0; i < operands; ++i)
+ {
+ uint64_t operand;
+ char buf[128];
+ if (opcode != 0)
+ sprintf (buf, "operand #%d of DW_LNS_%s",
+ i, dwarf_line_standard_opcode_string (opcode));
+ else
+ sprintf (buf, "operand #%d of extended opcode %d",
+ i, extended);
+ if (!checked_read_uleb128 (&sub_ctx, &operand, &where, buf))
+ goto skip;
+ }
+
+ if (first_file)
+ {
+ if (!use_file (1, &where))
+ retval = false;
+ first_file = false;
+ }
+
+ if (opcode != 0 || extended != DW_LNE_end_sequence)
+ seen_opcode = true;
+ }
+
+ for (size_t i = 0; i < _m_include_directories.size (); ++i)
+ if (!_m_include_directories[i].used)
+ wr_message (mc_impact_3 | mc_acc_bloat | mc_line | mc_header,
+ &where, ": the include #%zd `%s' is not used.\n",
+ i + 1, _m_include_directories[i].name.c_str ());
+
+ for (size_t i = 0; i < _m_files.size (); ++i)
+ if (!_m_files[i].used)
+ wr_message (mc_impact_3 | mc_acc_bloat | mc_line | mc_header,
+ &where, ": the file #%zd `%s' is not used.\n",
+ i + 1, _m_files[i].name);
+
+ if (!seen_opcode)
+ wr_message (mc_line | mc_acc_bloat | mc_impact_3, &where,
+ ": empty line number program.\n");
+
+ struct where wh = WHERE (sec_line, NULL);
+ if (!terminated)
+ wr_error (&where,
+ ": sequence of opcodes not terminated with DW_LNE_end_sequence.\n");
+ else if (sub_ctx.ptr != sub_ctx.end
+ && !check_zero_padding (&sub_ctx, mc_line, &wh))
+ wr_message_padding_n0 (mc_line, &wh,
+ /*begin*/read_ctx_get_offset (&sub_ctx),
+ /*end*/sub_ctx.end - sub_ctx.begin);
+ }
+
+ /* XXX overlaps in defined addresses are probably OK, one
+ instruction can be derived from several statements. But
+ certain flags in table should be consistent in that case,
+ namely is_stmt, basic_block, end_sequence, prologue_end,
+ epilogue_begin, isa. */
+
+ next:
+ if (!read_ctx_skip (&ctx, size))
+ goto not_enough;
+ }
+
+ if (retval)
+ relocation_skip_rest (&sec->rel, sec->id);
+
+ return retval;
+}
reg<check_debug_pub<sec_pubnames> > reg_debug_pubnames;
reg<check_debug_pub<sec_pubtypes> > reg_debug_pubtypes;
}
-
-namespace
-{
- class check_debug_line
- : public check<check_debug_line>
- {
- section<sec_line> *_m_sec;
-
- public:
- explicit check_debug_line (dwarflint &lint)
- : _m_sec (lint.check (_m_sec))
- {
- addr_record line_tables;
- WIPE (line_tables);
- if (!check_line_structural (&_m_sec->file,
- &_m_sec->sect,
- &line_tables))
- throw check_base::failed (""); //xxx
-
- check_debug_info *info = NULL;
- info = toplev_check (lint, info);
- if (info != NULL)
- for (std::vector<cu>::iterator it = info->cus.begin ();
- it != info->cus.end (); ++it)
- for (size_t i = 0; i < it->line_refs.size; ++i)
- {
- struct ref *ref = it->line_refs.refs + i;
- if (!addr_record_has_addr (&line_tables, ref->addr))
- {
- std::stringstream ss;
- ss << ": unresolved reference to .debug_line table "
- << "0x" << std::hex << ref->addr << ".";
- wr_error (&ref->who, "%s\n", ss.str ().c_str ());
- }
- }
- addr_record_free (&line_tables);
- }
- };
-
- reg<check_debug_line> reg_debug_line;
-}
#include <unistd.h>
#include "../libdw/dwarf.h"
-#include "../libdw/known-dwarf.h"
#include "../libebl/libebl.h"
#include "dwarfstrings.h"
#include "low.h"
#define PRI_CU "CU 0x%" PRIx64
#define PRI_DIE "DIE 0x%" PRIx64
-#define PRI_NOT_ENOUGH ": not enough data for %s.\n"
-#define PRI_LACK_RELOCATION ": %s seems to lack a relocation.\n"
static struct cu *cu_find_cu (struct cu *cu_chain, uint64_t offset);
return retval;
}
-static bool
+bool
read_size_extra (struct read_ctx *ctx, uint32_t size32, uint64_t *sizep,
int *offset_sizep, struct where *wh)
{
return true;
}
-static bool
+bool
check_zero_padding (struct read_ctx *ctx,
enum message_category category,
struct where *wh)
return true;
}
-static struct where
-where_from_reloc (struct relocation_data *reloc, struct where *ref)
-{
- struct where where
- = WHERE (reloc->type == SHT_REL ? sec_rel : sec_rela, NULL);
- where_reset_1 (&where, reloc->rel[reloc->index].offset);
- where.ref = ref;
- return where;
-}
-
-enum skip_type
-{
- skip_unref = 0,
- skip_mismatched = 1,
- skip_ok,
-};
-
-static struct relocation *
-relocation_next (struct relocation_data *reloc, uint64_t offset,
- struct where *where, enum skip_type st)
-{
- if (reloc == NULL || reloc->rel == NULL)
- return NULL;
-
- while (reloc->index < reloc->size)
- {
- struct relocation *rel = reloc->rel + reloc->index;
-
- /* This relocation entry is ahead of us. */
- if (rel->offset > offset)
- return NULL;
-
- reloc->index++;
-
- if (rel->invalid)
- continue;
-
- if (rel->offset < offset)
- {
- if (st != skip_ok)
- {
- struct where reloc_where = where_from_reloc (reloc, where);
- where_reset_2 (&reloc_where, rel->offset);
- void (*w) (const struct where *, const char *, ...) = wr_error;
- (*w) (&reloc_where,
- ((const char *[])
- {": relocation targets unreferenced portion of the section.\n",
- ": relocation is mismatched.\n"})[st]);
- }
- continue;
- }
-
- return rel;
- }
-
- return NULL;
-}
-
-/* Skip all relocation up to offset, and leave cursor pointing at that
- relocation, so that next time relocation_next is called, relocation
- matching that offset is immediately yielded. */
-static void
-relocation_skip (struct relocation_data *reloc, uint64_t offset,
- struct where *where, enum skip_type st)
-{
- if (reloc != NULL && reloc->rel != NULL)
- relocation_next (reloc, offset - 1, where, st);
-}
-
-/* Skip all the remaining relocations. */
-static void
-relocation_skip_rest (struct sec *sec)
-{
- if (sec->rel.rel != NULL)
- relocation_next (&sec->rel, (uint64_t)-1, &WHERE (sec->id, NULL),
- skip_mismatched);
-}
-
-/* SYMPTR may be NULL, otherwise (**SYMPTR) has to yield valid memory
- location. When the function returns, (*SYMPTR) is either NULL, in
- which case we failed or didn't get around to obtain the symbol from
- symbol table, or non-NULL, in which case the symbol was initialized. */
-static void
-relocate_one (struct elf_file *file,
- struct relocation_data *reloc,
- struct relocation *rel,
- unsigned width, uint64_t *value, struct where *where,
- enum section_id offset_into, GElf_Sym **symptr)
-{
- if (rel->invalid)
- return;
-
- struct where reloc_where = where_from_reloc (reloc, where);
- where_reset_2 (&reloc_where, rel->offset);
- struct where reloc_ref_where = reloc_where;
- reloc_ref_where.next = where;
-
- GElf_Sym symbol_mem, *symbol;
- if (symptr != NULL)
- {
- symbol = *symptr;
- *symptr = NULL;
- }
- else
- symbol = &symbol_mem;
-
- if (offset_into == sec_invalid)
- {
- wr_message (mc_impact_3 | mc_reloc, &reloc_ref_where,
- ": relocates a datum that shouldn't be relocated.\n");
- return;
- }
-
- Elf_Type type = ebl_reloc_simple_type (file->ebl, rel->type);
-
- unsigned rel_width;
- switch (type)
- {
- case ELF_T_BYTE:
- rel_width = 1;
- break;
-
- case ELF_T_HALF:
- rel_width = 2;
- break;
-
- case ELF_T_WORD:
- case ELF_T_SWORD:
- rel_width = 4;
- break;
-
- case ELF_T_XWORD:
- case ELF_T_SXWORD:
- rel_width = 8;
- break;
-
- default:
- /* This has already been diagnosed during the isolated
- validation of relocation section. */
- return;
- };
-
- if (rel_width != width)
- wr_error (&reloc_ref_where,
- ": %d-byte relocation relocates %d-byte datum.\n",
- rel_width, width);
-
- /* Tolerate that we might have failed to obtain the symbol table. */
- if (reloc->symdata != NULL)
- {
- symbol = gelf_getsym (reloc->symdata, rel->symndx, symbol);
- if (symptr != NULL)
- *symptr = symbol;
- if (symbol == NULL)
- {
- wr_error (&reloc_where,
- ": couldn't obtain symbol #%d: %s.\n",
- rel->symndx, elf_errmsg (-1));
- return;
- }
-
- uint64_t section_index = symbol->st_shndx;
- /* XXX We should handle SHN_XINDEX here. Or, instead, maybe it
- would be possible to use dwfl, which already does XINDEX
- translation. */
-
- /* For ET_REL files, we do section layout manually. But we
- don't update symbol table doing that. So instead of looking
- at symbol value, look at section address. */
- uint64_t sym_value = symbol->st_value;
- if (file->ehdr.e_type == ET_REL
- && ELF64_ST_TYPE (symbol->st_info) == STT_SECTION)
- {
- assert (sym_value == 0);
- sym_value = file->sec[section_index].shdr.sh_addr;
- }
-
- /* It's target value, not section offset. */
- if (offset_into == rel_value
- || offset_into == rel_address
- || offset_into == rel_exec)
- {
- /* If a target value is what's expected, then complain if
- it's not either SHN_ABS, an SHF_ALLOC section, or
- SHN_UNDEF. For data forms of address_size, an SHN_UNDEF
- reloc is acceptable, otherwise reject it. */
- if (!(section_index == SHN_ABS
- || (offset_into == rel_address
- && (section_index == SHN_UNDEF
- || section_index == SHN_COMMON))))
- {
- if (offset_into != rel_address && section_index == SHN_UNDEF)
- wr_error (&reloc_where,
- ": relocation of an address is formed against SHN_UNDEF symbol"
- " (symtab index %d).\n", rel->symndx);
- else
- {
- GElf_Shdr *shdr = &file->sec[section_index].shdr;
- if ((shdr->sh_flags & SHF_ALLOC) != SHF_ALLOC)
- wr_message (mc_reloc | mc_impact_3, &reloc_where,
- ": associated section %s isn't SHF_ALLOC.\n",
- file->sec[section_index].name);
- if (offset_into == rel_exec
- && (shdr->sh_flags & SHF_EXECINSTR) != SHF_EXECINSTR)
- /* This may still be kosher, but it's suspicious. */
- wr_message (mc_reloc | mc_impact_2, &reloc_where,
- ": relocation against %s is suspicious, expected executable section.\n",
- file->sec[section_index].name);
- }
- }
- }
- else
- {
- enum section_id id;
- /* If symtab[symndx].st_shndx does not match the expected
- debug section's index, complain. */
- if (section_index >= file->size)
- wr_error (&reloc_where,
- ": invalid associated section #%" PRId64 ".\n",
- section_index);
- else if ((id = file->sec[section_index].id) != offset_into)
- {
- char *wh1 = id != sec_invalid
- ? strdup (where_fmt (&WHERE (id, NULL), NULL))
- : (char *)file->sec[section_index].name;
- char *wh2 = strdup (where_fmt (&WHERE (offset_into, NULL), NULL));
- wr_error (&reloc_where,
- ": relocation references section %s, but %s was expected.\n",
- wh1, wh2);
- free (wh2);
- if (id != sec_invalid)
- free (wh1);
- }
- }
-
- /* Only do the actual relocation if we have ET_REL files. For
- non-ET_REL files, only do the above checking. */
- if (file->ehdr.e_type == ET_REL)
- {
- *value = rel->addend + sym_value;
- if (rel_width == 4)
- *value = *value & (uint64_t)(uint32_t)-1;
- }
- }
-}
-
static enum section_id
reloc_target (uint8_t form, struct abbrev_attrib *at)
{
return rel_value;
}
-static bool
+bool
supported_version (unsigned version,
size_t num_supported, struct where *where, ...)
{
": CU lengths don't exactly match Elf_Data contents.");
else
/* Did we consume all the relocations? */
- relocation_skip_rest (sec);
+ relocation_skip_rest (&sec->rel, sec->id);
/* If we managed to read up everything, now do abbrev usage
analysis. */
}
if (retval)
- relocation_skip_rest (sec);
+ relocation_skip_rest (&sec->rel, sec->id);
return retval;
}
if (retval)
{
- relocation_skip_rest (sec);
+ relocation_skip_rest (&sec->rel, sec->id);
/* We check that all CUs have the same address size when building
the CU chain. So just take the address size of the first CU in
sizeof (*sec->rel.rel), &compare);
return true;
}
-
-bool
-check_line_structural (struct elf_file *file,
- struct sec *sec,
- struct addr_record *line_tables)
-{
- struct read_ctx ctx;
- read_ctx_init (&ctx, sec->data, file->other_byte_order);
- bool retval = true;
-
- while (!read_ctx_eof (&ctx))
- {
- struct where where = WHERE (sec->id, NULL);
- uint64_t set_offset = read_ctx_get_offset (&ctx);
- where_reset_1 (&where, set_offset);
- addr_record_add (line_tables, set_offset);
- const unsigned char *set_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 *set_end = ctx.ptr + size;
- if (!read_ctx_init_sub (&sub_ctx, &ctx, set_begin, set_end))
- {
- not_enough:
- wr_error (&where, PRI_NOT_ENOUGH, "next unit");
- return false;
- }
- sub_ctx.ptr = ctx.ptr;
- sub_ctx.begin = ctx.begin;
-
- {
- /* Version. */
- uint16_t version;
- if (!read_ctx_read_2ubyte (&sub_ctx, &version))
- {
- wr_error (&where, ": can't read set version.\n");
- skip:
- retval = false;
- goto next;
- }
- if (!supported_version (version, 2, &where, 2, 3))
- goto skip;
-
- /* Header length. */
- uint64_t header_length;
- if (!read_ctx_read_offset (&sub_ctx, offset_size == 8, &header_length))
- {
- wr_error (&where, ": can't read attribute value.\n");
- goto skip;
- }
- const unsigned char *program_start = sub_ctx.ptr + header_length;
-
- /* Minimum instruction length. */
- uint8_t minimum_i_length;
- if (!read_ctx_read_ubyte (&sub_ctx, &minimum_i_length))
- {
- wr_error (&where, ": can't read minimum instruction length.\n");
- goto skip;
- }
-
- /* Default value of is_stmt. */
- uint8_t default_is_stmt;
- if (!read_ctx_read_ubyte (&sub_ctx, &default_is_stmt))
- {
- wr_error (&where, ": can't read default_is_stmt.\n");
- goto skip;
- }
- /* 7.21: The boolean values "true" and "false" used by the line
- number information program are encoded as a single byte
- containing the value 0 for "false," and a non-zero value for
- "true." [But give a notice if it's not 0 or 1.] */
- if (default_is_stmt != 0
- && default_is_stmt != 1)
- wr_message (mc_line | mc_impact_2 | mc_header, &where,
- ": default_is_stmt should be 0 or 1, not %ud\n",
- default_is_stmt);
-
- /* Line base. */
- int8_t line_base;
- if (!read_ctx_read_ubyte (&sub_ctx, (uint8_t *)&line_base))
- {
- wr_error (&where, ": can't read line_base.\n");
- goto skip;
- }
-
- /* Line range. */
- uint8_t line_range;
- if (!read_ctx_read_ubyte (&sub_ctx, &line_range))
- {
- wr_error (&where, ": can't read line_range.\n");
- goto skip;
- }
-
- /* Opcode base. */
- uint8_t opcode_base;
- if (!read_ctx_read_ubyte (&sub_ctx, &opcode_base))
- {
- wr_error (&where, ": can't read opcode_base.\n");
- goto skip;
- }
-
- /* Standard opcode lengths. */
- if (opcode_base == 0)
- {
- wr_error (&where, ": opcode base set to 0.\n");
- opcode_base = 1; // so that in following, our -1s don't underrun
- }
- uint8_t std_opc_lengths[opcode_base - 1]; /* -1, opcodes go from 1. */
- for (unsigned i = 0; i < (unsigned)(opcode_base - 1); ++i)
- if (!read_ctx_read_ubyte (&sub_ctx, std_opc_lengths + i))
- {
- wr_error (&where,
- ": can't read length of standard opcode #%d.\n", i);
- goto skip;
- }
-
- /* Include directories. */
- struct include_directory_t
- {
- const char *name;
- bool used;
- };
- struct include_directories_t
- {
- size_t size;
- size_t alloc;
- struct include_directory_t *dirs;
- } include_directories;
- WIPE (include_directories);
-
- while (!read_ctx_eof (&sub_ctx))
- {
- const char *name = read_ctx_read_str (&sub_ctx);
- if (name == NULL)
- {
- wr_error (&where,
- ": can't read name of include directory #%zd.\n",
- include_directories.size + 1); /* Numbered from 1. */
- goto skip;
- }
- if (*name == 0)
- break;
-
- REALLOC (&include_directories, dirs);
- include_directories.dirs[include_directories.size++] =
- (struct include_directory_t){name, false};
- }
-
- /* File names. */
- struct file_t
- {
- const char *name;
- uint64_t dir_idx;
- bool used;
- };
- struct files_t
- {
- size_t size;
- size_t alloc;
- struct file_t *files;
- } files;
- WIPE (files);
-
- /* Directory index. */
- bool read_directory_index (const char *name, uint64_t *ptr)
- {
- if (!checked_read_uleb128 (&sub_ctx, ptr,
- &where, "directory index"))
- return false;
- if (*name == '/' && *ptr != 0)
- wr_message (mc_impact_2 | mc_line | mc_header, &where,
- ": file #%zd has absolute pathname, but refers to directory != 0.\n",
- files.size + 1);
- if (*ptr > include_directories.size) /* Not >=, dirs indexed from 1. */
- {
- wr_message (mc_impact_4 | mc_line | mc_header, &where,
- ": file #%zd refers to directory #%" PRId64 ", which wasn't defined.\n",
- files.size + 1, *ptr);
- /* Consumer might choke on that. */
- retval = false;
- }
- else if (*ptr != 0)
- include_directories.dirs[*ptr - 1].used = true;
- return true;
- }
-
- while (1)
- {
- const char *name = read_ctx_read_str (&sub_ctx);
- if (name == NULL)
- {
- wr_error (&where,
- ": can't read name of file #%zd.\n",
- files.size + 1); /* Numbered from 1. */
- goto skip;
- }
- if (*name == 0)
- break;
-
- uint64_t dir_idx;
- if (!read_directory_index (name, &dir_idx))
- goto skip;
-
- /* Time of last modification. */
- uint64_t timestamp;
- if (!checked_read_uleb128 (&sub_ctx, ×tamp,
- &where, "timestamp of file entry"))
- goto skip;
-
- /* Size of the file. */
- uint64_t file_size;
- if (!checked_read_uleb128 (&sub_ctx, &file_size,
- &where, "file size of file entry"))
- goto skip;
-
- REALLOC (&files, files);
- files.files[files.size++]
- = (struct file_t){name, dir_idx, false};
- }
-
- /* Skip the rest of the header. */
- if (sub_ctx.ptr > program_start)
- {
- wr_error (&where,
- ": header claims that it has a size of %#" PRIx64
- ", but in fact it has a size of %#" PRIx64 ".\n",
- header_length, sub_ctx.ptr - program_start + header_length);
- /* Assume that the header lies, and what follows is in
- fact line number program. */
- retval = false;
- }
- else if (sub_ctx.ptr < program_start)
- {
- if (!check_zero_padding (&sub_ctx, mc_line | mc_header, &where))
- wr_message_padding_n0 (mc_line | mc_header, &WHERE (sec_line, NULL),
- read_ctx_get_offset (&sub_ctx),
- program_start - sub_ctx.begin);
- sub_ctx.ptr = program_start;
- }
-
- bool terminated = false;
- bool first_file = true;
- bool seen_opcode = false;
- while (!read_ctx_eof (&sub_ctx))
- {
- where_reset_2 (&where, read_ctx_get_offset (&sub_ctx));
- uint8_t opcode;
- if (!read_ctx_read_ubyte (&sub_ctx, &opcode))
- {
- wr_error (&where, ": can't read opcode.\n");
- goto skip;
- }
-
- void use_file (uint64_t file_idx)
- {
- if (file_idx == 0 || file_idx > files.size)
- {
- wr_error (&where,
- ": DW_LNS_set_file: invalid file index %" PRId64 ".\n",
- file_idx);
- retval = false;
- }
- else
- files.files[file_idx - 1].used = true;
- }
-
- unsigned operands = 0;
- uint8_t extended = 0;
- switch (opcode)
- {
- /* Extended opcodes. */
- case 0:
- {
- uint64_t skip_len;
- if (!checked_read_uleb128 (&sub_ctx, &skip_len, &where,
- "length of extended opcode"))
- goto skip;
- const unsigned char *next = sub_ctx.ptr + skip_len;
- if (!read_ctx_read_ubyte (&sub_ctx, &extended))
- {
- wr_error (&where, ": can't read extended opcode.\n");
- goto skip;
- }
-
- bool handled = true;
- switch (extended)
- {
- case DW_LNE_end_sequence:
- terminated = true;
- break;
-
- case DW_LNE_set_address:
- {
- uint64_t ctx_offset = read_ctx_get_offset (&sub_ctx);
- uint64_t addr;
- if (!read_ctx_read_offset (&sub_ctx,
- file->addr_64, &addr))
- {
- wr_error (&where, ": can't read operand of DW_LNE_set_address.\n");
- goto skip;
- }
-
- struct relocation *rel;
- if ((rel = relocation_next (&sec->rel, ctx_offset,
- &where, skip_mismatched)))
- relocate_one (file, &sec->rel, rel,
- file->addr_64 ? 8 : 4,
- &addr, &where, rel_address, NULL);
- else if (file->ehdr.e_type == ET_REL)
- wr_message (mc_impact_2 | mc_line | mc_reloc, &where,
- PRI_LACK_RELOCATION, "DW_LNE_set_address");
- break;
- }
-
- case DW_LNE_define_file:
- {
- const char *name;
- if ((name = read_ctx_read_str (&sub_ctx)) == NULL)
- {
- wr_error (&where,
- ": can't read filename operand of DW_LNE_define_file.\n");
- goto skip;
- }
- uint64_t dir_idx;
- if (!read_directory_index (name, &dir_idx))
- goto skip;
- REALLOC (&files, files);
- files.files[files.size++] =
- (struct file_t){name, dir_idx, false};
- operands = 2; /* Skip mtime & size of the file. */
- }
-
- /* See if we know about any other standard opcodes. */
- default:
- handled = false;
- switch (extended)
- {
-#define ONE_KNOWN_DW_LNE(NAME, CODE) case CODE: break;
- ALL_KNOWN_DW_LNE
-#undef ONE_KNOWN_DW_LNE
- default:
- /* No we don't, emit a warning. */
- wr_message (mc_impact_2 | mc_line, &where,
- ": unknown extended opcode #%d.\n", extended);
- };
- };
-
- if (sub_ctx.ptr > next)
- {
- wr_error (&where,
- ": opcode claims that it has a size of %#" PRIx64
- ", but in fact it has a size of %#" PRIx64 ".\n",
- skip_len, skip_len + (next - sub_ctx.ptr));
- retval = false;
- }
- else if (sub_ctx.ptr < next)
- {
- if (handled
- && !check_zero_padding (&sub_ctx, mc_line, &where))
- wr_message_padding_n0 (mc_line, &WHERE (sec_line, NULL),
- read_ctx_get_offset (&sub_ctx),
- next - sub_ctx.begin);
- sub_ctx.ptr = next;
- }
- break;
- }
-
- /* Standard opcodes that need validation or have
- non-ULEB operands. */
- case DW_LNS_fixed_advance_pc:
- {
- uint16_t a;
- if (!read_ctx_read_2ubyte (&sub_ctx, &a))
- {
- wr_error (&where, ": can't read operand of DW_LNS_fixed_advance_pc.\n");
- goto skip;
- }
- break;
- }
-
- case DW_LNS_set_file:
- {
- uint64_t file_idx;
- if (!checked_read_uleb128 (&sub_ctx, &file_idx, &where,
- "DW_LNS_set_file operand"))
- goto skip;
- use_file (file_idx);
- first_file = false;
- }
- break;
-
- case DW_LNS_set_isa:
- // XXX is it possible to validate this?
- operands = 1;
- break;
-
- /* All the other opcodes. */
- default:
- if (opcode < opcode_base)
- operands = std_opc_lengths[opcode - 1];
-
- switch (opcode)
- {
-#define ONE_KNOWN_DW_LNS(NAME, CODE) case CODE: break;
- ALL_KNOWN_DW_LNS
-#undef ONE_KNOWN_DW_LNS
-
- default:
- if (opcode < opcode_base)
- wr_message (mc_impact_2 | mc_line, &where,
- ": unknown standard opcode #%d.\n", opcode);
- };
- };
-
- for (unsigned i = 0; i < operands; ++i)
- {
- uint64_t operand;
- char buf[128];
- if (opcode != 0)
- sprintf (buf, "operand #%d of DW_LNS_%s",
- i, dwarf_line_standard_opcode_string (opcode));
- else
- sprintf (buf, "operand #%d of extended opcode %d",
- i, extended);
- if (!checked_read_uleb128 (&sub_ctx, &operand, &where, buf))
- goto skip;
- }
-
- if (first_file)
- {
- use_file (1);
- first_file = false;
- }
-
- if (opcode != 0 || extended != DW_LNE_end_sequence)
- seen_opcode = true;
- }
-
- for (size_t i = 0; i < include_directories.size; ++i)
- if (!include_directories.dirs[i].used)
- wr_message (mc_impact_3 | mc_acc_bloat | mc_line | mc_header,
- &where, ": the include #%zd `%s' is not used.\n",
- i + 1, include_directories.dirs[i].name);
-
- for (size_t i = 0; i < files.size; ++i)
- if (!files.files[i].used)
- wr_message (mc_impact_3 | mc_acc_bloat | mc_line | mc_header,
- &where, ": the file #%zd `%s' is not used.\n",
- i + 1, files.files[i].name);
-
- if (!seen_opcode)
- wr_message (mc_line | mc_acc_bloat | mc_impact_3, &where,
- ": empty line number program.\n");
- if (!terminated)
- wr_error (&where,
- ": sequence of opcodes not terminated with DW_LNE_end_sequence.\n");
- else if (sub_ctx.ptr != sub_ctx.end
- && !check_zero_padding (&sub_ctx, mc_line,
- &WHERE (sec_line, NULL)))
- wr_message_padding_n0 (mc_line, &WHERE (sec_line, NULL),
- /*begin*/read_ctx_get_offset (&sub_ctx),
- /*end*/sub_ctx.end - sub_ctx.begin);
- }
-
- /* XXX overlaps in defined addresses are probably OK, one
- instruction can be derived from several statements. But
- certain flags in table should be consistent in that case,
- namely is_stmt, basic_block, end_sequence, prologue_end,
- epilogue_begin, isa. */
-
- next:
- if (!read_ctx_skip (&ctx, size))
- goto not_enough;
- }
-
- if (retval)
- relocation_skip_rest (sec);
-
- return retval;
-}
Network licensing program, please visit www.openinventionnetwork.com
<http://www.openinventionnetwork.com>. */
-#ifndef DWARFLINT_HL_H
-#define DWARFLINT_HL_H
+#ifndef DWARFLINT_LOW_H
+#define DWARFLINT_LOW_H
#include "../libdw/libdw.h"
#include "../libebl/libebl.h"
#include "messages.h"
#include "readctx.h"
#include "addr-record.h"
+#include "reloc.h"
#ifdef __cplusplus
extern "C"
#endif
struct hl_ctx;
- struct relocation
- {
- uint64_t offset;
- uint64_t addend;
- int symndx;
- int type;
- bool invalid; /* Whether this one relocation should be
- ignored. Necessary so that we don't report
- invalid & missing relocation twice. */
- };
-
- struct relocation_data
- {
- Elf_Data *symdata; /* Symbol table associated with this
- relocation section. */
- size_t type; /* SHT_REL or SHT_RELA. */
-
- struct relocation *rel; /* Array of relocations. May be NULL
- if there are no associated
- relocation data. */
- size_t size;
- size_t alloc;
- size_t index; /* Current index. */
- };
struct sec
{
extern bool address_aligned (uint64_t addr, uint64_t align);
extern bool necessary_alignment (uint64_t start, uint64_t length,
uint64_t align);
+ extern bool read_size_extra (struct read_ctx *ctx, uint32_t size32,
+ uint64_t *sizep, int *offset_sizep,
+ struct where *wh);
+#define PRI_NOT_ENOUGH ": not enough data for %s.\n"
+ extern bool supported_version (unsigned version,
+ size_t num_supported, struct where *where, ...);
+ extern bool check_zero_padding (struct read_ctx *ctx,
+ enum message_category category,
+ struct where *wh);
struct section_coverage
{
}
#endif
-#endif/*DWARFLINT_HL_H*/
+#endif/*DWARFLINT_LOW_H*/
--- /dev/null
+#include "reloc.h"
+#include "messages.h"
+#include "low.h"
+#include <sstream>
+#include <libebl.h>
+#include <cassert>
+
+static struct where
+where_from_reloc (struct relocation_data *reloc, struct where *ref)
+{
+ struct where where
+ = WHERE (reloc->type == SHT_REL ? sec_rel : sec_rela, NULL);
+ where_reset_1 (&where, reloc->rel[reloc->index].offset);
+ where.ref = ref;
+ return where;
+}
+
+relocation *
+relocation_next (relocation_data *reloc, uint64_t offset,
+ struct where *where, enum skip_type st)
+{
+ if (reloc == NULL || reloc->rel == NULL)
+ return NULL;
+
+ while (reloc->index < reloc->size)
+ {
+ struct relocation *rel = reloc->rel + reloc->index;
+
+ /* This relocation entry is ahead of us. */
+ if (rel->offset > offset)
+ return NULL;
+
+ reloc->index++;
+
+ if (rel->invalid)
+ continue;
+
+ if (rel->offset < offset)
+ {
+ if (st != skip_ok)
+ {
+ struct where reloc_where = where_from_reloc (reloc, where);
+ where_reset_2 (&reloc_where, rel->offset);
+ std::stringstream ss;
+ if (st == skip_unref)
+ ss << ": relocation targets "
+ "unreferenced portion of the section.";
+ else
+ ss << ": relocation is mismatched.";
+ wr_error (&reloc_where, "%s\n", ss.str ().c_str ());
+ }
+ continue;
+ }
+
+ return rel;
+ }
+
+ return NULL;
+}
+
+/* Skip all relocation up to offset, and leave cursor pointing at that
+ relocation, so that next time relocation_next is called, relocation
+ matching that offset is immediately yielded. */
+void
+relocation_skip (struct relocation_data *reloc, uint64_t offset,
+ struct where *where, enum skip_type st)
+{
+ if (reloc != NULL && reloc->rel != NULL)
+ relocation_next (reloc, offset - 1, where, st);
+}
+
+/* Skip all the remaining relocations. */
+void
+relocation_skip_rest (struct relocation_data *reloc,
+ enum section_id id)
+{
+ if (reloc->rel != NULL)
+ {
+ where wh = WHERE (id, NULL);
+ relocation_next (reloc, (uint64_t)-1, &wh,
+ skip_mismatched);
+ }
+}
+
+/* SYMPTR may be NULL, otherwise (**SYMPTR) has to yield valid memory
+ location. When the function returns, (*SYMPTR) is either NULL, in
+ which case we failed or didn't get around to obtain the symbol from
+ symbol table, or non-NULL, in which case the symbol was initialized. */
+void
+relocate_one (struct elf_file *file,
+ struct relocation_data *reloc,
+ struct relocation *rel,
+ unsigned width, uint64_t *value, struct where *where,
+ enum section_id offset_into, GElf_Sym **symptr)
+{
+ if (rel->invalid)
+ return;
+
+ struct where reloc_where = where_from_reloc (reloc, where);
+ where_reset_2 (&reloc_where, rel->offset);
+ struct where reloc_ref_where = reloc_where;
+ reloc_ref_where.next = where;
+
+ GElf_Sym symbol_mem, *symbol;
+ if (symptr != NULL)
+ {
+ symbol = *symptr;
+ *symptr = NULL;
+ }
+ else
+ symbol = &symbol_mem;
+
+ if (offset_into == sec_invalid)
+ {
+ wr_message (mc_impact_3 | mc_reloc, &reloc_ref_where,
+ ": relocates a datum that shouldn't be relocated.\n");
+ return;
+ }
+
+ Elf_Type type = ebl_reloc_simple_type (file->ebl, rel->type);
+
+ unsigned rel_width;
+ switch (type)
+ {
+ case ELF_T_BYTE:
+ rel_width = 1;
+ break;
+
+ case ELF_T_HALF:
+ rel_width = 2;
+ break;
+
+ case ELF_T_WORD:
+ case ELF_T_SWORD:
+ rel_width = 4;
+ break;
+
+ case ELF_T_XWORD:
+ case ELF_T_SXWORD:
+ rel_width = 8;
+ break;
+
+ default:
+ /* This has already been diagnosed during the isolated
+ validation of relocation section. */
+ return;
+ };
+
+ if (rel_width != width)
+ wr_error (&reloc_ref_where,
+ ": %d-byte relocation relocates %d-byte datum.\n",
+ rel_width, width);
+
+ /* Tolerate that we might have failed to obtain the symbol table. */
+ if (reloc->symdata != NULL)
+ {
+ symbol = gelf_getsym (reloc->symdata, rel->symndx, symbol);
+ if (symptr != NULL)
+ *symptr = symbol;
+ if (symbol == NULL)
+ {
+ wr_error (&reloc_where,
+ ": couldn't obtain symbol #%d: %s.\n",
+ rel->symndx, elf_errmsg (-1));
+ return;
+ }
+
+ uint64_t section_index = symbol->st_shndx;
+ /* XXX We should handle SHN_XINDEX here. Or, instead, maybe it
+ would be possible to use dwfl, which already does XINDEX
+ translation. */
+
+ /* For ET_REL files, we do section layout manually. But we
+ don't update symbol table doing that. So instead of looking
+ at symbol value, look at section address. */
+ uint64_t sym_value = symbol->st_value;
+ if (file->ehdr.e_type == ET_REL
+ && ELF64_ST_TYPE (symbol->st_info) == STT_SECTION)
+ {
+ assert (sym_value == 0);
+ sym_value = file->sec[section_index].shdr.sh_addr;
+ }
+
+ /* It's target value, not section offset. */
+ if (offset_into == rel_value
+ || offset_into == rel_address
+ || offset_into == rel_exec)
+ {
+ /* If a target value is what's expected, then complain if
+ it's not either SHN_ABS, an SHF_ALLOC section, or
+ SHN_UNDEF. For data forms of address_size, an SHN_UNDEF
+ reloc is acceptable, otherwise reject it. */
+ if (!(section_index == SHN_ABS
+ || (offset_into == rel_address
+ && (section_index == SHN_UNDEF
+ || section_index == SHN_COMMON))))
+ {
+ if (offset_into != rel_address && section_index == SHN_UNDEF)
+ wr_error (&reloc_where,
+ ": relocation of an address is formed against SHN_UNDEF symbol"
+ " (symtab index %d).\n", rel->symndx);
+ else
+ {
+ GElf_Shdr *shdr = &file->sec[section_index].shdr;
+ if ((shdr->sh_flags & SHF_ALLOC) != SHF_ALLOC)
+ wr_message (mc_reloc | mc_impact_3, &reloc_where,
+ ": associated section %s isn't SHF_ALLOC.\n",
+ file->sec[section_index].name);
+ if (offset_into == rel_exec
+ && (shdr->sh_flags & SHF_EXECINSTR) != SHF_EXECINSTR)
+ /* This may still be kosher, but it's suspicious. */
+ wr_message (mc_reloc | mc_impact_2, &reloc_where,
+ ": relocation against %s is suspicious, expected executable section.\n",
+ file->sec[section_index].name);
+ }
+ }
+ }
+ else
+ {
+ enum section_id id;
+ /* If symtab[symndx].st_shndx does not match the expected
+ debug section's index, complain. */
+ if (section_index >= file->size)
+ {
+ std::stringstream ss;
+ ss << ": invalid associated section #" << section_index << ".";
+ wr_error (&reloc_where, "%s\n", ss.str ().c_str ());
+ }
+ else if ((id = file->sec[section_index].id) != offset_into)
+ {
+ std::stringstream ss;
+ ss << ": relocation references section "
+ << (id != sec_invalid
+ ? where_fmt (WHERE (id, NULL))
+ : file->sec[section_index].name)
+ << ", but " << where_fmt (WHERE (offset_into, NULL))
+ << " was expected.";
+ wr_error (&reloc_where, "%s\n", ss.str ().c_str ());
+ }
+ }
+
+ /* Only do the actual relocation if we have ET_REL files. For
+ non-ET_REL files, only do the above checking. */
+ if (file->ehdr.e_type == ET_REL)
+ {
+ *value = rel->addend + sym_value;
+ if (rel_width == 4)
+ *value = *value & (uint64_t)(uint32_t)-1;
+ }
+ }
+}
--- /dev/null
+/* Pedantic checking of DWARF files.
+ Copyright (C) 2008,2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by the
+ Free Software Foundation; version 2 of the License.
+
+ Red Hat 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 a copy of the GNU General Public License along
+ with Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifndef DWARFLINT_RELOC_H
+#define DWARFLINT_RELOC_H
+
+#include "where.h"
+#include <libelf.h>
+#include <gelf.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#else
+# include <stdbool.h>
+#endif
+
+ struct elf_file;
+
+ struct relocation
+ {
+ uint64_t offset;
+ uint64_t addend;
+ int symndx;
+ int type;
+ bool invalid; /* Whether this one relocation should be
+ ignored. Necessary so that we don't report
+ invalid & missing relocation twice. */
+ };
+
+ struct relocation_data
+ {
+ Elf_Data *symdata; /* Symbol table associated with this
+ relocation section. */
+ size_t type; /* SHT_REL or SHT_RELA. */
+
+ struct relocation *rel; /* Array of relocations. May be NULL
+ if there are no associated
+ relocation data. */
+ size_t size;
+ size_t alloc;
+ size_t index; /* Current index. */
+ };
+
+ enum skip_type
+ {
+ skip_unref = 0,
+ skip_mismatched = 1,
+ skip_ok,
+ };
+
+ struct relocation *relocation_next (struct relocation_data *reloc,
+ uint64_t offset, struct where *where,
+ enum skip_type st);
+
+ void relocation_skip (struct relocation_data *reloc, uint64_t offset,
+ struct where *where, enum skip_type st);
+
+ void relocation_skip_rest (struct relocation_data *reloc,
+ enum section_id id);
+
+ void relocate_one (struct elf_file *file,
+ struct relocation_data *reloc,
+ struct relocation *rel,
+ unsigned width, uint64_t *value, struct where *where,
+ enum section_id offset_into, GElf_Sym **symptr);
+
+#define PRI_LACK_RELOCATION ": %s seems to lack a relocation.\n"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif//DWARFLINT_RELOC_H
#ifdef __cplusplus
}
+
+inline const char *
+where_fmt (where const &wh)
+{
+ return where_fmt (&wh);
+}
#endif
#endif//DWARFLINT_WHERE_H