LCOV - code coverage report
Current view: top level - libdwfl - derelocate.c (source / functions) Hit Total Coverage
Test: elfutils- Lines: 142 180 78.9 %
Date: 2020-06-11 18:20:19 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        6286 : compare_secrefs (const void *a, const void *b)
      59             : {
      60        6286 :   struct secref *const *p1 = a;
      61        6286 :   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        6286 :   if ((*p1)->start < (*p2)->start)
      66             :     return -1;
      67          62 :   if ((*p1)->start > (*p2)->start)
      68             :     return 1;
      69             : 
      70          62 :   if ((*p1)->end < (*p2)->end)
      71             :     return -1;
      72          31 :   if ((*p1)->end > (*p2)->end)
      73             :     return 1;
      74             : 
      75             :   /* Same start/end, then just compare which section came first.  */
      76          19 :   return elf_ndxscn ((*p1)->scn) - elf_ndxscn ((*p2)->scn);
      77             : }
      78             : 
      79             : static int
      80      221079 : cache_sections (Dwfl_Module *mod)
      81             : {
      82      221079 :   if (likely (mod->reloc_info != NULL))
      83      220925 :     return mod->reloc_info->count;
      84             : 
      85         154 :   struct secref *refs = NULL;
      86         154 :   size_t nrefs = 0;
      87             : 
      88         154 :   size_t shstrndx;
      89         154 :   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        4914 :   while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
     100             :     {
     101        4760 :       GElf_Shdr shdr_mem;
     102        4760 :       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
     103        4760 :       if (shdr == NULL)
     104           0 :         goto elf_error;
     105             : 
     106        4760 :       if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0
     107          51 :           && mod->e_type == ET_REL)
     108             :         {
     109             :           /* This section might not yet have been looked at.  */
     110          50 :           if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx,
     111          50 :                                         elf_ndxscn (scn),
     112          50 :                                         &shdr->sh_addr) != DWFL_E_NOERROR)
     113           0 :             continue;
     114          50 :           shdr = gelf_getshdr (scn, &shdr_mem);
     115          50 :           if (unlikely (shdr == NULL))
     116           0 :             goto elf_error;
     117             :         }
     118             : 
     119        4760 :       if (shdr->sh_flags & SHF_ALLOC)
     120             :         {
     121        9027 :           const char *name = elf_strptr (mod->main.elf, shstrndx,
     122        3009 :                                          shdr->sh_name);
     123        3009 :           if (unlikely (name == NULL))
     124           0 :             goto elf_error;
     125             : 
     126        3009 :           struct secref *newref = malloc (sizeof *newref);
     127        3009 :           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        3009 :           newref->scn = scn;
     136        3009 :           newref->relocs = NULL;
     137        3009 :           newref->name = name;
     138        3009 :           newref->start = dwfl_adjusted_address (mod, shdr->sh_addr);
     139        3009 :           newref->end = newref->start + shdr->sh_size;
     140        3009 :           newref->next = refs;
     141        3009 :           refs = newref;
     142        3009 :           ++nrefs;
     143             :         }
     144             : 
     145        4760 :       if (mod->e_type == ET_REL
     146        1400 :           && shdr->sh_size != 0
     147        1082 :           && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
     148          93 :           && mod->dwfl->callbacks->section_address != NULL)
     149             :         {
     150          93 :           if (shdr->sh_info < elf_ndxscn (scn))
     151             :             {
     152             :               /* We've already looked at the section these relocs apply to.  */
     153          93 :               Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
     154          93 :               if (likely (tscn != NULL))
     155         117 :                 for (struct secref *sec = refs; sec != NULL; sec = sec->next)
     156         115 :                   if (sec->scn == tscn)
     157             :                     {
     158          91 :                       sec->relocs = scn;
     159          91 :                       break;
     160             :                     }
     161             :             }
     162             :           else
     163             :             /* We'll have to do a second pass.  */
     164             :             check_reloc_sections = true;
     165             :         }
     166             :     }
     167             : 
     168         154 :   mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs]));
     169         154 :   if (unlikely (mod->reloc_info == NULL))
     170             :     goto nomem;
     171             : 
     172         154 :   struct secref **sortrefs = malloc (nrefs * sizeof sortrefs[0]);
     173         154 :   if (unlikely (sortrefs == NULL))
     174             :     goto nomem;
     175             : 
     176        3163 :   for (size_t i = nrefs; i-- > 0; refs = refs->next)
     177        3009 :     sortrefs[i] = refs;
     178         154 :   assert (refs == NULL);
     179             : 
     180         154 :   qsort (sortrefs, nrefs, sizeof sortrefs[0], &compare_secrefs);
     181             : 
     182         154 :   mod->reloc_info->count = nrefs;
     183        3163 :   for (size_t i = 0; i < nrefs; ++i)
     184             :     {
     185        3009 :       mod->reloc_info->refs[i].name = sortrefs[i]->name;
     186        3009 :       mod->reloc_info->refs[i].scn = sortrefs[i]->scn;
     187        3009 :       mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs;
     188        3009 :       mod->reloc_info->refs[i].start = sortrefs[i]->start;
     189        3009 :       mod->reloc_info->refs[i].end = sortrefs[i]->end;
     190        3009 :       free (sortrefs[i]);
     191             :     }
     192             : 
     193         154 :   free (sortrefs);
     194             : 
     195         154 :   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           0 :           GElf_Shdr shdr_mem;
     205           0 :           GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
     206           0 :           if (shdr == NULL)
     207           0 :             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         154 : free_refs:
     225         154 :   while (refs != NULL)
     226             :     {
     227           0 :       struct secref *ref = refs;
     228           0 :       refs = ref->next;
     229           0 :       free (ref);
     230             :     }
     231             : 
     232         154 :   return nrefs;
     233             : }
     234             : 
     235             : 
     236             : int
     237     1029746 : dwfl_module_relocations (Dwfl_Module *mod)
     238             : {
     239     1029746 :   if (mod == NULL)
     240             :     return -1;
     241             : 
     242     1029746 :   switch (mod->e_type)
     243             :     {
     244       69464 :     case ET_REL:
     245       69464 :       return cache_sections (mod);
     246             : 
     247             :     case ET_DYN:
     248             :       return 1;
     249             : 
     250        1116 :     case ET_EXEC:
     251        1116 :       assert (mod->main.vaddr == mod->low_addr);
     252             :       break;
     253             :     }
     254             : 
     255        1116 :   return 0;
     256             : }
     257             : 
     258             : const char *
     259     1028639 : dwfl_module_relocation_info (Dwfl_Module *mod, unsigned int idx,
     260             :                              Elf32_Word *shndxp)
     261             : {
     262     1028639 :   if (mod == NULL)
     263             :     return NULL;
     264             : 
     265     1028639 :   switch (mod->e_type)
     266             :     {
     267             :     case ET_REL:
     268       69471 :       break;
     269             : 
     270      959168 :     case ET_DYN:
     271      959168 :       if (idx != 0)
     272             :         return NULL;
     273      959168 :       if (shndxp)
     274           0 :         *shndxp = SHN_ABS;
     275             :       return "";
     276             : 
     277             :     default:
     278             :       return NULL;
     279             :     }
     280             : 
     281       69471 :   if (cache_sections (mod) < 0)
     282             :     return NULL;
     283             : 
     284       69471 :   struct dwfl_relocation *sections = mod->reloc_info;
     285             : 
     286       69471 :   if (idx >= sections->count)
     287             :     return NULL;
     288             : 
     289       69471 :   if (shndxp)
     290           0 :     *shndxp = elf_ndxscn (sections->refs[idx].scn);
     291             : 
     292       69471 :   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     1028894 : check_module (Dwfl_Module *mod)
     298             : {
     299     1028894 :   if (mod == NULL)
     300             :     return true;
     301             : 
     302     1028894 :   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     1028894 :   if (mod->dw == NULL)
     313             :     {
     314        2563 :       Dwarf_Addr bias;
     315        2563 :       if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
     316             :         {
     317        2552 :           Dwfl_Error error = dwfl_errno ();
     318        2552 :           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       82144 : find_section (Dwfl_Module *mod, Dwarf_Addr *addr)
     332             : {
     333       82144 :   if (cache_sections (mod) < 0)
     334             :     return -1;
     335             : 
     336       82144 :   struct dwfl_relocation *sections = mod->reloc_info;
     337             : 
     338             :   /* The sections are sorted by address, so we can use binary search.  */
     339       82144 :   size_t l = 0, u = sections->count;
     340      460670 :   while (l < u)
     341             :     {
     342      378519 :       size_t idx = (l + u) / 2;
     343      378519 :       if (*addr < sections->refs[idx].start)
     344             :         u = idx;
     345       92853 :       else if (*addr > sections->refs[idx].end)
     346       10716 :         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       82137 :           if (*addr == sections->refs[idx].end
     353         306 :               && idx + 1 < sections->count
     354         305 :               && *addr == sections->refs[idx + 1].start)
     355         216 :             ++idx;
     356             : 
     357       82137 :           *addr -= sections->refs[idx].start;
     358       82137 :           return idx;
     359             :         }
     360             :     }
     361             : 
     362           7 :   __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_NO_MATCH));
     363           7 :   return -1;
     364             : }
     365             : 
     366             : size_t
     367             : internal_function
     368       12595 : __libdwfl_find_section_ndx (Dwfl_Module *mod, Dwarf_Addr *addr)
     369             : {
     370       12595 :   int idx = find_section (mod, addr);
     371       12595 :   if (unlikely (idx == -1))
     372             :     return SHN_UNDEF;
     373             : 
     374       12588 :   return elf_ndxscn (mod->reloc_info->refs[idx].scn);
     375             : }
     376             : 
     377             : int
     378     1028820 : dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr)
     379             : {
     380     1028820 :   if (unlikely (check_module (mod)))
     381             :     return -1;
     382             : 
     383     1028820 :   switch (mod->e_type)
     384             :     {
     385       69475 :     case ET_REL:
     386       69475 :       return find_section (mod, addr);
     387             : 
     388      959329 :     case ET_DYN:
     389             :       /* All relative to first and only relocation base: module start.  */
     390      959329 :       *addr -= mod->low_addr;
     391      959329 :       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