Imported Upstream version 0.153
[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 Red Hat elfutils.
4
5    Red Hat elfutils is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by the
7    Free Software Foundation; version 2 of the License.
8
9    Red Hat elfutils is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public License along
15    with Red Hat elfutils; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
17
18    In addition, as a special exception, Red Hat, Inc. gives You the
19    additional right to link the code of Red Hat elfutils with code licensed
20    under any Open Source Initiative certified open source license
21    (http://www.opensource.org/licenses/index.php) which requires the
22    distribution of source code with any binary distribution and to
23    distribute linked combinations of the two.  Non-GPL Code permitted under
24    this exception must only link to the code of Red Hat elfutils through
25    those well defined interfaces identified in the file named EXCEPTION
26    found in the source code files (the "Approved Interfaces").  The files
27    of Non-GPL Code may instantiate templates or use macros or inline
28    functions from the Approved Interfaces without causing the resulting
29    work to be covered by the GNU General Public License.  Only Red Hat,
30    Inc. may make changes or additions to the list of Approved Interfaces.
31    Red Hat's grant of this exception is conditioned upon your not adding
32    any new exceptions.  If you wish to add a new Approved Interface or
33    exception, please contact Red Hat.  You must obey the GNU General Public
34    License in all respects for all of the Red Hat elfutils code and other
35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
36    covered by this exception.  If you modify this file, you may extend this
37    exception to your version of the file, but you are not obligated to do
38    so.  If you do not wish to provide this exception without modification,
39    you must delete this exception statement from your version and license
40    this file solely under the GPL without exception.
41
42    Red Hat elfutils is an included package of the Open Invention Network.
43    An included package of the Open Invention Network is a package for which
44    Open Invention Network licensees cross-license their patents.  No patent
45    license is granted, either expressly or impliedly, by designation as an
46    included package.  Should you wish to participate in the Open Invention
47    Network licensing program, please visit www.openinventionnetwork.com
48    <http://www.openinventionnetwork.com>.  */
49
50 #include "libdwflP.h"
51 #include <fcntl.h>
52 #include <unistd.h>
53
54
55 /* We start every ET_REL module at a moderately aligned boundary.
56    This keeps the low addresses easy to read compared to a layout
57    starting at 0 (as when using -e).  It also makes it unlikely
58    that a middle section will have a larger alignment and require
59    rejiggering (see below).  */
60 #define REL_MIN_ALIGN   ((GElf_Xword) 0x100)
61
62 Dwfl_Module *
63 internal_function
64 __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
65                       int fd, Elf *elf, GElf_Addr base, bool sanity)
66 {
67   GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
68   if (ehdr == NULL)
69     {
70     elf_error:
71       __libdwfl_seterrno (DWFL_E_LIBELF);
72       return NULL;
73     }
74
75   GElf_Addr vaddr = 0;
76   GElf_Addr address_sync = 0;
77   GElf_Addr start = 0, end = 0, bias = 0;
78   switch (ehdr->e_type)
79     {
80     case ET_REL:
81       /* For a relocatable object, we do an arbitrary section layout.
82          By updating the section header in place, we leave the layout
83          information to be found by relocation.  */
84
85       start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN;
86
87       bool first = true;
88       Elf_Scn *scn = NULL;
89       while ((scn = elf_nextscn (elf, scn)) != NULL)
90         {
91           GElf_Shdr shdr_mem;
92           GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
93           if (unlikely (shdr == NULL))
94             goto elf_error;
95
96           if (shdr->sh_flags & SHF_ALLOC)
97             {
98               const GElf_Xword align = shdr->sh_addralign ?: 1;
99               const GElf_Addr next = (end + align - 1) & -align;
100               if (shdr->sh_addr == 0
101                   /* Once we've started doing layout we have to do it all,
102                      unless we just layed out the first section at 0 when
103                      it already was at 0.  */
104                   || (bias == 0 && end > start && end != next))
105                 {
106                   shdr->sh_addr = next;
107                   if (end == base)
108                     /* This is the first section assigned a location.
109                        Use its aligned address as the module's base.  */
110                     start = base = shdr->sh_addr;
111                   else if (unlikely (base & (align - 1)))
112                     {
113                       /* If BASE has less than the maximum alignment of
114                          any section, we eat more than the optimal amount
115                          of padding and so make the module's apparent
116                          size come out larger than it would when placed
117                          at zero.  So reset the layout with a better base.  */
118
119                       start = end = base = (base + align - 1) & -align;
120                       Elf_Scn *prev_scn = NULL;
121                       do
122                         {
123                           prev_scn = elf_nextscn (elf, prev_scn);
124                           GElf_Shdr prev_shdr_mem;
125                           GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
126                                                                &prev_shdr_mem);
127                           if (unlikely (prev_shdr == NULL))
128                             goto elf_error;
129                           if (prev_shdr->sh_flags & SHF_ALLOC)
130                             {
131                               const GElf_Xword prev_align
132                                 = prev_shdr->sh_addralign ?: 1;
133
134                               prev_shdr->sh_addr
135                                 = (end + prev_align - 1) & -prev_align;
136                               end = prev_shdr->sh_addr + prev_shdr->sh_size;
137
138                               if (unlikely (! gelf_update_shdr (prev_scn,
139                                                                 prev_shdr)))
140                                 goto elf_error;
141                             }
142                         }
143                       while (prev_scn != scn);
144                       continue;
145                     }
146
147                   end = shdr->sh_addr + shdr->sh_size;
148                   if (likely (shdr->sh_addr != 0)
149                       && unlikely (! gelf_update_shdr (scn, shdr)))
150                     goto elf_error;
151                 }
152               else
153                 {
154                   /* The address is already assigned.  Just track it.  */
155                   if (first || end < shdr->sh_addr + shdr->sh_size)
156                     end = shdr->sh_addr + shdr->sh_size;
157                   if (first || bias > shdr->sh_addr)
158                     /* This is the lowest address in the module.  */
159                     bias = shdr->sh_addr;
160
161                   if ((shdr->sh_addr - bias + base) & (align - 1))
162                     /* This section winds up misaligned using BASE.
163                        Adjust BASE upwards to make it congruent to
164                        the lowest section address in the file modulo ALIGN.  */
165                     base = (((base + align - 1) & -align)
166                             + (bias & (align - 1)));
167                 }
168
169               first = false;
170             }
171         }
172
173       if (bias != 0)
174         {
175           /* The section headers had nonzero sh_addr values.  The layout
176              was already done.  We've just collected the total span.
177              Now just compute the bias from the requested base.  */
178           start = base;
179           end = end - bias + start;
180           bias = start - bias;
181         }
182       break;
183
184       /* Everything else has to have program headers.  */
185
186     case ET_EXEC:
187     case ET_CORE:
188       /* An assigned base address is meaningless for these.  */
189       base = 0;
190
191     case ET_DYN:
192     default:;
193       size_t phnum;
194       if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
195         goto elf_error;
196       for (size_t i = 0; i < phnum; ++i)
197         {
198           GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
199           if (unlikely (ph == NULL))
200             goto elf_error;
201           if (ph->p_type == PT_LOAD)
202             {
203               vaddr = ph->p_vaddr & -ph->p_align;
204               address_sync = ph->p_vaddr + ph->p_memsz;
205               if ((base & (ph->p_align - 1)) != 0)
206                 base = (base + ph->p_align - 1) & -ph->p_align;
207               start = base + (ph->p_vaddr & -ph->p_align);
208               break;
209             }
210         }
211       bias = start - vaddr;
212
213       for (size_t i = phnum; i-- > 0;)
214         {
215           GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
216           if (unlikely (ph == NULL))
217             goto elf_error;
218           if (ph->p_type == PT_LOAD
219               && ph->p_vaddr + ph->p_memsz > 0)
220             {
221               end = base + (ph->p_vaddr + ph->p_memsz);
222               break;
223             }
224         }
225
226       if (end == 0 && sanity)
227         {
228           __libdwfl_seterrno (DWFL_E_NO_PHDR);
229           return NULL;
230         }
231       break;
232     }
233
234   Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end);
235   if (m != NULL)
236     {
237       if (m->main.name == NULL)
238         {
239           m->main.name = strdup (file_name);
240           m->main.fd = fd;
241         }
242       else if ((fd >= 0 && m->main.fd != fd)
243                || strcmp (m->main.name, file_name))
244         {
245         overlap:
246           m->gc = true;
247           __libdwfl_seterrno (DWFL_E_OVERLAP);
248           return NULL;
249         }
250
251       /* Preinstall the open ELF handle for the module.  */
252       if (m->main.elf == NULL)
253         {
254           m->main.elf = elf;
255           m->main.vaddr = vaddr;
256           m->main.address_sync = address_sync;
257           m->main_bias = bias;
258           m->e_type = ehdr->e_type;
259         }
260       else
261         {
262           elf_end (elf);
263           if (m->main_bias != bias
264               || m->main.vaddr != vaddr || m->main.address_sync != address_sync)
265             goto overlap;
266         }
267     }
268   return m;
269 }
270
271 Dwfl_Module *
272 dwfl_report_elf (Dwfl *dwfl, const char *name,
273                  const char *file_name, int fd, GElf_Addr base)
274 {
275   bool closefd = false;
276   if (fd < 0)
277     {
278       closefd = true;
279       fd = open64 (file_name, O_RDONLY);
280       if (fd < 0)
281         {
282           __libdwfl_seterrno (DWFL_E_ERRNO);
283           return NULL;
284         }
285     }
286
287   Elf *elf;
288   Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, false);
289   if (error != DWFL_E_NOERROR)
290     {
291       __libdwfl_seterrno (error);
292       return NULL;
293     }
294
295   Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name,
296                                            fd, elf, base, true);
297   if (mod == NULL)
298     {
299       elf_end (elf);
300       if (closefd)
301         close (fd);
302     }
303
304   return mod;
305 }
306 INTDEF (dwfl_report_elf)