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