packaging: update homepage url
[platform/upstream/elfutils.git] / libdwfl / dwfl_report_elf.c
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
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
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 *
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 = 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 }
321 INTDEF (dwfl_report_elf)
322 NEW_VERSION (dwfl_report_elf, ELFUTILS_0.156)
323
324 #ifdef SYMBOL_VERSIONING
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 *
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