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