Mercurial > hg > xemacs-beta
comparison dynodump/dynodump.c @ 153:25f70ba0133c r20-3b3
Import from CVS: tag r20-3b3
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:38:25 +0200 |
parents | |
children | 8eaf7971accc |
comparison
equal
deleted
inserted
replaced
152:4c132ee2d62b | 153:25f70ba0133c |
---|---|
1 /* | |
2 * Copyright (c) 1995 by Sun Microsystems, Inc. | |
3 * All rights reserved. | |
4 * | |
5 * This source code is a product of Sun Microsystems, Inc. and is provided | |
6 * for unrestricted use provided that this legend is included on all tape | |
7 * media and as a part of the software program in whole or part. Users | |
8 * may copy or modify this source code without charge, but are not authorized | |
9 * to license or distribute it to anyone else except as part of a product or | |
10 * program developed by the user. | |
11 * | |
12 * THIS PROGRAM CONTAINS SOURCE CODE COPYRIGHTED BY SUN MICROSYSTEMS, INC. | |
13 * SUN MICROSYSTEMS, INC., MAKES NO REPRESENTATIONS ABOUT THE SUITABLITY | |
14 * OF SUCH SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT | |
15 * EXPRESS OR IMPLIED WARRANTY OF ANY KIND. SUN MICROSYSTEMS, INC. DISCLAIMS | |
16 * ALL WARRANTIES WITH REGARD TO SUCH SOURCE CODE, INCLUDING ALL IMPLIED | |
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN | |
18 * NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT, | |
19 * INCIDENTAL, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING | |
20 * FROM USE OF SUCH SOURCE CODE, REGARDLESS OF THE THEORY OF LIABILITY. | |
21 * | |
22 * This source code is provided with no support and without any obligation on | |
23 * the part of Sun Microsystems, Inc. to assist in its use, correction, | |
24 * modification or enhancement. | |
25 * | |
26 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE | |
27 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS | |
28 * SOURCE CODE OR ANY PART THEREOF. | |
29 * | |
30 * Sun Microsystems, Inc. | |
31 * 2550 Garcia Avenue | |
32 * Mountain View, California 94043 | |
33 */ | |
34 | |
35 /* | |
36 * dynodump(3x) dumps a running executable into a specified ELF file. The new | |
37 * file consists of the memory contents of the original file together with any | |
38 * heap. This heap is assigned to a new `.heap' section within the new file. | |
39 * | |
40 * The new file may be re-executed, and will contain any data modifications | |
41 * made to the original image up until the time dynodump(3x) was called. | |
42 * | |
43 * The original image may have undergone relocations (performed by ld.so.1) | |
44 * prior to control being transferred to the image. These relocations will | |
45 * reside as the data copied from the image. To prevent subsequent executions | |
46 * of the new image from undergoing the same relocations, any relocation entries | |
47 * (besides copy or jump slot relocations) are nulled out. Note that copy | |
48 * relocations such as required for __iob must be reinitialized each time the | |
49 * process starts, so it is not sufficient to simply null out the .dynamic | |
50 * sections relocation information. The effect of this is that if the new | |
51 * image was bound to definitions in any shared object dependencies, then these | |
52 * dependencies *must* reside in the same location as when dynodump(3x) was | |
53 * called. Any changes to the shared object dependencies of the new image, or | |
54 * uses of such things as LD_PRELOAD, may result in the bindings encoded in the | |
55 * image becoming invalid. | |
56 * | |
57 * The following flags modify the data of the image created: | |
58 * | |
59 * RTLD_SAVREL save the original relocation data. Under this option any | |
60 * relocation offset is reset to contain the same data as was | |
61 * found in the images original file. | |
62 * | |
63 * This option allows relocation information to be retained in the | |
64 * new image so that it may be re-executed when the new image is | |
65 * run. This allows far greater flexibility as the new image can | |
66 * now take advantage of new shared objects. | |
67 * | |
68 * Note. under this mechanism, any data item that undergoes | |
69 * relocation and is then further modified during the execution of | |
70 * the image before dynodump(3x) is called will lose the | |
71 * modification that occured during the applications execution. | |
72 * | |
73 * N.B. The above commentary is not quite correct in the flags have been hardwired | |
74 * to RTLD_SAVREL. | |
75 */ | |
76 #pragma ident "@(#) $Id: dynodump.c,v 1.3 1997/05/29 04:22:30 steve Exp $ - SMI" | |
77 | |
78 #include <sys/param.h> | |
79 #include <sys/procfs.h> | |
80 #include <fcntl.h> | |
81 #include <stdio.h> | |
82 #include <libelf.h> | |
83 #include <link.h> | |
84 #include <stdlib.h> | |
85 #include <string.h> | |
86 #include <unistd.h> | |
87 #include <errno.h> | |
88 #include <malloc.h> | |
89 #include "machdep.h" | |
90 #include "_dynodump.h" | |
91 | |
92 /* | |
93 * Generic elf error message generator | |
94 */ | |
95 static int | |
96 elferr(const char * str) | |
97 { | |
98 fprintf(stderr, "%s: %s\n", str, elf_errmsg(elf_errno())); | |
99 return (1); | |
100 } | |
101 | |
102 int dynodump (const char * file); | |
103 int | |
104 dynodump(const char * file) | |
105 { | |
106 Elf *ielf, *oelf; | |
107 Ehdr *iehdr, *oehdr; | |
108 Phdr *iphdr, *ophdr, *data_phdr = 0; | |
109 Cache *icache, *ocache, *_icache, *_ocache; | |
110 Cache *data_cache = 0, *shstr_cache = 0; | |
111 Cache *heap_cache = 0; | |
112 Word heap_sz = 0; | |
113 Elf_Scn *scn; | |
114 Shdr *shdr; | |
115 Elf_Data *data, rundata; | |
116 Half ndx, _ndx; | |
117 int fd, _fd; | |
118 Addr edata, _addr; | |
119 char *istrs, *ostrs, *_ostrs, proc[16]; | |
120 const char heap[] = ".heap"; | |
121 prstatus_t pstat; | |
122 | |
123 /* make a call to the processor specific un-init stuff */ | |
124 dynodump_uninit(); | |
125 | |
126 /* | |
127 * Obtain a file descriptor for this process, | |
128 * for the executable and get a prstatus_t | |
129 * structure. | |
130 */ | |
131 sprintf(proc, "/proc/%ld", getpid()); | |
132 if (((_fd = open(proc, O_RDONLY, 0)) == -1) || | |
133 ((fd = ioctl(_fd, PIOCOPENM, (void *)0)) == -1) || | |
134 (ioctl(_fd, PIOCSTATUS, &pstat) == -1)) { | |
135 fprintf(stderr, "/proc: initialization error: %s\n", | |
136 strerror(errno)); | |
137 close(_fd); | |
138 return (1); | |
139 } | |
140 close(_fd); | |
141 | |
142 /* | |
143 * Initialize with the ELF library and make sure this is an executable | |
144 * ELF file we're dealing with. | |
145 */ | |
146 elf_version(EV_CURRENT); | |
147 if ((ielf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { | |
148 close(fd); | |
149 return (elferr("elf_begin")); | |
150 } | |
151 close(fd); | |
152 | |
153 if ((elf_kind(ielf) != ELF_K_ELF) || | |
154 ((iehdr = elf_getehdr(ielf)) == NULL) || | |
155 (iehdr->e_type != ET_EXEC)) { | |
156 fprintf(stderr, "image is not an ELF executable\n"); | |
157 elf_end(ielf); | |
158 return (1); | |
159 } | |
160 /* | |
161 * Elf_elf_header(iehdr); | |
162 */ | |
163 | |
164 /* | |
165 * Create the new output file. | |
166 */ | |
167 if ((fd = open(file, O_RDWR | O_CREAT | O_TRUNC, 0777)) == -1) { | |
168 fprintf(stderr, "%s: open failed: %s\n", file, | |
169 strerror(errno)); | |
170 elf_end(ielf); | |
171 return (1); | |
172 } | |
173 if ((oelf = elf_begin(fd, ELF_C_WRITE, NULL)) == NULL) { | |
174 elf_end(ielf); | |
175 close(fd); | |
176 return (elferr("elf_begin")); | |
177 } | |
178 | |
179 /* | |
180 * Obtain the input program headers. Remember the data segments | |
181 * program header entry as this will be updated later to reflect the | |
182 * new .heap sections size. | |
183 */ | |
184 if ((iphdr = elf_getphdr(ielf)) == NULL) | |
185 return (elferr("elf_getphdr")); | |
186 | |
187 for (ndx = 0, ophdr = iphdr; ndx != iehdr->e_phnum; ndx++, ophdr++) { | |
188 /* | |
189 * Save the program header that contains the NOBITS section, or | |
190 * the last loadable program header if no NOBITS exists. | |
191 * A NOBITS section translates to a memory size requirement that | |
192 * is greater than the file data it is mapped from. | |
193 */ | |
194 if (ophdr->p_type == PT_LOAD) { | |
195 if (ophdr->p_filesz != ophdr->p_memsz) | |
196 data_phdr = ophdr; | |
197 else if (data_phdr) { | |
198 if (data_phdr->p_vaddr < ophdr->p_vaddr) | |
199 data_phdr = ophdr; | |
200 } else | |
201 data_phdr = ophdr; | |
202 } | |
203 } | |
204 if (data_phdr == 0) { | |
205 fprintf(stderr, "no data segment found!\n"); | |
206 return (0); | |
207 } | |
208 | |
209 /* | |
210 * Obtain the input files section header string table. | |
211 */ | |
212 if ((scn = elf_getscn(ielf, iehdr->e_shstrndx)) == NULL) | |
213 return (elferr("elf_getscn")); | |
214 if ((data = elf_getdata(scn, NULL)) == NULL) | |
215 return (elferr("elf_getdata")); | |
216 istrs = data->d_buf; | |
217 | |
218 /* | |
219 * Construct a cache to maintain the input files section information. | |
220 */ | |
221 if ((icache = (Cache *) malloc(iehdr->e_shnum * sizeof (Cache))) == 0) { | |
222 fprintf(stderr, "malloc failed: %s\n", strerror(errno)); | |
223 return (1); | |
224 } | |
225 _icache = icache; | |
226 _icache++; | |
227 | |
228 /* | |
229 * Traverse each section from the input file. | |
230 */ | |
231 for (ndx = 1, scn = 0; | |
232 (_icache->c_scn = elf_nextscn(ielf, scn)); | |
233 ndx++, scn = _icache->c_scn, _icache++) { | |
234 | |
235 if ((_icache->c_shdr = shdr = elf_getshdr(_icache->c_scn)) == NULL) | |
236 return (elferr("elf_getshdr")); | |
237 | |
238 if ((_icache->c_data = elf_getdata(_icache->c_scn, NULL)) == NULL) | |
239 return (elferr("elf_getdata")); | |
240 | |
241 _icache->c_name = istrs + (size_t)(shdr->sh_name); | |
242 | |
243 /* | |
244 * For each section that has a virtual address reestablish the | |
245 * data buffer to point to the memory image. | |
246 * | |
247 * if (shdr->sh_addr) | |
248 * _icache->c_data->d_buf = (void *)shdr->sh_addr; | |
249 */ | |
250 | |
251 /* | |
252 * Remember the last section of the data segment, the new .heap | |
253 * section will be added after this section. | |
254 * If we already have one, then set data_cache to the previous | |
255 * section and set heap_cache to this one. | |
256 */ | |
257 if ((shdr->sh_addr + shdr->sh_size) | |
258 == (data_phdr->p_vaddr + data_phdr->p_memsz)) { | |
259 if (strcmp(_icache->c_name, heap) == 0) { | |
260 #ifdef DEBUG | |
261 printf("Found a previous .heap section\n"); | |
262 #endif | |
263 data_cache = _icache - 1; | |
264 heap_cache = _icache; | |
265 heap_sz = shdr->sh_size; | |
266 } else { | |
267 data_cache = _icache; | |
268 } | |
269 } | |
270 | |
271 /* | |
272 * Remember the section header string table as this will be | |
273 * rewritten with the new .heap name. | |
274 */ | |
275 if ((shdr->sh_type == SHT_STRTAB) && | |
276 ((strcmp(_icache->c_name, ".shstrtab")) == 0)) | |
277 shstr_cache = _icache; | |
278 } | |
279 if (data_cache == 0) { | |
280 fprintf(stderr, "final data section not found!\n"); | |
281 return (0); | |
282 } | |
283 | |
284 /* | |
285 * Determine the new .heap section to create. | |
286 */ | |
287 rundata.d_buf = (void *)(data_cache->c_shdr->sh_addr + | |
288 data_cache->c_shdr->sh_size); | |
289 rundata.d_size = (int)sbrk(0) - (int)rundata.d_buf; | |
290 rundata.d_type = ELF_T_BYTE; | |
291 rundata.d_off = 0; | |
292 rundata.d_align = 1; | |
293 rundata.d_version = EV_CURRENT; | |
294 | |
295 /* | |
296 * From the new data buffer determine the new value for _end and _edata. | |
297 * This will also be used to update the data segment program header. | |
298 * | |
299 * If we had a .heap section, then its size is part of the program | |
300 * headers notion of data size. Because we're only going to output one | |
301 * heap section (ignoring the one in the running binary) we need to | |
302 * subract the size of that which we're ignoring. | |
303 */ | |
304 if (heap_cache) { | |
305 edata = S_ROUND((data_phdr->p_vaddr | |
306 + data_phdr->p_memsz | |
307 - heap_sz), rundata.d_align) + rundata.d_size; | |
308 } else { | |
309 edata = S_ROUND((data_phdr->p_vaddr + data_phdr->p_memsz), | |
310 rundata.d_align) + rundata.d_size; | |
311 } | |
312 | |
313 /* | |
314 * We're now ready to construct the new elf image. | |
315 * | |
316 * Obtain a new elf header and initialize it with any basic information | |
317 * that isn't calculated as part of elf_update(). Bump the section | |
318 * header string table index to account for the .heap section we'll be | |
319 * adding. | |
320 */ | |
321 if ((oehdr = elf_newehdr(oelf)) == NULL) | |
322 return (elferr("elf_newehdr")); | |
323 | |
324 oehdr->e_entry = iehdr->e_entry; | |
325 oehdr->e_machine = iehdr->e_machine; | |
326 oehdr->e_type = iehdr->e_type; | |
327 oehdr->e_flags = iehdr->e_flags; | |
328 /* | |
329 * If we already have a heap section, we don't need any adjustment | |
330 */ | |
331 if (heap_cache) | |
332 oehdr->e_shstrndx = iehdr->e_shstrndx; | |
333 else | |
334 oehdr->e_shstrndx = iehdr->e_shstrndx + 1; | |
335 | |
336 #ifdef DEBUG | |
337 printf("iehdr->e_flags = %x\n", iehdr->e_flags); | |
338 printf("iehdr->e_entry = %x\n", iehdr->e_entry); | |
339 printf("iehdr->e_shstrndx= %d\n", iehdr->e_shstrndx); | |
340 printf("iehdr->e_machine = %d\n", iehdr->e_machine); | |
341 printf("iehdr->e_type = 0x%x\n", iehdr->e_type); | |
342 printf("oehdr->e_machine = %d\n", oehdr->e_machine); | |
343 printf("oehdr->e_type = 0x%x\n", oehdr->e_type); | |
344 #endif | |
345 | |
346 /* | |
347 * Obtain a new set of program headers. Initialize these with the same | |
348 * information as the input program headers and update the data segment | |
349 * to reflect the new .heap section. | |
350 */ | |
351 if ((ophdr = elf_newphdr(oelf, iehdr->e_phnum)) == NULL) | |
352 return (elferr("elf_newphdr")); | |
353 | |
354 for (ndx = 0; ndx != iehdr->e_phnum; ndx++, iphdr++, ophdr++) { | |
355 *ophdr = *iphdr; | |
356 if (data_phdr == iphdr) | |
357 ophdr->p_filesz = ophdr->p_memsz = edata - ophdr->p_vaddr; | |
358 } | |
359 | |
360 /* | |
361 * Obtain a new set of sections. | |
362 */ | |
363 _icache = icache; | |
364 _icache++; | |
365 for (ndx = 1; ndx != iehdr->e_shnum; ndx++, _icache++) { | |
366 /* | |
367 * Skip the heap section of the running executable | |
368 */ | |
369 if (_icache == heap_cache) | |
370 continue; | |
371 /* | |
372 * Create a matching section header in the output file. | |
373 */ | |
374 if ((scn = elf_newscn(oelf)) == NULL) | |
375 return (elferr("elf_newscn")); | |
376 if ((shdr = elf_getshdr(scn)) == NULL) | |
377 return (elferr("elf_getshdr")); | |
378 *shdr = *_icache->c_shdr; | |
379 | |
380 /* | |
381 * Create a matching data buffer for this section. | |
382 */ | |
383 if ((data = elf_newdata(scn)) == NULL) | |
384 return (elferr("elf_newdata")); | |
385 *data = *_icache->c_data; | |
386 | |
387 /* | |
388 * For each section that has a virtual address reestablish the | |
389 * data buffer to point to the memory image. Note, we skip | |
390 * the plt section. | |
391 */ | |
392 if ((shdr->sh_addr) && (!((shdr->sh_type == SHT_PROGBITS) | |
393 && (strcmp(_icache->c_name, ".plt") == 0)))) | |
394 data->d_buf = (void *)shdr->sh_addr; | |
395 | |
396 /* | |
397 * Update any NOBITS section to indicate that it now contains | |
398 * data. | |
399 */ | |
400 if (shdr->sh_type == SHT_NOBITS) | |
401 shdr->sh_type = SHT_PROGBITS; | |
402 | |
403 /* | |
404 * Add the new .heap section after the last section of the | |
405 * present data segment. If we had a heap section, then | |
406 * this is the section preceding it. | |
407 */ | |
408 if (data_cache == _icache) { | |
409 if ((scn = elf_newscn(oelf)) == NULL) | |
410 return (elferr("elf_newscn")); | |
411 if ((shdr = elf_getshdr(scn)) == NULL) | |
412 return (elferr("elf_getshdr")); | |
413 shdr->sh_type = SHT_PROGBITS; | |
414 shdr->sh_flags = SHF_ALLOC | SHF_WRITE; | |
415 | |
416 if ((data = elf_newdata(scn)) == NULL) | |
417 return (elferr("elf_newdata")); | |
418 *data = rundata; | |
419 } | |
420 | |
421 /* | |
422 * Update the section header string table size to reflect the | |
423 * new section name (only if we didn't already have a heap). | |
424 */ | |
425 if (!heap_cache) { | |
426 if (shstr_cache && (shstr_cache == _icache)) { | |
427 data->d_size += sizeof (heap); | |
428 } | |
429 } | |
430 } | |
431 | |
432 /* | |
433 * Write out the new image, and obtain a new elf descriptor that will | |
434 * allow us to write to the new image. | |
435 */ | |
436 if (elf_update(oelf, ELF_C_WRITE) == -1) | |
437 return (elferr("elf_update")); | |
438 elf_end(oelf); | |
439 if ((oelf = elf_begin(fd, ELF_C_RDWR, NULL)) == NULL) | |
440 return (elferr("elf_begin")); | |
441 if ((oehdr = elf_getehdr(oelf)) == NULL) | |
442 return (elferr("elf_getehdr")); | |
443 | |
444 /* | |
445 * Obtain the output files section header string table. | |
446 */ | |
447 if ((scn = elf_getscn(oelf, oehdr->e_shstrndx)) == NULL) | |
448 return (elferr("elf_getscn")); | |
449 if ((data = elf_getdata(scn, NULL)) == NULL) | |
450 return (elferr("elf_getdata")); | |
451 ostrs = _ostrs = data->d_buf; | |
452 *_ostrs++ = '\0'; | |
453 | |
454 /* | |
455 * Construct a cache to maintain the output files section information. | |
456 */ | |
457 if ((ocache = (Cache *)malloc(oehdr->e_shnum * sizeof (Cache))) == 0) { | |
458 fprintf(stderr, "malloc failed: %s\n", strerror(errno)); | |
459 return (1); | |
460 } | |
461 _ocache = ocache; | |
462 _ocache++; | |
463 _icache = icache; | |
464 _icache++; | |
465 | |
466 /* | |
467 * Traverse each section from the input file rebuilding the section | |
468 * header string table as we go. | |
469 */ | |
470 _ndx = _addr = 0; | |
471 for (ndx = 1, scn = 0; | |
472 (_ocache->c_scn = elf_nextscn(oelf, scn)); | |
473 ndx++, scn = _ocache->c_scn, _ocache++, _icache++) { | |
474 | |
475 const char *strs; | |
476 | |
477 if (_icache == heap_cache) { | |
478 #ifdef DEBUG | |
479 printf("ignoring .heap section in input\n"); | |
480 #endif | |
481 _icache++; | |
482 } | |
483 | |
484 if ((_ocache->c_shdr = shdr = | |
485 elf_getshdr(_ocache->c_scn)) == NULL) | |
486 return (elferr("elf_getshdr")); | |
487 if ((_ocache->c_data = | |
488 elf_getdata(_ocache->c_scn, NULL)) == NULL) | |
489 return (elferr("elf_getdata")); | |
490 | |
491 /* | |
492 * If were inserting the new .heap section, insert the new | |
493 * section name and initialize it's virtual address. | |
494 */ | |
495 if (_addr) { | |
496 strs = heap; | |
497 shdr->sh_addr = S_ROUND(_addr, shdr->sh_addralign); | |
498 _addr = 0; | |
499 } else { | |
500 strs = istrs + (size_t)(_icache->c_shdr->sh_name); | |
501 } | |
502 | |
503 strcpy(_ostrs, strs); | |
504 shdr->sh_name = _ostrs - ostrs; | |
505 _ocache->c_name = _ostrs; | |
506 _ostrs += strlen(strs) + 1; | |
507 | |
508 /* | |
509 * If we've inserted a new section any later section may need | |
510 * their sh_link fields updated. | |
511 * If we already had a heap section, then this is not required. | |
512 */ | |
513 if (!heap_cache) { | |
514 if (_ndx) { | |
515 if (_ocache->c_shdr->sh_link >= _ndx) | |
516 _ocache->c_shdr->sh_link++; | |
517 } | |
518 } | |
519 | |
520 /* | |
521 * If this is the last section of the original data segment | |
522 * determine sufficient information to initialize the new .heap | |
523 * section which will be obtained next. | |
524 */ | |
525 if (data_cache == _icache) { | |
526 _ndx = ndx + 1; | |
527 _addr = shdr->sh_addr + shdr->sh_size; | |
528 _icache--; | |
529 data_cache = 0; | |
530 } | |
531 } | |
532 | |
533 /* | |
534 * Now that we have a complete description of the new image update any | |
535 * sections that are required. | |
536 * | |
537 * o update the value of _edata and _end. | |
538 * | |
539 * o reset any relocation entries if necessary. | |
540 */ | |
541 _ocache = &ocache[1]; | |
542 _icache = &icache[1]; | |
543 for (ndx = 1; ndx < oehdr->e_shnum; ndx++, _ocache++, _icache++) { | |
544 if ((_ocache->c_shdr->sh_type == SHT_SYMTAB) || | |
545 (_ocache->c_shdr->sh_type == SHT_DYNSYM)) | |
546 update_sym(ocache, _ocache, edata); | |
547 | |
548 if (_ocache->c_shdr->sh_type == M_REL_SHT_TYPE) | |
549 update_reloc(ocache, _ocache, icache, _icache, oehdr->e_shnum); | |
550 } | |
551 | |
552 if (elf_update(oelf, ELF_C_WRITE) == -1) | |
553 return (elferr("elf_update")); | |
554 | |
555 elf_end(oelf); | |
556 elf_end(ielf); | |
557 return (0); | |
558 } |