blob: c0e7c46d7769c6f78ecad46a8e94f17071645696 [file] [log] [blame]
Ben Chengcc6695e2012-03-07 23:04:02 -08001/* Reconstruct an ELF file by reading the segments out of remote memory.
2 Copyright (C) 2005, 2006, 2007 Red Hat, Inc.
3 This file is part of Red Hat elfutils.
4
5 Red Hat elfutils is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by the
7 Free Software Foundation; version 2 of the License.
8
9 Red Hat elfutils is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with Red Hat elfutils; if not, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
17
18 In addition, as a special exception, Red Hat, Inc. gives You the
19 additional right to link the code of Red Hat elfutils with code licensed
20 under any Open Source Initiative certified open source license
21 (http://www.opensource.org/licenses/index.php) which requires the
22 distribution of source code with any binary distribution and to
23 distribute linked combinations of the two. Non-GPL Code permitted under
24 this exception must only link to the code of Red Hat elfutils through
25 those well defined interfaces identified in the file named EXCEPTION
26 found in the source code files (the "Approved Interfaces"). The files
27 of Non-GPL Code may instantiate templates or use macros or inline
28 functions from the Approved Interfaces without causing the resulting
29 work to be covered by the GNU General Public License. Only Red Hat,
30 Inc. may make changes or additions to the list of Approved Interfaces.
31 Red Hat's grant of this exception is conditioned upon your not adding
32 any new exceptions. If you wish to add a new Approved Interface or
33 exception, please contact Red Hat. You must obey the GNU General Public
34 License in all respects for all of the Red Hat elfutils code and other
35 code used in conjunction with Red Hat elfutils except the Non-GPL Code
36 covered by this exception. If you modify this file, you may extend this
37 exception to your version of the file, but you are not obligated to do
38 so. If you do not wish to provide this exception without modification,
39 you must delete this exception statement from your version and license
40 this file solely under the GPL without exception.
41
42 Red Hat elfutils is an included package of the Open Invention Network.
43 An included package of the Open Invention Network is a package for which
44 Open Invention Network licensees cross-license their patents. No patent
45 license is granted, either expressly or impliedly, by designation as an
46 included package. Should you wish to participate in the Open Invention
47 Network licensing program, please visit www.openinventionnetwork.com
48 <http://www.openinventionnetwork.com>. */
49
50#include <config.h>
51#include "../libelf/libelfP.h"
52#undef _
53
54#include "libdwflP.h"
55
56#include <gelf.h>
57#include <sys/types.h>
58#include <stdbool.h>
59#include <stdlib.h>
60#include <string.h>
61
62/* Reconstruct an ELF file by reading the segments out of remote memory
63 based on the ELF file header at EHDR_VMA and the ELF program headers it
64 points to. If not null, *LOADBASEP is filled in with the difference
65 between the addresses from which the segments were read, and the
66 addresses the file headers put them at.
67
68 The function READ_MEMORY is called to copy at least MINREAD and at most
69 MAXREAD bytes from the remote memory at target address ADDRESS into the
70 local buffer at DATA; it should return -1 for errors (with code in
71 `errno'), 0 if it failed to read at least MINREAD bytes due to EOF, or
72 the number of bytes read if >= MINREAD. ARG is passed through. */
73
74Elf *
75elf_from_remote_memory (GElf_Addr ehdr_vma,
76 GElf_Addr *loadbasep,
77 ssize_t (*read_memory) (void *arg, void *data,
78 GElf_Addr address,
79 size_t minread,
80 size_t maxread),
81 void *arg)
82{
83 /* First read in the file header and check its sanity. */
84
85 const size_t initial_bufsize = 256;
86 unsigned char *buffer = malloc (initial_bufsize);
87 if (buffer == NULL)
88 {
89 no_memory:
90 __libdwfl_seterrno (DWFL_E_NOMEM);
91 return NULL;
92 }
93
94 ssize_t nread = (*read_memory) (arg, buffer, ehdr_vma,
95 sizeof (Elf32_Ehdr), initial_bufsize);
96 if (nread <= 0)
97 {
98 read_error:
99 free (buffer);
100 __libdwfl_seterrno (nread < 0 ? DWFL_E_ERRNO : DWFL_E_TRUNCATED);
101 return NULL;
102 }
103
104 if (memcmp (buffer, ELFMAG, SELFMAG) != 0)
105 {
106 bad_elf:
107 __libdwfl_seterrno (DWFL_E_BADELF);
108 return NULL;
109 }
110
111 /* Extract the information we need from the file header. */
112
113 union
114 {
115 Elf32_Ehdr e32;
116 Elf64_Ehdr e64;
117 } ehdr;
118 Elf_Data xlatefrom =
119 {
120 .d_type = ELF_T_EHDR,
121 .d_buf = buffer,
122 .d_version = EV_CURRENT,
123 };
124 Elf_Data xlateto =
125 {
126 .d_type = ELF_T_EHDR,
127 .d_buf = &ehdr,
128 .d_size = sizeof ehdr,
129 .d_version = EV_CURRENT,
130 };
131
132 GElf_Off phoff;
133 uint_fast16_t phnum;
134 uint_fast16_t phentsize;
135 GElf_Off shdrs_end;
136
137 switch (buffer[EI_CLASS])
138 {
139 case ELFCLASS32:
140 xlatefrom.d_size = sizeof (Elf32_Ehdr);
141 if (elf32_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL)
142 {
143 libelf_error:
144 __libdwfl_seterrno (DWFL_E_LIBELF);
145 return NULL;
146 }
147 phoff = ehdr.e32.e_phoff;
148 phnum = ehdr.e32.e_phnum;
149 phentsize = ehdr.e32.e_phentsize;
150 if (phentsize != sizeof (Elf32_Phdr) || phnum == 0)
151 goto bad_elf;
152 shdrs_end = ehdr.e32.e_shoff + ehdr.e32.e_shnum * ehdr.e32.e_shentsize;
153 break;
154
155 case ELFCLASS64:
156 xlatefrom.d_size = sizeof (Elf64_Ehdr);
157 if (elf64_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL)
158 goto libelf_error;
159 phoff = ehdr.e64.e_phoff;
160 phnum = ehdr.e64.e_phnum;
161 phentsize = ehdr.e64.e_phentsize;
162 if (phentsize != sizeof (Elf64_Phdr) || phnum == 0)
163 goto bad_elf;
164 shdrs_end = ehdr.e64.e_shoff + ehdr.e64.e_shnum * ehdr.e64.e_shentsize;
165 break;
166
167 default:
168 goto bad_elf;
169 }
170
171
172 /* The file header tells where to find the program headers.
173 These are what we use to actually choose what to read. */
174
175 xlatefrom.d_type = xlateto.d_type = ELF_T_PHDR;
176 xlatefrom.d_size = phnum * phentsize;
177
178 if ((size_t) nread >= phoff + phnum * phentsize)
179 /* We already have all the phdrs from the initial read. */
180 xlatefrom.d_buf = buffer + phoff;
181 else
182 {
183 /* Read in the program headers. */
184
185 if (initial_bufsize < phnum * phentsize)
186 {
187 unsigned char *newbuf = realloc (buffer, phnum * phentsize);
188 if (newbuf == NULL)
189 {
190 free (buffer);
191 goto no_memory;
192 }
193 buffer = newbuf;
194 }
195 nread = (*read_memory) (arg, buffer, ehdr_vma + phoff,
196 phnum * phentsize, phnum * phentsize);
197 if (nread <= 0)
198 goto read_error;
199
200 xlatefrom.d_buf = buffer;
201 }
202
203 union
204 {
205 Elf32_Phdr p32[phnum];
206 Elf64_Phdr p64[phnum];
207 } phdrs;
208
209 xlateto.d_buf = &phdrs;
210 xlateto.d_size = sizeof phdrs;
211
212 /* Scan for PT_LOAD segments to find the total size of the file image. */
213 size_t contents_size = 0;
214 GElf_Off segments_end = 0;
215 GElf_Addr loadbase = ehdr_vma;
216 bool found_base = false;
217 switch (ehdr.e32.e_ident[EI_CLASS])
218 {
219 inline void handle_segment (GElf_Addr vaddr, GElf_Off offset,
220 GElf_Xword filesz, GElf_Xword align)
221 {
222 GElf_Off segment_end = ((offset + filesz + align - 1) & -align);
223
224 if (segment_end > (GElf_Off) contents_size)
225 contents_size = segment_end;
226
227 if (!found_base && (offset & -align) == 0)
228 {
229 loadbase = ehdr_vma - (vaddr & -align);
230 found_base = true;
231 }
232
233 segments_end = offset + filesz;
234 }
235
236 case ELFCLASS32:
237 if (elf32_xlatetom (&xlateto, &xlatefrom,
238 ehdr.e32.e_ident[EI_DATA]) == NULL)
239 goto libelf_error;
240 for (uint_fast16_t i = 0; i < phnum; ++i)
241 if (phdrs.p32[i].p_type == PT_LOAD)
242 handle_segment (phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset,
243 phdrs.p32[i].p_filesz, phdrs.p32[i].p_align);
244 break;
245
246 case ELFCLASS64:
247 if (elf32_xlatetom (&xlateto, &xlatefrom,
248 ehdr.e32.e_ident[EI_DATA]) == NULL)
249 goto libelf_error;
250 for (uint_fast16_t i = 0; i < phnum; ++i)
251 if (phdrs.p32[i].p_type == PT_LOAD)
252 handle_segment (phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset,
253 phdrs.p64[i].p_filesz, phdrs.p64[i].p_align);
254 break;
255
256 default:
257 abort ();
258 break;
259 }
260
261 /* Trim the last segment so we don't bother with zeros in the last page
262 that are off the end of the file. However, if the extra bit in that
263 page includes the section headers, keep them. */
264 if ((GElf_Off) contents_size > segments_end
265 && (GElf_Off) contents_size >= shdrs_end)
266 {
267 contents_size = segments_end;
268 if ((GElf_Off) contents_size < shdrs_end)
269 contents_size = shdrs_end;
270 }
271 else
272 contents_size = segments_end;
273
274 free (buffer);
275
276 /* Now we know the size of the whole image we want read in. */
277 buffer = calloc (1, contents_size);
278 if (buffer == NULL)
279 goto no_memory;
280
281 switch (ehdr.e32.e_ident[EI_CLASS])
282 {
283 inline bool handle_segment (GElf_Addr vaddr, GElf_Off offset,
284 GElf_Xword filesz, GElf_Xword align)
285 {
286 GElf_Off start = offset & -align;
287 GElf_Off end = (offset + filesz + align - 1) & -align;
288 if (end > (GElf_Off) contents_size)
289 end = contents_size;
290 nread = (*read_memory) (arg, buffer + start,
291 (loadbase + vaddr) & -align,
292 end - start, end - start);
293 return nread <= 0;
294 }
295
296 case ELFCLASS32:
297 for (uint_fast16_t i = 0; i < phnum; ++i)
298 if (phdrs.p32[i].p_type == PT_LOAD)
299 if (handle_segment (phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset,
300 phdrs.p32[i].p_filesz, phdrs.p32[i].p_align))
301 goto read_error;
302
303 /* If the segments visible in memory didn't include the section
304 headers, then clear them from the file header. */
305 if (contents_size < shdrs_end)
306 {
307 ehdr.e32.e_shoff = 0;
308 ehdr.e32.e_shnum = 0;
309 ehdr.e32.e_shstrndx = 0;
310 }
311
312 /* This will normally have been in the first PT_LOAD segment. But it
313 conceivably could be missing, and we might have just changed it. */
314 xlatefrom.d_type = xlateto.d_type = ELF_T_EHDR;
315 xlatefrom.d_size = xlateto.d_size = sizeof ehdr.e32;
316 xlatefrom.d_buf = &ehdr.e32;
317 xlateto.d_buf = buffer;
318 if (elf32_xlatetof (&xlateto, &xlatefrom,
319 ehdr.e32.e_ident[EI_DATA]) == NULL)
320 goto libelf_error;
321 break;
322
323 case ELFCLASS64:
324 for (uint_fast16_t i = 0; i < phnum; ++i)
325 if (phdrs.p32[i].p_type == PT_LOAD)
326 if (handle_segment (phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset,
327 phdrs.p64[i].p_filesz, phdrs.p64[i].p_align))
328 goto read_error;
329
330 /* If the segments visible in memory didn't include the section
331 headers, then clear them from the file header. */
332 if (contents_size < shdrs_end)
333 {
334 ehdr.e64.e_shoff = 0;
335 ehdr.e64.e_shnum = 0;
336 ehdr.e64.e_shstrndx = 0;
337 }
338
339 /* This will normally have been in the first PT_LOAD segment. But it
340 conceivably could be missing, and we might have just changed it. */
341 xlatefrom.d_type = xlateto.d_type = ELF_T_EHDR;
342 xlatefrom.d_size = xlateto.d_size = sizeof ehdr.e64;
343 xlatefrom.d_buf = &ehdr.e64;
344 xlateto.d_buf = buffer;
345 if (elf64_xlatetof (&xlateto, &xlatefrom,
346 ehdr.e64.e_ident[EI_DATA]) == NULL)
347 goto libelf_error;
348 break;
349
350 default:
351 abort ();
352 break;
353 }
354
355 /* Now we have the image. Open libelf on it. */
356
357 Elf *elf = elf_memory ((char *) buffer, contents_size);
358 if (elf == NULL)
359 {
360 free (buffer);
361 goto libelf_error;
362 }
363
364 elf->flags |= ELF_F_MALLOCED;
365 if (loadbasep != NULL)
366 *loadbasep = loadbase;
367 return elf;
368}