LCOV - code coverage report
Current view: top level - libdwfl - derelocate.c (source / functions) Hit Total Coverage
Test: elfutils-0.175 Lines: 138 171 80.7 %
Date: 2018-11-16 13:02:39 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #ifdef HAVE_CONFIG_H
      30             : # include <config.h>
      31             : #endif
      32             : 
      33             : #include "libdwflP.h"
      34             : 
      35             : struct dwfl_relocation
      36             : {
      37             :   size_t count;
      38             :   struct
      39             :   {
      40             :     Elf_Scn *scn;
      41             :     Elf_Scn *relocs;
      42             :     const char *name;
      43             :     GElf_Addr start, end;
      44             :   } refs[0];
      45             : };
      46             : 
      47             : 
      48             : struct secref
      49             : {
      50             :   struct secref *next;
      51             :   Elf_Scn *scn;
      52             :   Elf_Scn *relocs;
      53             :   const char *name;
      54             :   GElf_Addr start, end;
      55             : };
      56             : 
      57             : static int
      58        5540 : compare_secrefs (const void *a, const void *b)
      59             : {
      60        5540 :   struct secref *const *p1 = a;
      61        5540 :   struct secref *const *p2 = b;
      62             : 
      63             :   /* No signed difference calculation is correct here, since the
      64             :      terms are unsigned and could be more than INT64_MAX apart.  */
      65        5540 :   if ((*p1)->start < (*p2)->start)
      66             :     return -1;
      67          57 :   if ((*p1)->start > (*p2)->start)
      68             :     return 1;
      69             : 
      70          57 :   if ((*p1)->end < (*p2)->end)
      71             :     return -1;
      72          28 :   if ((*p1)->end > (*p2)->end)
      73             :     return 1;
      74             : 
      75             :   /* Same start/end, then just compare which section came first.  */
      76          16 :   return elf_ndxscn ((*p1)->scn) - elf_ndxscn ((*p2)->scn);
      77             : }
      78             : 
      79             : static int
      80      177625 : cache_sections (Dwfl_Module *mod)
      81             : {
      82      177625 :   if (likely (mod->reloc_info != NULL))
      83      177487 :     return mod->reloc_info->count;
      84             : 
      85         138 :   struct secref *refs = NULL;
      86         138 :   size_t nrefs = 0;
      87             : 
      88             :   size_t shstrndx;
      89         138 :   if (unlikely (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0))
      90             :     {
      91           0 :     elf_error:
      92           0 :       __libdwfl_seterrno (DWFL_E_LIBELF);
      93           0 :       nrefs = -1;
      94           0 :       goto free_refs;
      95             :     }
      96             : 
      97             :   bool check_reloc_sections = false;
      98             :   Elf_Scn *scn = NULL;
      99        4379 :   while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
     100             :     {
     101             :       GElf_Shdr shdr_mem;
     102        4241 :       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
     103        4241 :       if (shdr == NULL)
     104             :         goto elf_error;
     105             : 
     106        4241 :       if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0
     107          47 :           && mod->e_type == ET_REL)
     108             :         {
     109             :           /* This section might not yet have been looked at.  */
     110          46 :           if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx,
     111          46 :                                         elf_ndxscn (scn),
     112          46 :                                         &shdr->sh_addr) != DWFL_E_NOERROR)
     113           0 :             continue;
     114          46 :           shdr = gelf_getshdr (scn, &shdr_mem);
     115          46 :           if (unlikely (shdr == NULL))
     116             :             goto elf_error;
     117             :         }
     118             : 
     119        4241 :       if (shdr->sh_flags & SHF_ALLOC)
     120             :         {
     121        2658 :           const char *name = elf_strptr (mod->main.elf, shstrndx,
     122        2658 :                                          shdr->sh_name);
     123        2658 :           if (unlikely (name == NULL))
     124             :             goto elf_error;
     125             : 
     126        2658 :           struct secref *newref = malloc (sizeof *newref);
     127        2658 :           if (unlikely (newref == NULL))
     128             :             {
     129           0 :             nomem:
     130           0 :               __libdwfl_seterrno (DWFL_E_NOMEM);
     131           0 :               nrefs = -1;
     132           0 :               goto free_refs;
     133             :             }
     134             : 
     135        2658 :           newref->scn = scn;
     136        2658 :           newref->relocs = NULL;
     137        2658 :           newref->name = name;
     138        5316 :           newref->start = dwfl_adjusted_address (mod, shdr->sh_addr);
     139        2658 :           newref->end = newref->start + shdr->sh_size;
     140        2658 :           newref->next = refs;
     141        2658 :           refs = newref;
     142        2658 :           ++nrefs;
     143             :         }
     144             : 
     145        4241 :       if (mod->e_type == ET_REL
     146        1299 :           && shdr->sh_size != 0
     147        1011 :           && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
     148          89 :           && mod->dwfl->callbacks->section_address != NULL)
     149             :         {
     150          89 :           if (shdr->sh_info < elf_ndxscn (scn))
     151             :             {
     152             :               /* We've already looked at the section these relocs apply to.  */
     153          89 :               Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
     154          89 :               if (likely (tscn != NULL))
     155          24 :                 for (struct secref *sec = refs; sec != NULL; sec = sec->next)
     156         111 :                   if (sec->scn == tscn)
     157             :                     {
     158          87 :                       sec->relocs = scn;
     159          87 :                       break;
     160             :                     }
     161             :             }
     162             :           else
     163             :             /* We'll have to do a second pass.  */
     164             :             check_reloc_sections = true;
     165             :         }
     166             :     }
     167             : 
     168         138 :   mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs]));
     169         138 :   if (unlikely (mod->reloc_info == NULL))
     170             :     goto nomem;
     171             : 
     172         138 :   struct secref **sortrefs = malloc (nrefs * sizeof sortrefs[0]);
     173         138 :   if (unlikely (sortrefs == NULL))
     174             :     goto nomem;
     175             : 
     176        2658 :   for (size_t i = nrefs; i-- > 0; refs = refs->next)
     177        2658 :     sortrefs[i] = refs;
     178         138 :   assert (refs == NULL);
     179             : 
     180         138 :   qsort (sortrefs, nrefs, sizeof sortrefs[0], &compare_secrefs);
     181             : 
     182         138 :   mod->reloc_info->count = nrefs;
     183        2796 :   for (size_t i = 0; i < nrefs; ++i)
     184             :     {
     185        2658 :       mod->reloc_info->refs[i].name = sortrefs[i]->name;
     186        2658 :       mod->reloc_info->refs[i].scn = sortrefs[i]->scn;
     187        2658 :       mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs;
     188        2658 :       mod->reloc_info->refs[i].start = sortrefs[i]->start;
     189        2658 :       mod->reloc_info->refs[i].end = sortrefs[i]->end;
     190        2658 :       free (sortrefs[i]);
     191             :     }
     192             : 
     193         138 :   free (sortrefs);
     194             : 
     195         138 :   if (unlikely (check_reloc_sections))
     196             :     {
     197             :       /* There was a reloc section that preceded its target section.
     198             :          So we have to scan again now that we have cached all the
     199             :          possible target sections we care about.  */
     200             : 
     201             :       scn = NULL;
     202           0 :       while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
     203             :         {
     204             :           GElf_Shdr shdr_mem;
     205           0 :           GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
     206           0 :           if (shdr == NULL)
     207             :             goto elf_error;
     208             : 
     209           0 :           if (shdr->sh_size != 0
     210           0 :               && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA))
     211             :             {
     212           0 :               Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
     213           0 :               if (likely (tscn != NULL))
     214           0 :                 for (size_t i = 0; i < nrefs; ++i)
     215           0 :                   if (mod->reloc_info->refs[i].scn == tscn)
     216             :                     {
     217           0 :                       mod->reloc_info->refs[i].relocs = scn;
     218           0 :                       break;
     219             :                     }
     220             :             }
     221             :         }
     222             :     }
     223             : 
     224         276 : free_refs:
     225         138 :   while (refs != NULL)
     226             :     {
     227           0 :       struct secref *ref = refs;
     228           0 :       refs = ref->next;
     229           0 :       free (ref);
     230             :     }
     231             : 
     232         138 :   return nrefs;
     233             : }
     234             : 
     235             : 
     236             : int
     237      607191 : dwfl_module_relocations (Dwfl_Module *mod)
     238             : {
     239      607191 :   if (mod == NULL)
     240             :     return -1;
     241             : 
     242      607191 :   switch (mod->e_type)
     243             :     {
     244       53731 :     case ET_REL:
     245       53731 :       return cache_sections (mod);
     246             : 
     247             :     case ET_DYN:
     248             :       return 1;
     249             : 
     250        1005 :     case ET_EXEC:
     251        1005 :       assert (mod->main.vaddr == mod->low_addr);
     252             :       break;
     253             :     }
     254             : 
     255        1005 :   return 0;
     256             : }
     257             : 
     258             : const char *
     259      606195 : dwfl_module_relocation_info (Dwfl_Module *mod, unsigned int idx,
     260             :                              Elf32_Word *shndxp)
     261             : {
     262      606195 :   if (mod == NULL)
     263             :     return NULL;
     264             : 
     265      606195 :   switch (mod->e_type)
     266             :     {
     267             :     case ET_REL:
     268             :       break;
     269             : 
     270      552457 :     case ET_DYN:
     271      552457 :       if (idx != 0)
     272             :         return NULL;
     273      552457 :       if (shndxp)
     274           0 :         *shndxp = SHN_ABS;
     275             :       return "";
     276             : 
     277             :     default:
     278             :       return NULL;
     279             :     }
     280             : 
     281       53738 :   if (cache_sections (mod) < 0)
     282             :     return NULL;
     283             : 
     284       53738 :   struct dwfl_relocation *sections = mod->reloc_info;
     285             : 
     286       53738 :   if (idx >= sections->count)
     287             :     return NULL;
     288             : 
     289       53738 :   if (shndxp)
     290           0 :     *shndxp = elf_ndxscn (sections->refs[idx].scn);
     291             : 
     292       53738 :   return sections->refs[idx].name;
     293             : }
     294             : 
     295             : /* Check that MOD is valid and make sure its relocation has been done.  */
     296             : static bool
     297      606450 : check_module (Dwfl_Module *mod)
     298             : {
     299      606450 :   if (mod == NULL)
     300             :     return true;
     301             : 
     302      606450 :   if (INTUSE(dwfl_module_getsymtab) (mod) < 0)
     303             :     {
     304           0 :       Dwfl_Error error = dwfl_errno ();
     305           0 :       if (error != DWFL_E_NO_SYMTAB)
     306             :         {
     307           0 :           __libdwfl_seterrno (error);
     308           0 :           return true;
     309             :         }
     310             :     }
     311             : 
     312      606450 :   if (mod->dw == NULL)
     313             :     {
     314             :       Dwarf_Addr bias;
     315        1011 :       if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
     316             :         {
     317        1000 :           Dwfl_Error error = dwfl_errno ();
     318        1000 :           if (error != DWFL_E_NO_DWARF)
     319             :             {
     320           0 :               __libdwfl_seterrno (error);
     321           0 :               return true;
     322             :             }
     323             :         }
     324             :     }
     325             : 
     326             :   return false;
     327             : }
     328             : 
     329             : /* Find the index in MOD->reloc_info.refs containing *ADDR.  */
     330             : static int
     331       70156 : find_section (Dwfl_Module *mod, Dwarf_Addr *addr)
     332             : {
     333       70156 :   if (cache_sections (mod) < 0)
     334             :     return -1;
     335             : 
     336       70156 :   struct dwfl_relocation *sections = mod->reloc_info;
     337             : 
     338             :   /* The sections are sorted by address, so we can use binary search.  */
     339       70156 :   size_t l = 0, u = sections->count;
     340      384580 :   while (l < u)
     341             :     {
     342      314415 :       size_t idx = (l + u) / 2;
     343      314415 :       if (*addr < sections->refs[idx].start)
     344             :         u = idx;
     345       84450 :       else if (*addr > sections->refs[idx].end)
     346       14303 :         l = idx + 1;
     347             :       else
     348             :         {
     349             :           /* Consider the limit of a section to be inside it, unless it's
     350             :              inside the next one.  A section limit address can appear in
     351             :              line records.  */
     352       70147 :           if (*addr == sections->refs[idx].end
     353         230 :               && idx + 1 < sections->count
     354         229 :               && *addr == sections->refs[idx + 1].start)
     355         141 :             ++idx;
     356             : 
     357       70147 :           *addr -= sections->refs[idx].start;
     358       70147 :           return idx;
     359             :         }
     360             :     }
     361             : 
     362           9 :   __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_NO_MATCH));
     363           9 :   return -1;
     364             : }
     365             : 
     366             : size_t
     367             : internal_function
     368       16340 : __libdwfl_find_section_ndx (Dwfl_Module *mod, Dwarf_Addr *addr)
     369             : {
     370       16340 :   int idx = find_section (mod, addr);
     371       16340 :   if (unlikely (idx == -1))
     372             :     return SHN_UNDEF;
     373             : 
     374       16331 :   return elf_ndxscn (mod->reloc_info->refs[idx].scn);
     375             : }
     376             : 
     377             : int
     378      606376 : dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr)
     379             : {
     380      606376 :   if (unlikely (check_module (mod)))
     381             :     return -1;
     382             : 
     383      606376 :   switch (mod->e_type)
     384             :     {
     385       53742 :     case ET_REL:
     386       53742 :       return find_section (mod, addr);
     387             : 
     388      552618 :     case ET_DYN:
     389             :       /* All relative to first and only relocation base: module start.  */
     390      552618 :       *addr -= mod->low_addr;
     391      552618 :       break;
     392             : 
     393             :     default:
     394             :       /* Already absolute, dwfl_module_relocations returned zero.  We
     395             :          shouldn't really have been called, but it's a harmless no-op.  */
     396             :       break;
     397             :     }
     398             : 
     399             :   return 0;
     400             : }
     401             : INTDEF (dwfl_module_relocate_address)
     402             : 
     403             : Elf_Scn *
     404          74 : dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address,
     405             :                              Dwarf_Addr *bias)
     406             : {
     407          74 :   if (check_module (mod))
     408             :     return NULL;
     409             : 
     410          74 :   int idx = find_section (mod, address);
     411          74 :   if (idx < 0)
     412             :     return NULL;
     413             : 
     414          74 :   if (mod->reloc_info->refs[idx].relocs != NULL)
     415             :     {
     416           2 :       assert (mod->e_type == ET_REL);
     417             : 
     418           2 :       Elf_Scn *tscn = mod->reloc_info->refs[idx].scn;
     419           2 :       Elf_Scn *relocscn = mod->reloc_info->refs[idx].relocs;
     420           2 :       Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.elf,
     421             :                                                       relocscn, tscn, true);
     422           2 :       if (likely (result == DWFL_E_NOERROR))
     423           2 :         mod->reloc_info->refs[idx].relocs = NULL;
     424             :       else
     425             :         {
     426           0 :           __libdwfl_seterrno (result);
     427           0 :           return NULL;
     428             :         }
     429             :     }
     430             : 
     431          74 :   *bias = dwfl_adjusted_address (mod, 0);
     432          74 :   return mod->reloc_info->refs[idx].scn;
     433             : }
     434             : INTDEF (dwfl_module_address_section)

Generated by: LCOV version 1.13