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 Red Hat elfutils.
      4 
      5    Red Hat elfutils is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by the
      7    Free Software Foundation; version 2 of the License.
      8 
      9    Red Hat elfutils is distributed in the hope that it will be useful, but
     10    WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12    General Public License for more details.
     13 
     14    You should have received a copy of the GNU General Public License along
     15    with Red Hat elfutils; if not, write to the Free Software Foundation,
     16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
     17 
     18    In addition, as a special exception, Red Hat, Inc. gives You the
     19    additional right to link the code of Red Hat elfutils with code licensed
     20    under any Open Source Initiative certified open source license
     21    (http://www.opensource.org/licenses/index.php) which requires the
     22    distribution of source code with any binary distribution and to
     23    distribute linked combinations of the two.  Non-GPL Code permitted under
     24    this exception must only link to the code of Red Hat elfutils through
     25    those well defined interfaces identified in the file named EXCEPTION
     26    found in the source code files (the "Approved Interfaces").  The files
     27    of Non-GPL Code may instantiate templates or use macros or inline
     28    functions from the Approved Interfaces without causing the resulting
     29    work to be covered by the GNU General Public License.  Only Red Hat,
     30    Inc. may make changes or additions to the list of Approved Interfaces.
     31    Red Hat's grant of this exception is conditioned upon your not adding
     32    any new exceptions.  If you wish to add a new Approved Interface or
     33    exception, please contact Red Hat.  You must obey the GNU General Public
     34    License in all respects for all of the Red Hat elfutils code and other
     35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
     36    covered by this exception.  If you modify this file, you may extend this
     37    exception to your version of the file, but you are not obligated to do
     38    so.  If you do not wish to provide this exception without modification,
     39    you must delete this exception statement from your version and license
     40    this file solely under the GPL without exception.
     41 
     42    Red Hat elfutils is an included package of the Open Invention Network.
     43    An included package of the Open Invention Network is a package for which
     44    Open Invention Network licensees cross-license their patents.  No patent
     45    license is granted, either expressly or impliedly, by designation as an
     46    included package.  Should you wish to participate in the Open Invention
     47    Network licensing program, please visit www.openinventionnetwork.com
     48    <http://www.openinventionnetwork.com>.  */
     49 
     50 #include "libdwflP.h"
     51 #include <fcntl.h>
     52 #include <unistd.h>
     53 
     54 
     55 /* We start every ET_REL module at a moderately aligned boundary.
     56    This keeps the low addresses easy to read compared to a layout
     57    starting at 0 (as when using -e).  It also makes it unlikely
     58    that a middle section will have a larger alignment and require
     59    rejiggering (see below).  */
     60 #define REL_MIN_ALIGN	((GElf_Xword) 0x100)
     61 
     62 Dwfl_Module *
     63 internal_function
     64 __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
     65 		      int fd, Elf *elf, GElf_Addr base, bool sanity)
     66 {
     67   GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
     68   if (ehdr == NULL)
     69     {
     70     elf_error:
     71       __libdwfl_seterrno (DWFL_E_LIBELF);
     72       return NULL;
     73     }
     74 
     75   GElf_Addr vaddr = 0;
     76   GElf_Addr address_sync = 0;
     77   GElf_Addr start = 0, end = 0, bias = 0;
     78   switch (ehdr->e_type)
     79     {
     80     case ET_REL:
     81       /* For a relocatable object, we do an arbitrary section layout.
     82 	 By updating the section header in place, we leave the layout
     83 	 information to be found by relocation.  */
     84 
     85       start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN;
     86 
     87       bool first = true;
     88       Elf_Scn *scn = NULL;
     89       while ((scn = elf_nextscn (elf, scn)) != NULL)
     90 	{
     91 	  GElf_Shdr shdr_mem;
     92 	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
     93 	  if (unlikely (shdr == NULL))
     94 	    goto elf_error;
     95 
     96 	  if (shdr->sh_flags & SHF_ALLOC)
     97 	    {
     98 	      const GElf_Xword align = shdr->sh_addralign ?: 1;
     99 	      const GElf_Addr next = (end + align - 1) & -align;
    100 	      if (shdr->sh_addr == 0
    101 		  /* Once we've started doing layout we have to do it all,
    102 		     unless we just layed out the first section at 0 when
    103 		     it already was at 0.  */
    104 		  || (bias == 0 && end > start && end != next))
    105 		{
    106 		  shdr->sh_addr = next;
    107 		  if (end == base)
    108 		    /* This is the first section assigned a location.
    109 		       Use its aligned address as the module's base.  */
    110 		    start = base = shdr->sh_addr;
    111 		  else if (unlikely (base & (align - 1)))
    112 		    {
    113 		      /* If BASE has less than the maximum alignment of
    114 			 any section, we eat more than the optimal amount
    115 			 of padding and so make the module's apparent
    116 			 size come out larger than it would when placed
    117 			 at zero.  So reset the layout with a better base.  */
    118 
    119 		      start = end = base = (base + align - 1) & -align;
    120 		      Elf_Scn *prev_scn = NULL;
    121 		      do
    122 			{
    123 			  prev_scn = elf_nextscn (elf, prev_scn);
    124 			  GElf_Shdr prev_shdr_mem;
    125 			  GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
    126 							       &prev_shdr_mem);
    127 			  if (unlikely (prev_shdr == NULL))
    128 			    goto elf_error;
    129 			  if (prev_shdr->sh_flags & SHF_ALLOC)
    130 			    {
    131 			      const GElf_Xword prev_align
    132 				= prev_shdr->sh_addralign ?: 1;
    133 
    134 			      prev_shdr->sh_addr
    135 				= (end + prev_align - 1) & -prev_align;
    136 			      end = prev_shdr->sh_addr + prev_shdr->sh_size;
    137 
    138 			      if (unlikely (! gelf_update_shdr (prev_scn,
    139 								prev_shdr)))
    140 				goto elf_error;
    141 			    }
    142 			}
    143 		      while (prev_scn != scn);
    144 		      continue;
    145 		    }
    146 
    147 		  end = shdr->sh_addr + shdr->sh_size;
    148 		  if (likely (shdr->sh_addr != 0)
    149 		      && unlikely (! gelf_update_shdr (scn, shdr)))
    150 		    goto elf_error;
    151 		}
    152 	      else
    153 		{
    154 		  /* The address is already assigned.  Just track it.  */
    155 		  if (first || end < shdr->sh_addr + shdr->sh_size)
    156 		    end = shdr->sh_addr + shdr->sh_size;
    157 		  if (first || bias > shdr->sh_addr)
    158 		    /* This is the lowest address in the module.  */
    159 		    bias = shdr->sh_addr;
    160 
    161 		  if ((shdr->sh_addr - bias + base) & (align - 1))
    162 		    /* This section winds up misaligned using BASE.
    163 		       Adjust BASE upwards to make it congruent to
    164 		       the lowest section address in the file modulo ALIGN.  */
    165 		    base = (((base + align - 1) & -align)
    166 			    + (bias & (align - 1)));
    167 		}
    168 
    169 	      first = false;
    170 	    }
    171 	}
    172 
    173       if (bias != 0)
    174 	{
    175 	  /* The section headers had nonzero sh_addr values.  The layout
    176 	     was already done.  We've just collected the total span.
    177 	     Now just compute the bias from the requested base.  */
    178 	  start = base;
    179 	  end = end - bias + start;
    180 	  bias = start - bias;
    181 	}
    182       break;
    183 
    184       /* Everything else has to have program headers.  */
    185 
    186     case ET_EXEC:
    187     case ET_CORE:
    188       /* An assigned base address is meaningless for these.  */
    189       base = 0;
    190 
    191     case ET_DYN:
    192     default:;
    193       size_t phnum;
    194       if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
    195 	goto elf_error;
    196       for (size_t i = 0; i < phnum; ++i)
    197 	{
    198 	  GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
    199 	  if (unlikely (ph == NULL))
    200 	    goto elf_error;
    201 	  if (ph->p_type == PT_LOAD)
    202 	    {
    203 	      vaddr = ph->p_vaddr & -ph->p_align;
    204 	      address_sync = ph->p_vaddr + ph->p_memsz;
    205 	      if ((base & (ph->p_align - 1)) != 0)
    206 		base = (base + ph->p_align - 1) & -ph->p_align;
    207 	      start = base + (ph->p_vaddr & -ph->p_align);
    208 	      break;
    209 	    }
    210 	}
    211       bias = start - vaddr;
    212 
    213       for (size_t i = phnum; i-- > 0;)
    214 	{
    215 	  GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
    216 	  if (unlikely (ph == NULL))
    217 	    goto elf_error;
    218 	  if (ph->p_type == PT_LOAD
    219 	      && ph->p_vaddr + ph->p_memsz > 0)
    220 	    {
    221 	      end = base + (ph->p_vaddr + ph->p_memsz);
    222 	      break;
    223 	    }
    224 	}
    225 
    226       if (end == 0 && sanity)
    227 	{
    228 	  __libdwfl_seterrno (DWFL_E_NO_PHDR);
    229 	  return NULL;
    230 	}
    231       break;
    232     }
    233 
    234   Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end);
    235   if (m != NULL)
    236     {
    237       if (m->main.name == NULL)
    238 	{
    239 	  m->main.name = strdup (file_name);
    240 	  m->main.fd = fd;
    241 	}
    242       else if ((fd >= 0 && m->main.fd != fd)
    243 	       || strcmp (m->main.name, file_name))
    244 	{
    245 	overlap:
    246 	  m->gc = true;
    247 	  __libdwfl_seterrno (DWFL_E_OVERLAP);
    248 	  return NULL;
    249 	}
    250 
    251       /* Preinstall the open ELF handle for the module.  */
    252       if (m->main.elf == NULL)
    253 	{
    254 	  m->main.elf = elf;
    255 	  m->main.vaddr = vaddr;
    256 	  m->main.address_sync = address_sync;
    257 	  m->main_bias = bias;
    258 	  m->e_type = ehdr->e_type;
    259 	}
    260       else
    261 	{
    262 	  elf_end (elf);
    263 	  if (m->main_bias != bias
    264 	      || m->main.vaddr != vaddr || m->main.address_sync != address_sync)
    265 	    goto overlap;
    266 	}
    267     }
    268   return m;
    269 }
    270 
    271 Dwfl_Module *
    272 dwfl_report_elf (Dwfl *dwfl, const char *name,
    273 		 const char *file_name, int fd, GElf_Addr base)
    274 {
    275   bool closefd = false;
    276   if (fd < 0)
    277     {
    278       closefd = true;
    279       fd = open64 (file_name, O_RDONLY);
    280       if (fd < 0)
    281 	{
    282 	  __libdwfl_seterrno (DWFL_E_ERRNO);
    283 	  return NULL;
    284 	}
    285     }
    286 
    287   Elf *elf;
    288   Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, false);
    289   if (error != DWFL_E_NOERROR)
    290     {
    291       __libdwfl_seterrno (error);
    292       return NULL;
    293     }
    294 
    295   Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name,
    296 					   fd, elf, base, true);
    297   if (mod == NULL)
    298     {
    299       elf_end (elf);
    300       if (closefd)
    301 	close (fd);
    302     }
    303 
    304   return mod;
    305 }
    306 INTDEF (dwfl_report_elf)
    307