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
__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)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
__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)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 *
dwfl_report_elf(Dwfl * dwfl,const char * name,const char * file_name,int fd,GElf_Addr base,bool add_p_vaddr)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 = open64 (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 SHARED
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 *
_compat_without_add_p_vaddr_dwfl_report_elf(Dwfl * dwfl,const char * name,const char * file_name,int fd,GElf_Addr base)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
339