summaryrefslogtreecommitdiff
path: root/dwarflint/sections.cc
diff options
context:
space:
mode:
Diffstat (limited to 'dwarflint/sections.cc')
-rw-r--r--dwarflint/sections.cc444
1 files changed, 444 insertions, 0 deletions
diff --git a/dwarflint/sections.cc b/dwarflint/sections.cc
new file mode 100644
index 00000000..f17ce531
--- /dev/null
+++ b/dwarflint/sections.cc
@@ -0,0 +1,444 @@
+/* Low-level section handling.
+ Copyright (C) 2009, 2010, 2011 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 the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <cstdlib>
+#include <cstring>
+#include "../libelf/gelf.h"
+
+#include "sections.hh"
+#include "messages.hh"
+#include "pri.hh"
+#include "misc.hh"
+
+checkdescriptor const *
+load_sections::descriptor ()
+{
+ static checkdescriptor cd
+ (checkdescriptor::create ("load_sections")
+ .hidden ());
+ return &cd;
+}
+
+checkdescriptor const *
+section_base::descriptor ()
+{
+ static checkdescriptor cd;
+ return &cd;
+}
+
+namespace
+{
+ int
+ layout_rel_file (Elf *elf)
+ {
+ GElf_Ehdr ehdr;
+ if (gelf_getehdr (elf, &ehdr) == NULL)
+ return 1;
+
+ if (ehdr.e_type != ET_REL)
+ return 0;
+
+ /* Taken from libdwfl. */
+ GElf_Addr base = 0;
+ GElf_Addr start = 0x1000, end = 0x1000, bias = 0;
+
+ bool first = true;
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (shdr == NULL))
+ return 1;
+
+ if (shdr->sh_flags & SHF_ALLOC)
+ {
+ const GElf_Xword align = shdr->sh_addralign ?: 1;
+ const GElf_Addr next = (end + align - 1) & -align;
+ if (shdr->sh_addr == 0
+ /* Once we've started doing layout we have to do it all,
+ unless we just layed out the first section at 0 when
+ it already was at 0. */
+ || (bias == 0 && end > start && end != next))
+ {
+ shdr->sh_addr = next;
+ if (end == base)
+ /* This is the first section assigned a location.
+ Use its aligned address as the module's base. */
+ start = base = shdr->sh_addr;
+ else if (unlikely (base & (align - 1)))
+ {
+ /* If BASE has less than the maximum alignment of
+ any section, we eat more than the optimal amount
+ of padding and so make the module's apparent
+ size come out larger than it would when placed
+ at zero. So reset the layout with a better base. */
+
+ start = end = base = (base + align - 1) & -align;
+ Elf_Scn *prev_scn = NULL;
+ do
+ {
+ prev_scn = elf_nextscn (elf, prev_scn);
+ GElf_Shdr prev_shdr_mem;
+ GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
+ &prev_shdr_mem);
+ if (unlikely (prev_shdr == NULL))
+ return 1;
+ if (prev_shdr->sh_flags & SHF_ALLOC)
+ {
+ const GElf_Xword prev_align
+ = prev_shdr->sh_addralign ?: 1;
+
+ prev_shdr->sh_addr
+ = (end + prev_align - 1) & -prev_align;
+ end = prev_shdr->sh_addr + prev_shdr->sh_size;
+
+ if (unlikely (! gelf_update_shdr (prev_scn,
+ prev_shdr)))
+ return 1;
+ }
+ }
+ while (prev_scn != scn);
+ continue;
+ }
+
+ end = shdr->sh_addr + shdr->sh_size;
+ if (likely (shdr->sh_addr != 0)
+ && unlikely (! gelf_update_shdr (scn, shdr)))
+ return 1;
+ }
+ else
+ {
+ /* The address is already assigned. Just track it. */
+ if (first || end < shdr->sh_addr + shdr->sh_size)
+ end = shdr->sh_addr + shdr->sh_size;
+ if (first || bias > shdr->sh_addr)
+ /* This is the lowest address in the module. */
+ bias = shdr->sh_addr;
+
+ if ((shdr->sh_addr - bias + base) & (align - 1))
+ /* This section winds up misaligned using BASE.
+ Adjust BASE upwards to make it congruent to
+ the lowest section address in the file modulo ALIGN. */
+ base = (((base + align - 1) & -align)
+ + (bias & (align - 1)));
+ }
+
+ first = false;
+ }
+ }
+ return 0;
+ }
+
+ Elf *
+ open_elf (int fd)
+ {
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
+ if (unlikely (elf == NULL))
+ {
+ wr_error ()
+ << "Error opening file: " << elf_errmsg (-1) << std::endl;
+ throw check_base::failed ();
+ }
+
+ if (layout_rel_file (elf))
+ {
+ wr_error ()
+ << "Couldn't layout ET_REL file." << std::endl;
+ throw check_base::failed ();
+ }
+
+ return elf;
+ }
+
+ struct secentry
+ {
+ Elf_Data *reldata; /* Relocation data if any found. */
+ size_t reltype; /* SHT_REL or SHT_RELA. We need this
+ temporary store to be able to resolve
+ relocation section appearing before
+ relocated section. */
+ size_t secndx; /* Index into file->sec or 0 if not yet loaded. */
+ section_id id; /* Section type. */
+
+ explicit secentry (section_id a_id = sec_invalid)
+ : reldata (NULL)
+ , reltype (0)
+ , secndx (0)
+ , id (a_id)
+ {}
+ };
+
+ struct secinfo_map
+ : public std::map <std::string, secentry>
+ {
+ secinfo_map ()
+ {
+ // 0 is invalid section
+ for (unsigned i = 1; i < count_debuginfo_sections; ++i)
+ (*this)[section_name[i]] = secentry (static_cast<section_id> (i));
+ }
+
+ secentry *get (const char *name)
+ {
+ iterator it = find (std::string (name));
+ if (it == end ())
+ return NULL;
+ else
+ return &it->second;
+ }
+ };
+
+ bool
+ elf_file_init (struct elf_file *file, int fd)
+ {
+ Elf *elf = open_elf (fd);
+ assert (elf != NULL);
+ memset (file, 0, sizeof (*file));
+
+ file->elf = elf;
+ file->ebl = ebl_openbackend (elf);
+
+ if (file->ebl == NULL
+ || gelf_getehdr (elf, &file->ehdr) == NULL)
+ return false;
+
+ file->addr_64 = file->ehdr.e_ident[EI_CLASS] == ELFCLASS64;
+
+ /* Taken from dwarf_begin_elf.c. */
+ if ((BYTE_ORDER == LITTLE_ENDIAN
+ && file->ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
+ || (BYTE_ORDER == BIG_ENDIAN
+ && file->ehdr.e_ident[EI_DATA] == ELFDATA2LSB))
+ file->other_byte_order = true;
+
+ Elf_Scn *reloc_symtab = NULL;
+ secinfo_map secinfo;
+
+ /* Now find all necessary debuginfo sections and associated
+ relocation sections. */
+
+ /* Section 0 is special, skip it. */
+ REALLOC (file, sec);
+ new (file->sec + file->size++) sec ();
+
+ if (false)
+ {
+ invalid_elf:
+ wr_error () << "Broken ELF: " << elf_errmsg (-1) << "."
+ << std::endl;
+ goto close_and_out;
+ }
+
+ /* Check that the ELF file is sound. */
+ for (Elf_Scn *scn = NULL; (scn = elf_nextscn (elf, scn)); )
+ if (elf_rawdata (scn, NULL) == NULL)
+ goto invalid_elf;
+
+ for (Elf_Scn *scn = NULL; (scn = elf_nextscn (elf, scn)); )
+ {
+ REALLOC (file, sec);
+ size_t curndx = file->size++;
+ struct sec *cursec = file->sec + curndx;
+ new (cursec) sec ();
+
+ GElf_Shdr *shdr = gelf_getshdr (scn, &cursec->shdr);
+ if (shdr == NULL)
+ goto invalid_elf;
+
+ const char *scnname = elf_strptr (elf, file->ehdr.e_shstrndx,
+ shdr->sh_name);
+ // Validate the section name
+ if (scnname == NULL)
+ goto invalid_elf;
+
+ if (!address_aligned (shdr->sh_addr, shdr->sh_addralign))
+ wr_error ()
+ << "Base address of section " << scnname << ", "
+ << pri::addr (shdr->sh_addr) << ", should have an alignment of "
+ << shdr->sh_addralign << std::endl;
+
+ secentry *entry = secinfo.get (scnname);
+ cursec->scn = scn;
+ if (entry != NULL)
+ cursec->id = entry->id;
+ cursec->name = scnname;
+
+ /* Dwarf section. */
+ if (entry != NULL)
+ {
+ if (unlikely (entry->secndx != 0))
+ wr_error ()
+ << "Multiple occurrences of section " << scnname << std::endl;
+ else
+ {
+ /* Haven't seen a section of that name yet. */
+ cursec->data = elf_getdata (scn, NULL);
+ if (cursec->data == NULL || cursec->data->d_buf == NULL)
+ /* Don't print out a warning, we'll get to that in
+ process_file. */
+ cursec->data = NULL;
+ entry->secndx = curndx;
+ }
+ }
+ /* Relocation section. */
+ else if (shdr->sh_type == SHT_RELA || shdr->sh_type == SHT_REL)
+ {
+ /* Get data of section that this REL(A) section relocates. */
+ Elf_Scn *relocated_scn = elf_getscn (elf, shdr->sh_info);
+ Elf_Scn *symtab_scn = elf_getscn (elf, shdr->sh_link);
+ if (relocated_scn == NULL || symtab_scn == NULL)
+ goto invalid_elf;
+
+ GElf_Shdr relocated_shdr_mem;
+ GElf_Shdr *relocated_shdr = gelf_getshdr (relocated_scn,
+ &relocated_shdr_mem);
+ if (relocated_shdr == NULL)
+ goto invalid_elf;
+
+ const char *relocated_scnname
+ = elf_strptr (elf, file->ehdr.e_shstrndx,
+ relocated_shdr->sh_name);
+ if (unlikely (relocated_scnname == NULL))
+ goto invalid_elf;
+
+ secentry *relocated = secinfo.get (relocated_scnname);
+ if (relocated != NULL)
+ {
+ if (relocated->reldata != NULL)
+ wr_error ()
+ << "Several relocation sections for debug section "
+ << relocated_scnname << ". Ignoring " << scnname
+ << "." << std::endl;
+ else
+ {
+ relocated->reldata = elf_getdata (scn, NULL);
+ if (unlikely (relocated->reldata == NULL
+ || relocated->reldata->d_buf == NULL))
+ {
+ wr_error ()
+ << "Data-less relocation section " << scnname
+ << "." << std::endl;
+ relocated->reldata = NULL;
+ }
+ else
+ relocated->reltype = shdr->sh_type;
+ }
+
+ if (reloc_symtab == NULL)
+ reloc_symtab = symtab_scn;
+ else if (reloc_symtab != symtab_scn)
+ wr_error ()
+ << "Relocation sections use multiple symbol tables."
+ << std::endl;
+ }
+ }
+ }
+
+ for (secinfo_map::iterator it = secinfo.begin (); it != secinfo.end (); ++it)
+ if (it->second.secndx != 0)
+ file->debugsec[it->second.id] = file->sec + it->second.secndx;
+
+ if (true)
+ {
+ Elf_Data *reloc_symdata = NULL;
+ if (reloc_symtab != NULL)
+ {
+ reloc_symdata = elf_getdata (reloc_symtab, NULL);
+ if (reloc_symdata == NULL)
+ /* Not a show stopper, we can check a lot of stuff even
+ without a symbol table. */
+ wr_error () << "Couldn't obtain symtab data." << std::endl;
+ }
+
+ /* Check relocation sections that we've got. */
+ for (secinfo_map::iterator it = secinfo.begin (); it != secinfo.end (); ++it)
+ {
+ secentry *cur = &it->second;
+ if (cur->secndx != 0 && cur->reldata != NULL)
+ {
+ struct sec *sec = file->sec + cur->secndx;
+ sec->rel.type = cur->reltype;
+ if (sec->data == NULL)
+ wr_error (section_locus (sec->id))
+ << "this data-less section has a relocation section."
+ << std::endl;
+ else if (read_rel (file, sec, cur->reldata, file->addr_64))
+ sec->rel.symdata = reloc_symdata;
+ }
+ }
+
+ if (secentry *str = secinfo.get (".debug_str"))
+ if (str->reldata != NULL)
+ wr_message (section_locus (sec_str), mc_impact_2 | mc_elf)
+ << "there's a relocation section associated with this section."
+ << std::endl;
+ }
+
+ return true;
+
+ close_and_out:
+ if (elf != NULL)
+ {
+ elf_errno (); // clear errno
+ elf_end (elf);
+ int err = elf_errno ();
+ if (err != 0)
+ wr_error ()
+ << "error while closing Elf descriptor: "
+ << elf_errmsg (err) << std::endl;
+ }
+ return false;
+ }
+}
+
+load_sections::load_sections (checkstack &stack __attribute__ ((unused)),
+ dwarflint &lint)
+{
+ if (!elf_file_init (&file, lint.fd ()))
+ throw check_base::failed ();
+}
+
+load_sections::~load_sections ()
+{
+ if (file.ebl != NULL)
+ ebl_closebackend (file.ebl);
+ free (file.sec);
+ elf_end (file.elf);
+}
+
+sec &
+section_base::get_sec_or_throw (section_id secid)
+{
+ if (sec *s = sections->file.debugsec[secid])
+ if (s->data != NULL)
+ return *s;
+
+ throw check_base::failed ();
+}
+
+section_base::section_base (checkstack &stack,
+ dwarflint &lint, section_id secid)
+ : sections (lint.check (stack, sections))
+ , sect (get_sec_or_throw (secid))
+ , file (sections->file)
+{
+}