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