Home | History | Annotate | Download | only in libdwfl
      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 __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   GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
     50   if (ehdr == NULL)
     51     {
     52     elf_error:
     53       __libdwfl_seterrno (DWFL_E_LIBELF);
     54       return false;
     55     }
     56 
     57   GElf_Addr vaddr = 0;
     58   GElf_Addr address_sync = 0;
     59   GElf_Addr start = 0, end = 0, bias = 0;
     60   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       start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN;
     68 
     69       bool first = true;
     70       Elf_Scn *scn = NULL;
     71       while ((scn = elf_nextscn (elf, scn)) != NULL)
     72 	{
     73 	  GElf_Shdr shdr_mem;
     74 	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
     75 	  if (unlikely (shdr == NULL))
     76 	    goto elf_error;
     77 
     78 	  if (shdr->sh_flags & SHF_ALLOC)
     79 	    {
     80 	      const GElf_Xword align = shdr->sh_addralign ?: 1;
     81 	      const GElf_Addr next = (end + align - 1) & -align;
     82 	      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 		  || (bias == 0 && end > start && end != next))
     87 		{
     88 		  shdr->sh_addr = next;
     89 		  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 		  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 		      start = end = base = (base + align - 1) & -align;
    102 		      Elf_Scn *prev_scn = NULL;
    103 		      do
    104 			{
    105 			  prev_scn = elf_nextscn (elf, prev_scn);
    106 			  GElf_Shdr prev_shdr_mem;
    107 			  GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
    108 							       &prev_shdr_mem);
    109 			  if (unlikely (prev_shdr == NULL))
    110 			    goto elf_error;
    111 			  if (prev_shdr->sh_flags & SHF_ALLOC)
    112 			    {
    113 			      const GElf_Xword prev_align
    114 				= prev_shdr->sh_addralign ?: 1;
    115 
    116 			      prev_shdr->sh_addr
    117 				= (end + prev_align - 1) & -prev_align;
    118 			      end = prev_shdr->sh_addr + prev_shdr->sh_size;
    119 
    120 			      if (unlikely (! gelf_update_shdr (prev_scn,
    121 								prev_shdr)))
    122 				goto elf_error;
    123 			    }
    124 			}
    125 		      while (prev_scn != scn);
    126 		      continue;
    127 		    }
    128 
    129 		  end = shdr->sh_addr + shdr->sh_size;
    130 		  if (likely (shdr->sh_addr != 0)
    131 		      && unlikely (! gelf_update_shdr (scn, shdr)))
    132 		    goto elf_error;
    133 		}
    134 	      else
    135 		{
    136 		  /* The address is already assigned.  Just track it.  */
    137 		  if (first || end < shdr->sh_addr + shdr->sh_size)
    138 		    end = shdr->sh_addr + shdr->sh_size;
    139 		  if (first || bias > shdr->sh_addr)
    140 		    /* This is the lowest address in the module.  */
    141 		    bias = shdr->sh_addr;
    142 
    143 		  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 		    base = (((base + align - 1) & -align)
    148 			    + (bias & (align - 1)));
    149 		}
    150 
    151 	      first = false;
    152 	    }
    153 	}
    154 
    155       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 	  start = base;
    161 	  end = end - bias + start;
    162 	  bias = start - bias;
    163 	}
    164       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       base = 0;
    172       add_p_vaddr = true;
    173 
    174     case ET_DYN:
    175     default:;
    176       size_t phnum;
    177       if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
    178 	goto elf_error;
    179       for (size_t i = 0; i < phnum; ++i)
    180 	{
    181 	  GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
    182 	  if (unlikely (ph == NULL))
    183 	    goto elf_error;
    184 	  if (ph->p_type == PT_LOAD)
    185 	    {
    186 	      vaddr = ph->p_vaddr & -ph->p_align;
    187 	      address_sync = ph->p_vaddr + ph->p_memsz;
    188 	      break;
    189 	    }
    190 	}
    191       if (add_p_vaddr)
    192 	{
    193 	  start = base + vaddr;
    194 	  bias = base;
    195 	}
    196       else
    197 	{
    198 	  start = base;
    199 	  bias = base - vaddr;
    200 	}
    201 
    202       for (size_t i = phnum; i-- > 0;)
    203 	{
    204 	  GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
    205 	  if (unlikely (ph == NULL))
    206 	    goto elf_error;
    207 	  if (ph->p_type == PT_LOAD
    208 	      && ph->p_vaddr + ph->p_memsz > 0)
    209 	    {
    210 	      end = bias + (ph->p_vaddr + ph->p_memsz);
    211 	      break;
    212 	    }
    213 	}
    214 
    215       if (end == 0 && sanity)
    216 	{
    217 	  __libdwfl_seterrno (DWFL_E_NO_PHDR);
    218 	  return false;
    219 	}
    220       break;
    221     }
    222   if (vaddrp)
    223     *vaddrp = vaddr;
    224   if (address_syncp)
    225     *address_syncp = address_sync;
    226   if (startp)
    227     *startp = start;
    228   if (endp)
    229     *endp = end;
    230   if (biasp)
    231     *biasp = bias;
    232   if (e_typep)
    233     *e_typep = ehdr->e_type;
    234   return true;
    235 }
    236 
    237 Dwfl_Module *
    238 internal_function
    239 __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   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   Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end);
    250   if (m != NULL)
    251     {
    252       if (m->main.name == NULL)
    253 	{
    254 	  m->main.name = strdup (file_name);
    255 	  m->main.fd = fd;
    256 	}
    257       else if ((fd >= 0 && m->main.fd != fd)
    258 	       || strcmp (m->main.name, file_name))
    259 	{
    260 	overlap:
    261 	  m->gc = true;
    262 	  __libdwfl_seterrno (DWFL_E_OVERLAP);
    263 	  return NULL;
    264 	}
    265 
    266       /* Preinstall the open ELF handle for the module.  */
    267       if (m->main.elf == NULL)
    268 	{
    269 	  m->main.elf = elf;
    270 	  m->main.vaddr = vaddr;
    271 	  m->main.address_sync = address_sync;
    272 	  m->main_bias = bias;
    273 	  m->e_type = e_type;
    274 	}
    275       else
    276 	{
    277 	  elf_end (elf);
    278 	  if (m->main_bias != bias
    279 	      || m->main.vaddr != vaddr || m->main.address_sync != address_sync)
    280 	    goto overlap;
    281 	}
    282     }
    283   return m;
    284 }
    285 
    286 Dwfl_Module *
    287 dwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
    288 		 GElf_Addr base, bool add_p_vaddr)
    289 {
    290   bool closefd = false;
    291   if (fd < 0)
    292     {
    293       closefd = true;
    294       fd = open (file_name, O_RDONLY);
    295       if (fd < 0)
    296 	{
    297 	  __libdwfl_seterrno (DWFL_E_ERRNO);
    298 	  return NULL;
    299 	}
    300     }
    301 
    302   Elf *elf;
    303   Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, false);
    304   if (error != DWFL_E_NOERROR)
    305     {
    306       __libdwfl_seterrno (error);
    307       return NULL;
    308     }
    309 
    310   Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name,
    311 					   fd, elf, base, add_p_vaddr, true);
    312   if (mod == NULL)
    313     {
    314       elf_end (elf);
    315       if (closefd)
    316 	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
    339