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