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 
41 bool
42 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)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 
237 Dwfl_Module *
238 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)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 
286 Dwfl_Module *
dwfl_report_elf(Dwfl * dwfl,const char * name,const char * file_name,int fd,GElf_Addr base,bool add_p_vaddr)287 dwfl_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 = open64 (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 }
321 INTDEF (dwfl_report_elf)
322 NEW_VERSION (dwfl_report_elf, ELFUTILS_0.156)
323 
324 #ifdef SHARED
325 Dwfl_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);
329 COMPAT_VERSION_NEWPROTO (dwfl_report_elf, ELFUTILS_0.122, without_add_p_vaddr)
330 
331 Dwfl_Module *
_compat_without_add_p_vaddr_dwfl_report_elf(Dwfl * dwfl,const char * name,const char * file_name,int fd,GElf_Addr base)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