• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
__libdwfl_report_elf(Dwfl * dwfl,const char * name,const char * file_name,int fd,Elf * elf,GElf_Addr base,bool sanity)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 *
dwfl_report_elf(Dwfl * dwfl,const char * name,const char * file_name,int fd,GElf_Addr base)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