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