libdwfl: Only intern CU when not EOF marker and cuoff points to a DIE.
[platform/upstream/elfutils.git] / libdwfl / relocate.c
index e809a97..e102e1e 100644 (file)
@@ -1,51 +1,30 @@
 /* Relocate debug information.
-   Copyright (C) 2005-2009 Red Hat, Inc.
-   This file is part of Red Hat elfutils.
+   Copyright (C) 2005-2011, 2014 Red Hat, Inc.
+   This file is part of 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.
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
 
-   Red Hat elfutils is distributed in the hope that it will be useful, but
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   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.
-
-   In addition, as a special exception, Red Hat, Inc. gives You the
-   additional right to link the code of Red Hat elfutils with code licensed
-   under any Open Source Initiative certified open source license
-   (http://www.opensource.org/licenses/index.php) which requires the
-   distribution of source code with any binary distribution and to
-   distribute linked combinations of the two.  Non-GPL Code permitted under
-   this exception must only link to the code of Red Hat elfutils through
-   those well defined interfaces identified in the file named EXCEPTION
-   found in the source code files (the "Approved Interfaces").  The files
-   of Non-GPL Code may instantiate templates or use macros or inline
-   functions from the Approved Interfaces without causing the resulting
-   work to be covered by the GNU General Public License.  Only Red Hat,
-   Inc. may make changes or additions to the list of Approved Interfaces.
-   Red Hat's grant of this exception is conditioned upon your not adding
-   any new exceptions.  If you wish to add a new Approved Interface or
-   exception, please contact Red Hat.  You must obey the GNU General Public
-   License in all respects for all of the Red Hat elfutils code and other
-   code used in conjunction with Red Hat elfutils except the Non-GPL Code
-   covered by this exception.  If you modify this file, you may extend this
-   exception to your version of the file, but you are not obligated to do
-   so.  If you do not wish to provide this exception without modification,
-   you must delete this exception statement from your version and license
-   this file solely under the GPL without exception.
-
-   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>.  */
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
 
 #include "libdwflP.h"
 
@@ -59,6 +38,12 @@ internal_function
 __libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, size_t *shstrndx,
                          Elf32_Word shndx, GElf_Addr *value)
 {
+  /* No adjustment needed for section zero, it is never loaded.
+     Handle it first, just in case the ELF file has strange section
+     zero flags set.  */
+  if (shndx == 0)
+    return DWFL_E_NOERROR;
+
   Elf_Scn *refscn = elf_getscn (elf, shndx);
   GElf_Shdr refshdr_mem, *refshdr = gelf_getshdr (refscn, &refshdr_mem);
   if (refshdr == NULL)
@@ -70,7 +55,7 @@ __libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, size_t *shstrndx,
         address and update the section header.  */
 
       if (*shstrndx == SHN_UNDEF
-         && unlikely (elf_getshstrndx (elf, shstrndx) < 0))
+         && unlikely (elf_getshdrstrndx (elf, shstrndx) < 0))
        return DWFL_E_LIBELF;
 
       const char *name = elf_strptr (elf, *shstrndx, refshdr->sh_name);
@@ -97,7 +82,7 @@ __libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, size_t *shstrndx,
 
   if (refshdr->sh_flags & SHF_ALLOC)
     /* Apply the adjustment.  */
-    *value += refshdr->sh_addr + mod->main.bias;
+    *value += dwfl_adjusted_address (mod, refshdr->sh_addr);
 
   return DWFL_E_NOERROR;
 }
@@ -189,7 +174,10 @@ relocate_getsym (Dwfl_Module *mod,
     {
     case SHN_ABS:
     case SHN_UNDEF:
+      return DWFL_E_NOERROR;
+
     case SHN_COMMON:
+      sym->st_value = 0;       /* Value is size, not helpful. */
       return DWFL_E_NOERROR;
     }
 
@@ -218,7 +206,8 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
          symtab->symstrdata = elf_getdata (elf_getscn (symtab->symelf,
                                                        symtab->strtabndx),
                                            NULL);
-         if (unlikely (symtab->symstrdata == NULL))
+         if (unlikely (symtab->symstrdata == NULL
+                       || symtab->symstrdata->d_buf == NULL))
            return DWFL_E_LIBELF;
        }
       if (unlikely (sym->st_name >= symtab->symstrdata->d_size))
@@ -265,9 +254,16 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
                  continue;
 
                /* We found it!  */
-               if (shndx == SHN_ABS)
+               if (shndx == SHN_ABS) /* XXX maybe should apply bias? */
                  return DWFL_E_NOERROR;
 
+               if (m->e_type != ET_REL)
+                 {
+                   sym->st_value = dwfl_adjusted_st_value (m, m->symfile->elf,
+                                                           sym->st_value);
+                   return DWFL_E_NOERROR;
+                 }
+
                /* In an ET_REL file, the symbol table values are relative
                   to the section, not to the module's load base.  */
                size_t symshstrndx = SHN_UNDEF;
@@ -294,6 +290,10 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
   if (tname == NULL)
     return DWFL_E_LIBELF;
 
+  if (unlikely (tshdr->sh_type == SHT_NOBITS) || unlikely (tshdr->sh_size == 0))
+    /* No contents to relocate.  */
+    return DWFL_E_NOERROR;
+
   if (debugscn && ! ebl_debugscn_p (mod->ebl, tname))
     /* This relocation section is not for a debugging section.
        Nothing to do here.  */
@@ -304,12 +304,61 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
   if (tdata == NULL)
     return DWFL_E_LIBELF;
 
+  /* If either the section that needs the relocation applied, or the
+     section that the relocations come from overlap one of the ehdrs,
+     shdrs or phdrs data then we refuse to do the relocations.  It
+     isn't illegal for ELF section data to overlap the header data,
+     but updating the (relocation) data might corrupt the in-memory
+     libelf headers causing strange corruptions or errors.  */
+  size_t ehsize = gelf_fsize (relocated, ELF_T_EHDR, 1, EV_CURRENT);
+  if (unlikely (shdr->sh_offset < ehsize
+               || tshdr->sh_offset < ehsize))
+    return DWFL_E_BADELF;
+
+  GElf_Off shdrs_start = ehdr->e_shoff;
+  size_t shnums;
+  if (elf_getshdrnum (relocated, &shnums) < 0)
+    return DWFL_E_LIBELF;
+  /* Overflows will have been checked by elf_getshdrnum/get|rawdata.  */
+  size_t shentsize = gelf_fsize (relocated, ELF_T_SHDR, 1, EV_CURRENT);
+  GElf_Off shdrs_end = shdrs_start + shnums * shentsize;
+  if (unlikely ((shdrs_start < shdr->sh_offset + shdr->sh_size
+                && shdr->sh_offset < shdrs_end)
+               || (shdrs_start < tshdr->sh_offset + tshdr->sh_size
+                   && tshdr->sh_offset < shdrs_end)))
+    return DWFL_E_BADELF;
+
+  GElf_Off phdrs_start = ehdr->e_phoff;
+  size_t phnums;
+  if (elf_getphdrnum (relocated, &phnums) < 0)
+    return DWFL_E_LIBELF;
+  if (phdrs_start != 0 && phnums != 0)
+    {
+      /* Overflows will have been checked by elf_getphdrnum/get|rawdata.  */
+      size_t phentsize = gelf_fsize (relocated, ELF_T_PHDR, 1, EV_CURRENT);
+      GElf_Off phdrs_end = phdrs_start + phnums * phentsize;
+      if (unlikely ((phdrs_start < shdr->sh_offset + shdr->sh_size
+                    && shdr->sh_offset < phdrs_end)
+                   || (phdrs_start < tshdr->sh_offset + tshdr->sh_size
+                       && tshdr->sh_offset < phdrs_end)))
+       return DWFL_E_BADELF;
+    }
+
   /* Apply one relocation.  Returns true for any invalid data.  */
   Dwfl_Error relocate (GElf_Addr offset, const GElf_Sxword *addend,
                       int rtype, int symndx)
   {
     /* First see if this is a reloc we can handle.
        If we are skipping it, don't bother resolving the symbol.  */
+
+    if (unlikely (rtype == 0))
+      /* In some odd situations, the linker can leave R_*_NONE relocs
+        behind.  This is probably bogus ld -r behavior, but the only
+        cases it's known to appear in are harmless: DWARF data
+        referring to addresses in a section that has been discarded.
+        So we just pretend it's OK without further relocation.  */
+      return DWFL_E_NOERROR;
+
     Elf_Type type = ebl_reloc_simple_type (mod->ebl, rtype);
     if (unlikely (type == ELF_T_NUM))
       return DWFL_E_BADRELTYPE;
@@ -338,7 +387,8 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
          {
            /* Maybe we can figure it out anyway.  */
            error = resolve_symbol (mod, reloc_symtab, &sym, shndx);
-           if (error != DWFL_E_NOERROR)
+           if (error != DWFL_E_NOERROR
+               && !(error == DWFL_E_RELUNDEF && shndx == SHN_COMMON))
              return error;
          }
 
@@ -362,7 +412,7 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
        return DWFL_E_BADRELTYPE;
       }
 
-    if (offset + size > tdata->d_size)
+    if (offset > tdata->d_size || tdata->d_size - offset < size)
       return DWFL_E_BADRELOFF;
 
 #define DO_TYPE(NAME, Name) GElf_##Name Name;
@@ -453,7 +503,10 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
       }
   }
 
-  size_t nrels = shdr->sh_size / shdr->sh_entsize;
+  size_t sh_entsize
+    = gelf_fsize (relocated, shdr->sh_type == SHT_REL ? ELF_T_REL : ELF_T_RELA,
+                 1, EV_CURRENT);
+  size_t nrels = shdr->sh_size / sh_entsize;
   size_t complete = 0;
   if (shdr->sh_type == SHT_REL)
     for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
@@ -555,7 +608,7 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
          nrels = next;
        }
 
-      shdr->sh_size = reldata->d_size = nrels * shdr->sh_entsize;
+      shdr->sh_size = reldata->d_size = nrels * sh_entsize;
       gelf_update_shdr (scn, shdr);
     }
 
@@ -574,7 +627,7 @@ __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile, bool debug)
     return DWFL_E_LIBELF;
 
   size_t d_shstrndx;
-  if (elf_getshstrndx (debugfile, &d_shstrndx) < 0)
+  if (elf_getshdrstrndx (debugfile, &d_shstrndx) < 0)
     return DWFL_E_LIBELF;
 
   RELOC_SYMTAB_CACHE (reloc_symtab);
@@ -618,7 +671,7 @@ __libdwfl_relocate_section (Dwfl_Module *mod, Elf *relocated,
   RELOC_SYMTAB_CACHE (reloc_symtab);
 
   size_t shstrndx;
-  if (elf_getshstrndx (relocated, &shstrndx) < 0)
+  if (elf_getshdrstrndx (relocated, &shstrndx) < 0)
     return DWFL_E_LIBELF;
 
   return (__libdwfl_module_getebl (mod)