| Ulrich Drepper | b08d5a8 | 2005-07-26 05:00:05 +0000 | [diff] [blame] | 1 | /* Reconstruct an ELF file by reading the segments out of remote memory. | 
|  | 2 | Copyright (C) 2005 Red Hat, Inc. | 
| Ulrich Drepper | 361df7d | 2006-04-04 21:38:57 +0000 | [diff] [blame^] | 3 | This file is part of Red Hat elfutils. | 
| Ulrich Drepper | b08d5a8 | 2005-07-26 05:00:05 +0000 | [diff] [blame] | 4 |  | 
| Ulrich Drepper | 361df7d | 2006-04-04 21:38:57 +0000 | [diff] [blame^] | 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. | 
| Ulrich Drepper | b08d5a8 | 2005-07-26 05:00:05 +0000 | [diff] [blame] | 8 |  | 
| Ulrich Drepper | 361df7d | 2006-04-04 21:38:57 +0000 | [diff] [blame^] | 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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>.  */ | 
| Ulrich Drepper | b08d5a8 | 2005-07-26 05:00:05 +0000 | [diff] [blame] | 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 |  | 
|  | 74 | Elf * | 
|  | 75 | elf_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 (elf32_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 | switch (ehdr.e32.e_ident[EI_CLASS]) | 
|  | 217 | { | 
|  | 218 | inline void handle_segment (GElf_Addr vaddr, GElf_Off offset, | 
|  | 219 | GElf_Xword filesz, GElf_Xword align) | 
|  | 220 | { | 
|  | 221 | GElf_Off segment_end = ((offset + filesz + align - 1) & -align); | 
|  | 222 |  | 
|  | 223 | if (segment_end > (GElf_Off) contents_size) | 
|  | 224 | contents_size = segment_end; | 
|  | 225 |  | 
|  | 226 | if ((offset & -align) == 0 && loadbase == ehdr_vma) | 
|  | 227 | loadbase = ehdr_vma - (vaddr & -align); | 
|  | 228 |  | 
|  | 229 | segments_end = offset + filesz; | 
|  | 230 | } | 
|  | 231 |  | 
|  | 232 | case ELFCLASS32: | 
|  | 233 | if (elf32_xlatetom (&xlateto, &xlatefrom, | 
|  | 234 | ehdr.e32.e_ident[EI_DATA]) == NULL) | 
|  | 235 | goto libelf_error; | 
|  | 236 | for (uint_fast16_t i = 0; i < phnum; ++i) | 
|  | 237 | if (phdrs.p32[i].p_type == PT_LOAD) | 
|  | 238 | handle_segment (phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset, | 
|  | 239 | phdrs.p32[i].p_filesz, phdrs.p32[i].p_align); | 
|  | 240 | break; | 
|  | 241 |  | 
|  | 242 | case ELFCLASS64: | 
|  | 243 | if (elf32_xlatetom (&xlateto, &xlatefrom, | 
|  | 244 | ehdr.e32.e_ident[EI_DATA]) == NULL) | 
|  | 245 | goto libelf_error; | 
|  | 246 | for (uint_fast16_t i = 0; i < phnum; ++i) | 
|  | 247 | if (phdrs.p32[i].p_type == PT_LOAD) | 
|  | 248 | handle_segment (phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset, | 
|  | 249 | phdrs.p64[i].p_filesz, phdrs.p64[i].p_align); | 
|  | 250 | break; | 
|  | 251 |  | 
|  | 252 | default: | 
|  | 253 | abort (); | 
|  | 254 | break; | 
|  | 255 | } | 
|  | 256 |  | 
|  | 257 | /* Trim the last segment so we don't bother with zeros in the last page | 
|  | 258 | that are off the end of the file.  However, if the extra bit in that | 
|  | 259 | page includes the section headers, keep them.  */ | 
|  | 260 | if ((GElf_Off) contents_size > segments_end | 
|  | 261 | && (GElf_Off) contents_size >= shdrs_end) | 
|  | 262 | { | 
|  | 263 | contents_size = segments_end; | 
|  | 264 | if ((GElf_Off) contents_size < shdrs_end) | 
|  | 265 | contents_size = shdrs_end; | 
|  | 266 | } | 
|  | 267 | else | 
|  | 268 | contents_size = segments_end; | 
|  | 269 |  | 
|  | 270 | free (buffer); | 
|  | 271 |  | 
|  | 272 | /* Now we know the size of the whole image we want read in.  */ | 
|  | 273 | buffer = calloc (1, contents_size); | 
|  | 274 | if (buffer == NULL) | 
|  | 275 | goto no_memory; | 
|  | 276 |  | 
|  | 277 | switch (ehdr.e32.e_ident[EI_CLASS]) | 
|  | 278 | { | 
|  | 279 | inline bool handle_segment (GElf_Addr vaddr, GElf_Off offset, | 
|  | 280 | GElf_Xword filesz, GElf_Xword align) | 
|  | 281 | { | 
|  | 282 | GElf_Off start = offset & -align; | 
|  | 283 | GElf_Off end = (offset + filesz + align - 1) & -align; | 
|  | 284 | if (end > (GElf_Off) contents_size) | 
|  | 285 | end = contents_size; | 
|  | 286 | nread = (*read_memory) (arg, buffer + start, | 
|  | 287 | (loadbase + vaddr) & -align, | 
|  | 288 | end - start, end - start); | 
|  | 289 | return nread <= 0; | 
|  | 290 | } | 
|  | 291 |  | 
|  | 292 | case ELFCLASS32: | 
|  | 293 | for (uint_fast16_t i = 0; i < phnum; ++i) | 
|  | 294 | if (phdrs.p32[i].p_type == PT_LOAD) | 
|  | 295 | if (handle_segment (phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset, | 
|  | 296 | phdrs.p32[i].p_filesz, phdrs.p32[i].p_align)) | 
|  | 297 | goto read_error; | 
|  | 298 |  | 
|  | 299 | /* If the segments visible in memory didn't include the section | 
|  | 300 | headers, then clear them from the file header.  */ | 
|  | 301 | if (contents_size < shdrs_end) | 
|  | 302 | { | 
|  | 303 | ehdr.e32.e_shoff = 0; | 
|  | 304 | ehdr.e32.e_shnum = 0; | 
|  | 305 | ehdr.e32.e_shstrndx = 0; | 
|  | 306 | } | 
|  | 307 |  | 
|  | 308 | /* This will normally have been in the first PT_LOAD segment.  But it | 
|  | 309 | conceivably could be missing, and we might have just changed it.  */ | 
|  | 310 | xlatefrom.d_type = xlateto.d_type = ELF_T_EHDR; | 
|  | 311 | xlatefrom.d_size = xlateto.d_size = sizeof ehdr.e32; | 
|  | 312 | xlatefrom.d_buf = &ehdr.e32; | 
|  | 313 | xlateto.d_buf = buffer; | 
|  | 314 | if (elf32_xlatetof (&xlateto, &xlatefrom, | 
|  | 315 | ehdr.e32.e_ident[EI_DATA]) == NULL) | 
|  | 316 | goto libelf_error; | 
|  | 317 | break; | 
|  | 318 |  | 
|  | 319 | case ELFCLASS64: | 
|  | 320 | for (uint_fast16_t i = 0; i < phnum; ++i) | 
|  | 321 | if (phdrs.p32[i].p_type == PT_LOAD) | 
|  | 322 | if (handle_segment (phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset, | 
|  | 323 | phdrs.p64[i].p_filesz, phdrs.p64[i].p_align)) | 
|  | 324 | goto read_error; | 
|  | 325 |  | 
|  | 326 | /* If the segments visible in memory didn't include the section | 
|  | 327 | headers, then clear them from the file header.  */ | 
|  | 328 | if (contents_size < shdrs_end) | 
|  | 329 | { | 
|  | 330 | ehdr.e64.e_shoff = 0; | 
|  | 331 | ehdr.e64.e_shnum = 0; | 
|  | 332 | ehdr.e64.e_shstrndx = 0; | 
|  | 333 | } | 
|  | 334 |  | 
|  | 335 | /* This will normally have been in the first PT_LOAD segment.  But it | 
|  | 336 | conceivably could be missing, and we might have just changed it.  */ | 
|  | 337 | xlatefrom.d_type = xlateto.d_type = ELF_T_EHDR; | 
|  | 338 | xlatefrom.d_size = xlateto.d_size = sizeof ehdr.e64; | 
|  | 339 | xlatefrom.d_buf = &ehdr.e64; | 
|  | 340 | xlateto.d_buf = buffer; | 
|  | 341 | if (elf32_xlatetof (&xlateto, &xlatefrom, | 
|  | 342 | ehdr.e64.e_ident[EI_DATA]) == NULL) | 
|  | 343 | goto libelf_error; | 
|  | 344 | break; | 
|  | 345 |  | 
|  | 346 | default: | 
|  | 347 | abort (); | 
|  | 348 | break; | 
|  | 349 | } | 
|  | 350 |  | 
|  | 351 | /* Now we have the image.  Open libelf on it.  */ | 
|  | 352 |  | 
|  | 353 | Elf *elf = elf_memory ((char *) buffer, contents_size); | 
|  | 354 | if (elf == NULL) | 
|  | 355 | { | 
|  | 356 | free (buffer); | 
|  | 357 | return NULL; | 
|  | 358 | } | 
|  | 359 |  | 
|  | 360 | elf->flags |= ELF_F_MALLOCED; | 
|  | 361 | if (loadbasep != NULL) | 
|  | 362 | *loadbasep = loadbase; | 
|  | 363 | return elf; | 
|  | 364 | } |