Home | History | Annotate | Download | only in libdwfl
      1 /* Recover relocatibility for addresses computed from debug information.
      2    Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc.
      3    This file is part of Red Hat elfutils.
      4 
      5    Red Hat elfutils is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by the
      7    Free Software Foundation; version 2 of the License.
      8 
      9    Red Hat elfutils is distributed in the hope that it will be useful, but
     10    WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12    General Public License for more details.
     13 
     14    You should have received a copy of the GNU General Public License along
     15    with Red Hat elfutils; if not, write to the Free Software Foundation,
     16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
     17 
     18    In addition, as a special exception, Red Hat, Inc. gives You the
     19    additional right to link the code of Red Hat elfutils with code licensed
     20    under any Open Source Initiative certified open source license
     21    (http://www.opensource.org/licenses/index.php) which requires the
     22    distribution of source code with any binary distribution and to
     23    distribute linked combinations of the two.  Non-GPL Code permitted under
     24    this exception must only link to the code of Red Hat elfutils through
     25    those well defined interfaces identified in the file named EXCEPTION
     26    found in the source code files (the "Approved Interfaces").  The files
     27    of Non-GPL Code may instantiate templates or use macros or inline
     28    functions from the Approved Interfaces without causing the resulting
     29    work to be covered by the GNU General Public License.  Only Red Hat,
     30    Inc. may make changes or additions to the list of Approved Interfaces.
     31    Red Hat's grant of this exception is conditioned upon your not adding
     32    any new exceptions.  If you wish to add a new Approved Interface or
     33    exception, please contact Red Hat.  You must obey the GNU General Public
     34    License in all respects for all of the Red Hat elfutils code and other
     35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
     36    covered by this exception.  If you modify this file, you may extend this
     37    exception to your version of the file, but you are not obligated to do
     38    so.  If you do not wish to provide this exception without modification,
     39    you must delete this exception statement from your version and license
     40    this file solely under the GPL without exception.
     41 
     42    Red Hat elfutils is an included package of the Open Invention Network.
     43    An included package of the Open Invention Network is a package for which
     44    Open Invention Network licensees cross-license their patents.  No patent
     45    license is granted, either expressly or impliedly, by designation as an
     46    included package.  Should you wish to participate in the Open Invention
     47    Network licensing program, please visit www.openinventionnetwork.com
     48    <http://www.openinventionnetwork.com>.  */
     49 
     50 #include "libdwflP.h"
     51 
     52 struct dwfl_relocation
     53 {
     54   size_t count;
     55   struct
     56   {
     57     Elf_Scn *scn;
     58     Elf_Scn *relocs;
     59     const char *name;
     60     GElf_Addr start, end;
     61   } refs[0];
     62 };
     63 
     64 
     65 struct secref
     66 {
     67   struct secref *next;
     68   Elf_Scn *scn;
     69   Elf_Scn *relocs;
     70   const char *name;
     71   GElf_Addr start, end;
     72 };
     73 
     74 static int
     75 compare_secrefs (const void *a, const void *b)
     76 {
     77   struct secref *const *p1 = a;
     78   struct secref *const *p2 = b;
     79 
     80   /* No signed difference calculation is correct here, since the
     81      terms are unsigned and could be more than INT64_MAX apart.  */
     82   if ((*p1)->start < (*p2)->start)
     83     return -1;
     84   if ((*p1)->start > (*p2)->start)
     85     return 1;
     86 
     87   return 0;
     88 }
     89 
     90 static int
     91 cache_sections (Dwfl_Module *mod)
     92 {
     93   struct secref *refs = NULL;
     94   size_t nrefs = 0;
     95 
     96   size_t shstrndx;
     97   if (unlikely (elf_getshstrndx (mod->main.elf, &shstrndx) < 0))
     98     {
     99     elf_error:
    100       __libdwfl_seterrno (DWFL_E_LIBELF);
    101       return -1;
    102     }
    103 
    104   bool check_reloc_sections = false;
    105   Elf_Scn *scn = NULL;
    106   while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
    107     {
    108       GElf_Shdr shdr_mem;
    109       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
    110       if (shdr == NULL)
    111 	goto elf_error;
    112 
    113       if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0)
    114 	{
    115 	  /* This section might not yet have been looked at.  */
    116 	  if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx,
    117 					elf_ndxscn (scn),
    118 					&shdr->sh_addr) != DWFL_E_NOERROR)
    119 	    continue;
    120 	  shdr = gelf_getshdr (scn, &shdr_mem);
    121 	  if (unlikely (shdr == NULL))
    122 	    goto elf_error;
    123 	}
    124 
    125       if (shdr->sh_flags & SHF_ALLOC)
    126 	{
    127 	  const char *name = elf_strptr (mod->main.elf, shstrndx,
    128 					 shdr->sh_name);
    129 	  if (unlikely (name == NULL))
    130 	    goto elf_error;
    131 
    132 	  struct secref *newref = alloca (sizeof *newref);
    133 	  newref->scn = scn;
    134 	  newref->relocs = NULL;
    135 	  newref->name = name;
    136 	  newref->start = shdr->sh_addr + mod->main.bias;
    137 	  newref->end = newref->start + shdr->sh_size;
    138 	  newref->next = refs;
    139 	  refs = newref;
    140 	  ++nrefs;
    141 	}
    142 
    143       if (mod->e_type == ET_REL
    144 	  && shdr->sh_size != 0
    145 	  && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
    146 	  && mod->dwfl->callbacks->section_address != NULL)
    147 	{
    148 	  if (shdr->sh_info < elf_ndxscn (scn))
    149 	    {
    150 	      /* We've already looked at the section these relocs apply to.  */
    151 	      Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
    152 	      if (likely (tscn != NULL))
    153 		for (struct secref *sec = refs; sec != NULL; sec = sec->next)
    154 		  if (sec->scn == tscn)
    155 		    {
    156 		      sec->relocs = scn;
    157 		      break;
    158 		    }
    159 	    }
    160 	  else
    161 	    /* We'll have to do a second pass.  */
    162 	    check_reloc_sections = true;
    163 	}
    164     }
    165 
    166   mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs]));
    167   if (mod->reloc_info == NULL)
    168     {
    169       __libdwfl_seterrno (DWFL_E_NOMEM);
    170       return -1;
    171     }
    172 
    173   struct secref **sortrefs = alloca (nrefs * sizeof sortrefs[0]);
    174   for (size_t i = nrefs; i-- > 0; refs = refs->next)
    175     sortrefs[i] = refs;
    176   assert (refs == NULL);
    177 
    178   qsort (sortrefs, nrefs, sizeof sortrefs[0], &compare_secrefs);
    179 
    180   mod->reloc_info->count = nrefs;
    181   for (size_t i = 0; i < nrefs; ++i)
    182     {
    183       mod->reloc_info->refs[i].name = sortrefs[i]->name;
    184       mod->reloc_info->refs[i].scn = sortrefs[i]->scn;
    185       mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs;
    186       mod->reloc_info->refs[i].start = sortrefs[i]->start;
    187       mod->reloc_info->refs[i].end = sortrefs[i]->end;
    188     }
    189 
    190   if (unlikely (check_reloc_sections))
    191     {
    192       /* There was a reloc section that preceded its target section.
    193 	 So we have to scan again now that we have cached all the
    194 	 possible target sections we care about.  */
    195 
    196       scn = NULL;
    197       while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
    198 	{
    199 	  GElf_Shdr shdr_mem;
    200 	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
    201 	  if (shdr == NULL)
    202 	    goto elf_error;
    203 
    204       	  if (shdr->sh_size != 0
    205 	      && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA))
    206 	    {
    207 	      Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
    208 	      if (likely (tscn != NULL))
    209 		for (size_t i = 0; i < nrefs; ++i)
    210 		  if (mod->reloc_info->refs[i].scn == tscn)
    211 		    {
    212 		      mod->reloc_info->refs[i].relocs = scn;
    213 		      break;
    214 		    }
    215 	    }
    216 	}
    217     }
    218 
    219   return nrefs;
    220 }
    221 
    222 
    223 int
    224 dwfl_module_relocations (Dwfl_Module *mod)
    225 {
    226   if (mod == NULL)
    227     return -1;
    228 
    229   if (mod->reloc_info != NULL)
    230     return mod->reloc_info->count;
    231 
    232   switch (mod->e_type)
    233     {
    234     case ET_REL:
    235       return cache_sections (mod);
    236 
    237     case ET_DYN:
    238       return 1;
    239 
    240     case ET_EXEC:
    241       assert (mod->main.bias == 0);
    242       assert (mod->debug.bias == 0);
    243       break;
    244     }
    245 
    246   return 0;
    247 }
    248 
    249 const char *
    250 dwfl_module_relocation_info (Dwfl_Module *mod, unsigned int idx,
    251 			     Elf32_Word *shndxp)
    252 {
    253   if (mod == NULL)
    254     return NULL;
    255 
    256   switch (mod->e_type)
    257     {
    258     case ET_REL:
    259       break;
    260 
    261     case ET_DYN:
    262       if (idx != 0)
    263 	return NULL;
    264       if (shndxp)
    265 	*shndxp = SHN_ABS;
    266       return "";
    267 
    268     default:
    269       return NULL;
    270     }
    271 
    272   if (unlikely (mod->reloc_info == NULL) && cache_sections (mod) < 0)
    273     return NULL;
    274 
    275   struct dwfl_relocation *sections = mod->reloc_info;
    276 
    277   if (idx >= sections->count)
    278     return NULL;
    279 
    280   if (shndxp)
    281     *shndxp = elf_ndxscn (sections->refs[idx].scn);
    282 
    283   return sections->refs[idx].name;
    284 }
    285 
    286 /* Check that MOD is valid and make sure its relocation has been done.  */
    287 static bool
    288 check_module (Dwfl_Module *mod)
    289 {
    290   if (INTUSE(dwfl_module_getsymtab) (mod) < 0)
    291     {
    292       Dwfl_Error error = dwfl_errno ();
    293       if (error != DWFL_E_NO_SYMTAB)
    294 	{
    295 	  __libdwfl_seterrno (error);
    296 	  return true;
    297 	}
    298     }
    299 
    300   if (mod->dw == NULL)
    301     {
    302       Dwarf_Addr bias;
    303       if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
    304 	{
    305 	  Dwfl_Error error = dwfl_errno ();
    306 	  if (error != DWFL_E_NO_DWARF)
    307 	    {
    308 	      __libdwfl_seterrno (error);
    309 	      return true;
    310 	    }
    311 	}
    312     }
    313 
    314   return false;
    315 }
    316 
    317 /* Find the index in MOD->reloc_info.refs containing *ADDR.  */
    318 static int
    319 find_section (Dwfl_Module *mod, Dwarf_Addr *addr)
    320 {
    321   if (unlikely (mod->reloc_info == NULL) && cache_sections (mod) < 0)
    322     return -1;
    323 
    324   struct dwfl_relocation *sections = mod->reloc_info;
    325 
    326   /* The sections are sorted by address, so we can use binary search.  */
    327   size_t l = 0, u = sections->count;
    328   while (l < u)
    329     {
    330       size_t idx = (l + u) / 2;
    331       if (*addr < sections->refs[idx].start)
    332 	u = idx;
    333       else if (*addr > sections->refs[idx].end)
    334 	l = idx + 1;
    335       else
    336 	{
    337 	  /* Consider the limit of a section to be inside it, unless it's
    338 	     inside the next one.  A section limit address can appear in
    339 	     line records.  */
    340 	  if (*addr == sections->refs[idx].end
    341 	      && idx < sections->count
    342 	      && *addr == sections->refs[idx + 1].start)
    343 	    ++idx;
    344 
    345 	  *addr -= sections->refs[idx].start;
    346 	  return idx;
    347 	}
    348     }
    349 
    350   __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_NO_MATCH));
    351   return -1;
    352 }
    353 
    354 int
    355 dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr)
    356 {
    357   if (unlikely (check_module (mod)))
    358     return -1;
    359 
    360   switch (mod->e_type)
    361     {
    362     case ET_REL:
    363       return find_section (mod, addr);
    364 
    365     case ET_DYN:
    366       /* All relative to first and only relocation base: module start.  */
    367       *addr -= mod->low_addr;
    368       break;
    369 
    370     default:
    371       /* Already absolute, dwfl_module_relocations returned zero.  We
    372 	 shouldn't really have been called, but it's a harmless no-op.  */
    373       break;
    374     }
    375 
    376   return 0;
    377 }
    378 INTDEF (dwfl_module_relocate_address)
    379 
    380 Elf_Scn *
    381 dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address,
    382 			     Dwarf_Addr *bias)
    383 {
    384   if (check_module (mod))
    385     return NULL;
    386 
    387   int idx = find_section (mod, address);
    388   if (idx < 0)
    389     return NULL;
    390 
    391   if (mod->reloc_info->refs[idx].relocs != NULL)
    392     {
    393       assert (mod->e_type == ET_REL);
    394 
    395       Elf_Scn *tscn = mod->reloc_info->refs[idx].scn;
    396       Elf_Scn *relocscn = mod->reloc_info->refs[idx].relocs;
    397       Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.elf,
    398 						      relocscn, tscn, true);
    399       if (likely (result == DWFL_E_NOERROR))
    400 	mod->reloc_info->refs[idx].relocs = NULL;
    401       else
    402 	{
    403 	  __libdwfl_seterrno (result);
    404 	  return NULL;
    405 	}
    406     }
    407 
    408   *bias = mod->main.bias;
    409   return mod->reloc_info->refs[idx].scn;
    410 }
    411 INTDEF (dwfl_module_address_section)
    412