/src/elfutils/libdwfl/dwfl_report_elf.c
Line | Count | Source (jump to first uncovered line) |
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 | 9.41k | #define REL_MIN_ALIGN ((GElf_Xword) 0x100) |
42 | | |
43 | | bool |
44 | | internal_function |
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 | 21.4k | { |
51 | 21.4k | GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem); |
52 | 21.4k | if (ehdr == NULL) |
53 | 0 | { |
54 | 864 | elf_error: |
55 | 864 | __libdwfl_seterrno (DWFL_E_LIBELF); |
56 | 864 | return false; |
57 | 0 | } |
58 | | |
59 | 21.4k | GElf_Addr vaddr = 0; |
60 | 21.4k | GElf_Addr address_sync = 0; |
61 | 21.4k | GElf_Addr start = 0, end = 0, bias = 0; |
62 | 21.4k | switch (ehdr->e_type) |
63 | 21.4k | { |
64 | 4.70k | 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 | 4.70k | start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN; |
70 | | |
71 | 4.70k | bool first = true; |
72 | 4.70k | Elf_Scn *scn = NULL; |
73 | 48.0k | while ((scn = elf_nextscn (elf, scn)) != NULL) |
74 | 121k | { |
75 | 121k | GElf_Shdr shdr_mem; |
76 | 121k | GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); |
77 | 121k | if (unlikely (shdr == NULL)) |
78 | 0 | goto elf_error; |
79 | | |
80 | 121k | if (shdr->sh_flags & SHF_ALLOC) |
81 | 55.0k | { |
82 | 55.0k | const GElf_Xword align = shdr->sh_addralign ?: 1; |
83 | 55.0k | const GElf_Addr next = (end + align - 1) & -align; |
84 | 898 | 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 | 53.9k | || (bias == 0 && end > start && end != next)) |
89 | 3.40k | { |
90 | 3.40k | shdr->sh_addr = next; |
91 | 3.40k | if (end == base) |
92 | | /* This is the first section assigned a location. |
93 | | Use its aligned address as the module's base. */ |
94 | 591 | start = base = shdr->sh_addr; |
95 | 2.81k | else if (unlikely (base & (align - 1))) |
96 | 566 | { |
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 | 566 | start = end = base = (base + align - 1) & -align; |
104 | 566 | Elf_Scn *prev_scn = NULL; |
105 | 566 | do |
106 | 123k | { |
107 | 123k | prev_scn = elf_nextscn (elf, prev_scn); |
108 | 123k | GElf_Shdr prev_shdr_mem; |
109 | 123k | GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn, |
110 | 123k | &prev_shdr_mem); |
111 | 123k | if (unlikely (prev_shdr == NULL)) |
112 | 0 | goto elf_error; |
113 | 123k | if (prev_shdr->sh_flags & SHF_ALLOC) |
114 | 36.3k | { |
115 | 36.3k | const GElf_Xword prev_align |
116 | 36.3k | = prev_shdr->sh_addralign ?: 1; |
117 | | |
118 | 36.3k | prev_shdr->sh_addr |
119 | 12.5k | = (end + prev_align - 1) & -prev_align; |
120 | 12.5k | end = prev_shdr->sh_addr + prev_shdr->sh_size; |
121 | | |
122 | 12.5k | if (unlikely (! gelf_update_shdr (prev_scn, |
123 | 12.5k | prev_shdr))) |
124 | 59 | goto elf_error; |
125 | 12.5k | } |
126 | 123k | } |
127 | 99.9k | while (prev_scn != scn); |
128 | 18.4E | continue; |
129 | 566 | } |
130 | | |
131 | 2.84k | end = shdr->sh_addr + shdr->sh_size; |
132 | 2.84k | if (likely (shdr->sh_addr != 0) |
133 | 2.84k | && unlikely (! gelf_update_shdr (scn, shdr))) |
134 | 23 | goto elf_error; |
135 | 2.84k | } |
136 | 18.4E | else |
137 | 18.4E | { |
138 | | /* The address is already assigned. Just track it. */ |
139 | 18.4E | if (first || end < shdr->sh_addr + shdr->sh_size) |
140 | 1.93k | end = shdr->sh_addr + shdr->sh_size; |
141 | 18.4E | if (first || bias > shdr->sh_addr) |
142 | | /* This is the lowest address in the module. */ |
143 | 1.29k | bias = shdr->sh_addr; |
144 | | |
145 | 18.4E | 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 | 50.5k | base = (((base + align - 1) & -align) |
150 | 50.5k | + (bias & (align - 1))); |
151 | 18.4E | } |
152 | | |
153 | 309 | first = false; |
154 | 309 | } |
155 | 121k | } |
156 | | |
157 | 18.4E | if (bias != 0) |
158 | 995 | { |
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 | 995 | start = base; |
163 | 995 | end = end - bias + start; |
164 | 995 | bias = start - bias; |
165 | 995 | } |
166 | 18.4E | break; |
167 | | |
168 | | /* Everything else has to have program headers. */ |
169 | | |
170 | 256 | case ET_EXEC: |
171 | 367 | case ET_CORE: |
172 | | /* An assigned base address is meaningless for these. */ |
173 | 367 | base = 0; |
174 | 367 | add_p_vaddr = true; |
175 | 367 | FALLTHROUGH; |
176 | 376 | case ET_DYN: |
177 | 16.7k | default:; |
178 | 16.7k | size_t phnum; |
179 | 16.7k | if (unlikely (elf_getphdrnum (elf, &phnum) != 0)) |
180 | 595 | goto elf_error; |
181 | 4.36M | for (size_t i = 0; i < phnum; ++i) |
182 | 4.34M | { |
183 | 4.34M | GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem); |
184 | 4.34M | if (unlikely (ph == NULL)) |
185 | 187 | goto elf_error; |
186 | 4.34M | if (ph->p_type == PT_LOAD) |
187 | 2.15k | { |
188 | 2.15k | vaddr = ph->p_vaddr & -ph->p_align; |
189 | 2.15k | address_sync = ph->p_vaddr + ph->p_memsz; |
190 | 2.15k | break; |
191 | 2.15k | } |
192 | 4.34M | } |
193 | 15.9k | if (add_p_vaddr) |
194 | 15.9k | { |
195 | 15.9k | start = base + vaddr; |
196 | 15.9k | bias = base; |
197 | 15.9k | } |
198 | 0 | else |
199 | 0 | { |
200 | 0 | start = base; |
201 | 0 | bias = base - vaddr; |
202 | 0 | } |
203 | | |
204 | 5.45M | for (size_t i = phnum; i-- > 0;) |
205 | 5.44M | { |
206 | 5.44M | GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem); |
207 | 5.44M | if (unlikely (ph == NULL)) |
208 | 0 | goto elf_error; |
209 | 5.44M | if (ph->p_type == PT_LOAD |
210 | 5.44M | && ph->p_vaddr + ph->p_memsz > 0) |
211 | 2.14k | { |
212 | 2.14k | end = bias + (ph->p_vaddr + ph->p_memsz); |
213 | 2.14k | break; |
214 | 2.14k | } |
215 | 5.44M | } |
216 | | |
217 | 15.9k | if (end == 0 && sanity) |
218 | 0 | { |
219 | 0 | __libdwfl_seterrno (DWFL_E_NO_PHDR); |
220 | 0 | return false; |
221 | 0 | } |
222 | 15.9k | break; |
223 | 21.4k | } |
224 | 20.5k | if (vaddrp) |
225 | 20.5k | *vaddrp = vaddr; |
226 | 20.5k | if (address_syncp) |
227 | 20.5k | *address_syncp = address_sync; |
228 | 20.5k | if (startp) |
229 | 20.5k | *startp = start; |
230 | 20.5k | if (endp) |
231 | 20.5k | *endp = end; |
232 | 20.5k | if (biasp) |
233 | 20.5k | *biasp = bias; |
234 | 20.5k | if (e_typep) |
235 | 20.5k | *e_typep = ehdr->e_type; |
236 | 20.5k | return true; |
237 | 21.4k | } |
238 | | |
239 | | Dwfl_Module * |
240 | | internal_function |
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 | 21.4k | { |
245 | 21.4k | GElf_Addr vaddr, address_sync, start, end, bias; |
246 | 21.4k | GElf_Half e_type; |
247 | 21.4k | if (! __libdwfl_elf_address_range (elf, base, add_p_vaddr, sanity, &vaddr, |
248 | 21.4k | &address_sync, &start, &end, &bias, |
249 | 21.4k | &e_type)) |
250 | 864 | return NULL; |
251 | 20.5k | Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end); |
252 | 20.5k | if (m != NULL) |
253 | 20.5k | { |
254 | 20.5k | if (m->main.name == NULL) |
255 | 19.9k | { |
256 | 19.9k | m->main.name = strdup (file_name); |
257 | 19.9k | m->main.fd = fd; |
258 | 19.9k | } |
259 | 666 | else if ((fd >= 0 && m->main.fd != fd) |
260 | 666 | || strcmp (m->main.name, file_name)) |
261 | 10 | { |
262 | 56 | overlap: |
263 | 56 | m->gc = true; |
264 | 56 | __libdwfl_seterrno (DWFL_E_OVERLAP); |
265 | 56 | return NULL; |
266 | 10 | } |
267 | | |
268 | | /* Preinstall the open ELF handle for the module. */ |
269 | 20.5k | if (m->main.elf == NULL) |
270 | 19.9k | { |
271 | 19.9k | m->main.elf = elf; |
272 | 19.9k | m->main.vaddr = vaddr; |
273 | 19.9k | m->main.address_sync = address_sync; |
274 | 19.9k | m->main_bias = bias; |
275 | 19.9k | m->e_type = e_type; |
276 | 19.9k | } |
277 | 656 | else |
278 | 656 | { |
279 | 656 | if (m->main_bias != bias |
280 | 656 | || m->main.vaddr != vaddr || m->main.address_sync != address_sync) |
281 | 46 | goto overlap; |
282 | 610 | elf_end (m->main.elf); |
283 | 610 | m->main.elf = elf; |
284 | 610 | } |
285 | 20.5k | } |
286 | 20.5k | return m; |
287 | 20.5k | } |
288 | | |
289 | | NEW_VERSION (dwfl_report_elf, ELFUTILS_0.156) |
290 | | Dwfl_Module * |
291 | | dwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd, |
292 | | GElf_Addr base, bool add_p_vaddr) |
293 | 0 | { |
294 | 0 | bool closefd = false; |
295 | 0 | if (fd < 0) |
296 | 0 | { |
297 | 0 | closefd = true; |
298 | 0 | fd = open (file_name, O_RDONLY); |
299 | 0 | if (fd < 0) |
300 | 0 | { |
301 | 0 | __libdwfl_seterrno (DWFL_E_ERRNO); |
302 | 0 | return NULL; |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | 0 | Elf *elf; |
307 | 0 | Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, false); |
308 | 0 | if (error != DWFL_E_NOERROR) |
309 | 0 | { |
310 | 0 | __libdwfl_seterrno (error); |
311 | 0 | return NULL; |
312 | 0 | } |
313 | | |
314 | 0 | Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name, |
315 | 0 | fd, elf, base, add_p_vaddr, true); |
316 | 0 | if (mod == NULL) |
317 | 0 | { |
318 | 0 | elf_end (elf); |
319 | 0 | if (closefd) |
320 | 0 | close (fd); |
321 | 0 | } |
322 | |
|
323 | 0 | return mod; |
324 | 0 | } |
325 | | NEW_INTDEF (dwfl_report_elf) |
326 | | |
327 | | #ifdef SYMBOL_VERSIONING |
328 | | Dwfl_Module * |
329 | | _compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name, |
330 | | const char *file_name, int fd, |
331 | | GElf_Addr base); |
332 | | COMPAT_VERSION_NEWPROTO (dwfl_report_elf, ELFUTILS_0.122, without_add_p_vaddr) |
333 | | |
334 | | Dwfl_Module * |
335 | | _compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name, |
336 | | const char *file_name, int fd, |
337 | | GElf_Addr base) |
338 | | { |
339 | | return dwfl_report_elf (dwfl, name, file_name, fd, base, true); |
340 | | } |
341 | | #endif |