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