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