LCOV - code coverage report
Current view: top level - libdwfl - dwfl_report_elf.c (source / functions) Hit Total Coverage
Test: lcov.out Lines: 88 126 69.8 %
Date: 2017-01-05 09:15:16 Functions: 3 3 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Report a module to libdwfl based on ELF program headers.
       2             :    Copyright (C) 2005-2010 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             : #include <fcntl.h>
      31             : #include <unistd.h>
      32             : 
      33             : 
      34             : /* We start every ET_REL module at a moderately aligned boundary.
      35             :    This keeps the low addresses easy to read compared to a layout
      36             :    starting at 0 (as when using -e).  It also makes it unlikely
      37             :    that a middle section will have a larger alignment and require
      38             :    rejiggering (see below).  */
      39             : #define REL_MIN_ALIGN   ((GElf_Xword) 0x100)
      40             : 
      41             : bool
      42             : internal_function
      43         392 : __libdwfl_elf_address_range (Elf *elf, GElf_Addr base, bool add_p_vaddr,
      44             :                              bool sanity, GElf_Addr *vaddrp,
      45             :                              GElf_Addr *address_syncp, GElf_Addr *startp,
      46             :                              GElf_Addr *endp, GElf_Addr *biasp,
      47             :                              GElf_Half *e_typep)
      48             : {
      49         392 :   GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
      50         392 :   if (ehdr == NULL)
      51             :     {
      52             :     elf_error:
      53           0 :       __libdwfl_seterrno (DWFL_E_LIBELF);
      54           0 :       return false;
      55             :     }
      56             : 
      57         392 :   GElf_Addr vaddr = 0;
      58         392 :   GElf_Addr address_sync = 0;
      59         392 :   GElf_Addr start = 0, end = 0, bias = 0;
      60         392 :   switch (ehdr->e_type)
      61             :     {
      62             :     case ET_REL:
      63             :       /* For a relocatable object, we do an arbitrary section layout.
      64             :          By updating the section header in place, we leave the layout
      65             :          information to be found by relocation.  */
      66             : 
      67          77 :       start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN;
      68             : 
      69          77 :       bool first = true;
      70          77 :       Elf_Scn *scn = NULL;
      71        1926 :       while ((scn = elf_nextscn (elf, scn)) != NULL)
      72             :         {
      73             :           GElf_Shdr shdr_mem;
      74        1772 :           GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
      75        1772 :           if (unlikely (shdr == NULL))
      76             :             goto elf_error;
      77             : 
      78        1772 :           if (shdr->sh_flags & SHF_ALLOC)
      79             :             {
      80         588 :               const GElf_Xword align = shdr->sh_addralign ?: 1;
      81         588 :               const GElf_Addr next = (end + align - 1) & -align;
      82         588 :               if (shdr->sh_addr == 0
      83             :                   /* Once we've started doing layout we have to do it all,
      84             :                      unless we just layed out the first section at 0 when
      85             :                      it already was at 0.  */
      86           3 :                   || (bias == 0 && end > start && end != next))
      87             :                 {
      88         585 :                   shdr->sh_addr = next;
      89         585 :                   if (end == base)
      90             :                     /* This is the first section assigned a location.
      91             :                        Use its aligned address as the module's base.  */
      92             :                     start = base = shdr->sh_addr;
      93         496 :                   else if (unlikely (base & (align - 1)))
      94             :                     {
      95             :                       /* If BASE has less than the maximum alignment of
      96             :                          any section, we eat more than the optimal amount
      97             :                          of padding and so make the module's apparent
      98             :                          size come out larger than it would when placed
      99             :                          at zero.  So reset the layout with a better base.  */
     100             : 
     101           0 :                       start = end = base = (base + align - 1) & -align;
     102           0 :                       Elf_Scn *prev_scn = NULL;
     103             :                       do
     104             :                         {
     105           0 :                           prev_scn = elf_nextscn (elf, prev_scn);
     106             :                           GElf_Shdr prev_shdr_mem;
     107           0 :                           GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
     108             :                                                                &prev_shdr_mem);
     109           0 :                           if (unlikely (prev_shdr == NULL))
     110             :                             goto elf_error;
     111           0 :                           if (prev_shdr->sh_flags & SHF_ALLOC)
     112             :                             {
     113           0 :                               const GElf_Xword prev_align
     114           0 :                                 = prev_shdr->sh_addralign ?: 1;
     115             : 
     116             :                               prev_shdr->sh_addr
     117           0 :                                 = (end + prev_align - 1) & -prev_align;
     118           0 :                               end = prev_shdr->sh_addr + prev_shdr->sh_size;
     119             : 
     120           0 :                               if (unlikely (! gelf_update_shdr (prev_scn,
     121             :                                                                 prev_shdr)))
     122             :                                 goto elf_error;
     123             :                             }
     124             :                         }
     125           0 :                       while (prev_scn != scn);
     126           0 :                       continue;
     127             :                     }
     128             : 
     129         585 :                   end = shdr->sh_addr + shdr->sh_size;
     130         585 :                   if (likely (shdr->sh_addr != 0)
     131         496 :                       && unlikely (! gelf_update_shdr (scn, shdr)))
     132             :                     goto elf_error;
     133             :                 }
     134             :               else
     135             :                 {
     136             :                   /* The address is already assigned.  Just track it.  */
     137           3 :                   if (first || end < shdr->sh_addr + shdr->sh_size)
     138           3 :                     end = shdr->sh_addr + shdr->sh_size;
     139           3 :                   if (first || bias > shdr->sh_addr)
     140             :                     /* This is the lowest address in the module.  */
     141           0 :                     bias = shdr->sh_addr;
     142             : 
     143           3 :                   if ((shdr->sh_addr - bias + base) & (align - 1))
     144             :                     /* This section winds up misaligned using BASE.
     145             :                        Adjust BASE upwards to make it congruent to
     146             :                        the lowest section address in the file modulo ALIGN.  */
     147           0 :                     base = (((base + align - 1) & -align)
     148           0 :                             + (bias & (align - 1)));
     149             :                 }
     150             : 
     151             :               first = false;
     152             :             }
     153             :         }
     154             : 
     155          77 :       if (bias != 0)
     156             :         {
     157             :           /* The section headers had nonzero sh_addr values.  The layout
     158             :              was already done.  We've just collected the total span.
     159             :              Now just compute the bias from the requested base.  */
     160           0 :           start = base;
     161           0 :           end = end - bias + start;
     162           0 :           bias = start - bias;
     163             :         }
     164         392 :       break;
     165             : 
     166             :       /* Everything else has to have program headers.  */
     167             : 
     168             :     case ET_EXEC:
     169             :     case ET_CORE:
     170             :       /* An assigned base address is meaningless for these.  */
     171         174 :       base = 0;
     172         174 :       add_p_vaddr = true;
     173             :       /* Fallthrough. */
     174             :     case ET_DYN:
     175             :     default:;
     176             :       size_t phnum;
     177         315 :       if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
     178             :         goto elf_error;
     179         408 :       for (size_t i = 0; i < phnum; ++i)
     180             :         {
     181         722 :           GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
     182         722 :           if (unlikely (ph == NULL))
     183             :             goto elf_error;
     184         722 :           if (ph->p_type == PT_LOAD)
     185             :             {
     186         314 :               vaddr = ph->p_vaddr & -ph->p_align;
     187         314 :               address_sync = ph->p_vaddr + ph->p_memsz;
     188         314 :               break;
     189             :             }
     190             :         }
     191         315 :       if (add_p_vaddr)
     192             :         {
     193         313 :           start = base + vaddr;
     194         313 :           bias = base;
     195             :         }
     196             :       else
     197             :         {
     198           2 :           start = base;
     199           2 :           bias = base - vaddr;
     200             :         }
     201             : 
     202        1932 :       for (size_t i = phnum; i-- > 0;)
     203             :         {
     204        1616 :           GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
     205        1616 :           if (unlikely (ph == NULL))
     206             :             goto elf_error;
     207        1616 :           if (ph->p_type == PT_LOAD
     208         314 :               && ph->p_vaddr + ph->p_memsz > 0)
     209             :             {
     210         314 :               end = bias + (ph->p_vaddr + ph->p_memsz);
     211         314 :               break;
     212             :             }
     213             :         }
     214             : 
     215         315 :       if (end == 0 && sanity)
     216             :         {
     217           0 :           __libdwfl_seterrno (DWFL_E_NO_PHDR);
     218           0 :           return false;
     219             :         }
     220             :       break;
     221             :     }
     222         392 :   if (vaddrp)
     223         314 :     *vaddrp = vaddr;
     224         392 :   if (address_syncp)
     225         314 :     *address_syncp = address_sync;
     226         392 :   if (startp)
     227         392 :     *startp = start;
     228         392 :   if (endp)
     229         392 :     *endp = end;
     230         392 :   if (biasp)
     231         314 :     *biasp = bias;
     232         392 :   if (e_typep)
     233         314 :     *e_typep = ehdr->e_type;
     234             :   return true;
     235             : }
     236             : 
     237             : Dwfl_Module *
     238             : internal_function
     239         314 : __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
     240             :                       int fd, Elf *elf, GElf_Addr base, bool add_p_vaddr,
     241             :                       bool sanity)
     242             : {
     243             :   GElf_Addr vaddr, address_sync, start, end, bias;
     244             :   GElf_Half e_type;
     245         314 :   if (! __libdwfl_elf_address_range (elf, base, add_p_vaddr, sanity, &vaddr,
     246             :                                      &address_sync, &start, &end, &bias,
     247             :                                      &e_type))
     248             :     return NULL;
     249         314 :   Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end);
     250         314 :   if (m != NULL)
     251             :     {
     252         314 :       if (m->main.name == NULL)
     253             :         {
     254         314 :           m->main.name = strdup (file_name);
     255         314 :           m->main.fd = fd;
     256             :         }
     257           0 :       else if ((fd >= 0 && m->main.fd != fd)
     258           0 :                || strcmp (m->main.name, file_name))
     259             :         {
     260             :         overlap:
     261           0 :           m->gc = true;
     262           0 :           __libdwfl_seterrno (DWFL_E_OVERLAP);
     263           0 :           return NULL;
     264             :         }
     265             : 
     266             :       /* Preinstall the open ELF handle for the module.  */
     267         314 :       if (m->main.elf == NULL)
     268             :         {
     269         314 :           m->main.elf = elf;
     270         314 :           m->main.vaddr = vaddr;
     271         314 :           m->main.address_sync = address_sync;
     272         314 :           m->main_bias = bias;
     273         314 :           m->e_type = e_type;
     274             :         }
     275             :       else
     276             :         {
     277           0 :           elf_end (elf);
     278           0 :           if (m->main_bias != bias
     279           0 :               || m->main.vaddr != vaddr || m->main.address_sync != address_sync)
     280             :             goto overlap;
     281             :         }
     282             :     }
     283             :   return m;
     284             : }
     285             : 
     286             : Dwfl_Module *
     287           3 : dwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
     288             :                  GElf_Addr base, bool add_p_vaddr)
     289             : {
     290           3 :   bool closefd = false;
     291           3 :   if (fd < 0)
     292             :     {
     293           3 :       closefd = true;
     294           3 :       fd = open (file_name, O_RDONLY);
     295           3 :       if (fd < 0)
     296             :         {
     297           0 :           __libdwfl_seterrno (DWFL_E_ERRNO);
     298           0 :           return NULL;
     299             :         }
     300             :     }
     301             : 
     302             :   Elf *elf;
     303           3 :   Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, false);
     304           3 :   if (error != DWFL_E_NOERROR)
     305             :     {
     306           0 :       __libdwfl_seterrno (error);
     307           0 :       return NULL;
     308             :     }
     309             : 
     310           3 :   Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name,
     311             :                                            fd, elf, base, add_p_vaddr, true);
     312           3 :   if (mod == NULL)
     313             :     {
     314           0 :       elf_end (elf);
     315           0 :       if (closefd)
     316           0 :         close (fd);
     317             :     }
     318             : 
     319             :   return mod;
     320             : }
     321             : INTDEF (dwfl_report_elf)
     322             : NEW_VERSION (dwfl_report_elf, ELFUTILS_0.156)
     323             : 
     324             : #ifdef SYMBOL_VERSIONING
     325             : Dwfl_Module *
     326             :   _compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name,
     327             :                                                const char *file_name, int fd,
     328             :                                                GElf_Addr base);
     329             : COMPAT_VERSION_NEWPROTO (dwfl_report_elf, ELFUTILS_0.122, without_add_p_vaddr)
     330             : 
     331             : Dwfl_Module *
     332             : _compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name,
     333             :                                              const char *file_name, int fd,
     334             :                                              GElf_Addr base)
     335             : {
     336             :   return dwfl_report_elf (dwfl, name, file_name, fd, base, true);
     337             : }
     338             : #endif

Generated by: LCOV version 1.12