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