Home | History | Annotate | Download | only in libdwfl
      1 /* Report a module to libdwfl based on ELF program headers.
      2    Copyright (C) 2005, 2007 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)
     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 start = 0, end = 0, bias = 0;
     76   switch (ehdr->e_type)
     77     {
     78     case ET_REL:
     79       /* For a relocatable object, we do an arbitrary section layout.
     80 	 By updating the section header in place, we leave the layout
     81 	 information to be found by relocation.  */
     82 
     83       start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN;
     84 
     85       bool first = true;
     86       Elf_Scn *scn = NULL;
     87       while ((scn = elf_nextscn (elf, scn)) != NULL)
     88 	{
     89 	  GElf_Shdr shdr_mem;
     90 	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
     91 	  if (unlikely (shdr == NULL))
     92 	    goto elf_error;
     93 
     94 	  if (shdr->sh_flags & SHF_ALLOC)
     95 	    {
     96 	      const GElf_Xword align = shdr->sh_addralign ?: 1;
     97 	      const GElf_Addr next = (end + align - 1) & -align;
     98 	      if (shdr->sh_addr == 0
     99 		  /* Once we've started doing layout we have to do it all,
    100 		     unless we just layed out the first section at 0 when
    101 		     it already was at 0.  */
    102 		  || (bias == 0 && end > start && end != next))
    103 		{
    104 		  shdr->sh_addr = next;
    105 		  if (end == base)
    106 		    /* This is the first section assigned a location.
    107 		       Use its aligned address as the module's base.  */
    108 		    start = base = shdr->sh_addr;
    109 		  else if (unlikely (base & (align - 1)))
    110 		    {
    111 		      /* If BASE has less than the maximum alignment of
    112 			 any section, we eat more than the optimal amount
    113 			 of padding and so make the module's apparent
    114 			 size come out larger than it would when placed
    115 			 at zero.  So reset the layout with a better base.  */
    116 
    117 		      start = end = base = (base + align - 1) & -align;
    118 		      Elf_Scn *prev_scn = NULL;
    119 		      do
    120 			{
    121 			  prev_scn = elf_nextscn (elf, prev_scn);
    122 			  GElf_Shdr prev_shdr_mem;
    123 			  GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
    124 							       &prev_shdr_mem);
    125 			  if (unlikely (prev_shdr == NULL))
    126 			    goto elf_error;
    127 			  if (prev_shdr->sh_flags & SHF_ALLOC)
    128 			    {
    129 			      const GElf_Xword prev_align
    130 				= prev_shdr->sh_addralign ?: 1;
    131 
    132 			      prev_shdr->sh_addr
    133 				= (end + prev_align - 1) & -prev_align;
    134 			      end = prev_shdr->sh_addr + prev_shdr->sh_size;
    135 
    136 			      if (unlikely (! gelf_update_shdr (prev_scn,
    137 								prev_shdr)))
    138 				goto elf_error;
    139 			    }
    140 			}
    141 		      while (prev_scn != scn);
    142 		      continue;
    143 		    }
    144 
    145 		  end = shdr->sh_addr + shdr->sh_size;
    146 		  if (likely (shdr->sh_addr != 0)
    147 		      && unlikely (! gelf_update_shdr (scn, shdr)))
    148 		    goto elf_error;
    149 		}
    150 	      else
    151 		{
    152 		  /* The address is already assigned.  Just track it.  */
    153 		  if (first || end < shdr->sh_addr + shdr->sh_size)
    154 		    end = shdr->sh_addr + shdr->sh_size;
    155 		  if (first || bias > shdr->sh_addr)
    156 		    /* This is the lowest address in the module.  */
    157 		    bias = shdr->sh_addr;
    158 
    159 		  if ((shdr->sh_addr - bias + base) & (align - 1))
    160 		    /* This section winds up misaligned using BASE.
    161 		       Adjust BASE upwards to make it congruent to
    162 		       the lowest section address in the file modulo ALIGN.  */
    163 		    base = (((base + align - 1) & -align)
    164 			    + (bias & (align - 1)));
    165 		}
    166 
    167 	      first = false;
    168 	    }
    169 	}
    170 
    171       if (bias != 0)
    172 	{
    173 	  /* The section headers had nonzero sh_addr values.  The layout
    174 	     was already done.  We've just collected the total span.
    175 	     Now just compute the bias from the requested base.  */
    176 	  start = base;
    177 	  end = end - bias + start;
    178 	  bias = start - bias;
    179 	}
    180       break;
    181 
    182       /* Everything else has to have program headers.  */
    183 
    184     case ET_EXEC:
    185     case ET_CORE:
    186       /* An assigned base address is meaningless for these.  */
    187       base = 0;
    188 
    189     case ET_DYN:
    190     default:
    191       for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i)
    192 	{
    193 	  GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
    194 	  if (unlikely (ph == NULL))
    195 	    goto elf_error;
    196 	  if (ph->p_type == PT_LOAD)
    197 	    {
    198 	      if ((base & (ph->p_align - 1)) != 0)
    199 		base = (base + ph->p_align - 1) & -ph->p_align;
    200 	      start = base + (ph->p_vaddr & -ph->p_align);
    201 	      break;
    202 	    }
    203 	}
    204       bias = base;
    205 
    206       for (uint_fast16_t i = ehdr->e_phnum; i-- > 0;)
    207 	{
    208 	  GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
    209 	  if (unlikely (ph == NULL))
    210 	    goto elf_error;
    211 	  if (ph->p_type == PT_LOAD)
    212 	    {
    213 	      end = base + (ph->p_vaddr + ph->p_memsz);
    214 	      break;
    215 	    }
    216 	}
    217 
    218       if (end == 0)
    219 	{
    220 	  __libdwfl_seterrno (DWFL_E_NO_PHDR);
    221 	  return NULL;
    222 	}
    223       break;
    224     }
    225 
    226   Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end);
    227   if (m != NULL)
    228     {
    229       if (m->main.name == NULL)
    230 	{
    231 	  m->main.name = strdup (file_name);
    232 	  m->main.fd = fd;
    233 	}
    234       else if ((fd >= 0 && m->main.fd != fd)
    235 	       || strcmp (m->main.name, file_name))
    236 	{
    237 	  elf_end (elf);
    238 	overlap:
    239 	  m->gc = true;
    240 	  __libdwfl_seterrno (DWFL_E_OVERLAP);
    241 	  m = NULL;
    242 	}
    243 
    244       /* Preinstall the open ELF handle for the module.  */
    245       if (m->main.elf == NULL)
    246 	{
    247 	  m->main.elf = elf;
    248 	  m->main.bias = bias;
    249 	  m->e_type = ehdr->e_type;
    250 	}
    251       else
    252 	{
    253 	  elf_end (elf);
    254 	  if (m->main.bias != base)
    255 	    goto overlap;
    256 	}
    257     }
    258   return m;
    259 }
    260 
    261 Dwfl_Module *
    262 dwfl_report_elf (Dwfl *dwfl, const char *name,
    263 		 const char *file_name, int fd, GElf_Addr base)
    264 {
    265   bool closefd = false;
    266   if (fd < 0)
    267     {
    268       closefd = true;
    269       fd = open64 (file_name, O_RDONLY);
    270       if (fd < 0)
    271 	{
    272 	  __libdwfl_seterrno (DWFL_E_ERRNO);
    273 	  return NULL;
    274 	}
    275     }
    276 
    277   Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
    278   Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name,
    279 					   fd, elf, base);
    280   if (mod == NULL)
    281     {
    282       elf_end (elf);
    283       if (closefd)
    284 	close (fd);
    285     }
    286 
    287   return mod;
    288 }
    289 INTDEF (dwfl_report_elf)
    290