diff options
Diffstat (limited to 'dwarflint/sections.cc')
-rw-r--r-- | dwarflint/sections.cc | 444 |
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) +{ +} |