This source file includes following definitions.
- report_error
- ATTRIBUTE_FORMAT_PRINTF
- unexec
- make_hdr
- copy_text_and_data
- write_segment
- copy_sym
- adjust_lnnoptrs
- unrelocate_symbols
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 #include <config.h>
44 #include "unexec.h"
45 #include "lisp.h"
46
47 #define PERROR(file) report_error (file, new)
48 #include <a.out.h>
49
50
51
52 #include "getpagesize.h"
53
54 #include <sys/types.h>
55 #include <inttypes.h>
56 #include <stdarg.h>
57 #include <stdio.h>
58 #include <errno.h>
59 #include <unistd.h>
60 #include <fcntl.h>
61
62 extern char _data[];
63 extern char _text[];
64
65 #include <filehdr.h>
66 #include <aouthdr.h>
67 #include <scnhdr.h>
68 #include <syms.h>
69
70 static struct filehdr f_hdr;
71 static struct aouthdr f_ohdr;
72 static off_t bias;
73 static off_t lnnoptr;
74
75 static off_t text_scnptr;
76 static off_t data_scnptr;
77 #define ALIGN(val, pwr) (((val) + ((1L<<(pwr))-1)) & ~((1L<<(pwr))-1))
78 static off_t load_scnptr;
79 static off_t orig_load_scnptr;
80 static off_t orig_data_scnptr;
81 static int unrelocate_symbols (int, int, const char *, const char *);
82
83 #ifndef MAX_SECTIONS
84 #define MAX_SECTIONS 10
85 #endif
86
87 static int adjust_lnnoptrs (int, int, const char *);
88
89 static int pagemask;
90
91 #include "lisp.h"
92
93 static _Noreturn void
94 report_error (const char *file, int fd)
95 {
96 int err = errno;
97 if (fd)
98 emacs_close (fd);
99 report_file_errno ("Cannot unexec", build_string (file), err);
100 }
101
102 #define ERROR0(msg) report_error_1 (new, msg)
103 #define ERROR1(msg,x) report_error_1 (new, msg, x)
104 #define ERROR2(msg,x,y) report_error_1 (new, msg, x, y)
105
106 static _Noreturn void ATTRIBUTE_FORMAT_PRINTF (2, 3)
107 report_error_1 (int fd, const char *msg, ...)
108 {
109 va_list ap;
110 emacs_close (fd);
111 va_start (ap, msg);
112 verror (msg, ap);
113 va_end (ap);
114 }
115
116 static int make_hdr (int, int, const char *, const char *);
117 static void mark_x (const char *);
118 static int copy_text_and_data (int);
119 static int copy_sym (int, int, const char *, const char *);
120 static void write_segment (int, char *, char *);
121
122
123
124
125
126
127 void
128 unexec (const char *new_name, const char *a_name)
129 {
130 int new = -1, a_out = -1;
131
132 if (a_name && (a_out = emacs_open (a_name, O_RDONLY, 0)) < 0)
133 {
134 PERROR (a_name);
135 }
136 if ((new = emacs_open (new_name, O_WRONLY | O_CREAT | O_TRUNC, 0777)) < 0)
137 {
138 PERROR (new_name);
139 }
140 if (make_hdr (new, a_out,
141 a_name, new_name) < 0
142 || copy_text_and_data (new) < 0
143 || copy_sym (new, a_out, a_name, new_name) < 0
144 || adjust_lnnoptrs (new, a_out, new_name) < 0
145 || unrelocate_symbols (new, a_out, a_name, new_name) < 0)
146 {
147 emacs_close (new);
148 return;
149 }
150
151 emacs_close (new);
152 if (a_out >= 0)
153 emacs_close (a_out);
154 }
155
156
157
158
159
160
161
162 static int
163 make_hdr (int new, int a_out,
164 const char *a_name, const char *new_name)
165 {
166 int scns;
167 uintptr_t bss_start;
168 uintptr_t data_start;
169
170 struct scnhdr section[MAX_SECTIONS];
171 struct scnhdr * f_thdr;
172 struct scnhdr * f_dhdr;
173 struct scnhdr * f_bhdr;
174 struct scnhdr * f_lhdr;
175 struct scnhdr * f_tchdr;
176 struct scnhdr * f_dbhdr;
177 struct scnhdr * f_xhdr;
178
179 load_scnptr = orig_load_scnptr = lnnoptr = 0;
180 pagemask = getpagesize () - 1;
181
182
183 data_start = (uintptr_t) _data;
184
185 data_start = data_start & ~pagemask;
186
187 bss_start = (uintptr_t) sbrk (0) + pagemask;
188 bss_start &= ~ pagemask;
189
190 if (data_start > bss_start)
191 {
192 ERROR2 (("unexec: data_start (0x%"PRIxPTR
193 ") can't be greater than bss_start (0x%"PRIxPTR")"),
194 data_start, bss_start);
195 }
196
197
198 f_thdr = NULL; f_dhdr = NULL; f_bhdr = NULL;
199 f_lhdr = NULL; f_tchdr = NULL; f_dbhdr = NULL; f_xhdr = NULL;
200 if (a_out >= 0)
201 {
202 if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
203 {
204 PERROR (a_name);
205 }
206 if (f_hdr.f_opthdr > 0)
207 {
208 if (read (a_out, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
209 {
210 PERROR (a_name);
211 }
212 }
213 if (f_hdr.f_nscns > MAX_SECTIONS)
214 {
215 ERROR0 ("unexec: too many section headers -- increase MAX_SECTIONS");
216 }
217
218 for (scns = 0; scns < f_hdr.f_nscns; scns++) {
219 struct scnhdr *s = §ion[scns];
220 if (read (a_out, s, sizeof (*s)) != sizeof (*s))
221 {
222 PERROR (a_name);
223 }
224
225 #define CHECK_SCNHDR(ptr, name, flags) \
226 if (strcmp (s->s_name, name) == 0) { \
227 if (s->s_flags != flags) { \
228 fprintf (stderr, "unexec: %lx flags where %x expected in %s section.\n", \
229 (unsigned long)s->s_flags, flags, name); \
230 } \
231 if (ptr) { \
232 fprintf (stderr, "unexec: duplicate section header for section %s.\n", \
233 name); \
234 } \
235 ptr = s; \
236 }
237 CHECK_SCNHDR (f_thdr, _TEXT, STYP_TEXT);
238 CHECK_SCNHDR (f_dhdr, _DATA, STYP_DATA);
239 CHECK_SCNHDR (f_bhdr, _BSS, STYP_BSS);
240 CHECK_SCNHDR (f_lhdr, _LOADER, STYP_LOADER);
241 CHECK_SCNHDR (f_dbhdr, _DEBUG, STYP_DEBUG);
242 CHECK_SCNHDR (f_tchdr, _TYPCHK, STYP_TYPCHK);
243 CHECK_SCNHDR (f_xhdr, _EXCEPT, STYP_EXCEPT);
244 }
245
246 if (f_thdr == 0)
247 {
248 ERROR1 ("unexec: couldn't find \"%s\" section", _TEXT);
249 }
250 if (f_dhdr == 0)
251 {
252 ERROR1 ("unexec: couldn't find \"%s\" section", _DATA);
253 }
254 if (f_bhdr == 0)
255 {
256 ERROR1 ("unexec: couldn't find \"%s\" section", _BSS);
257 }
258 }
259 else
260 {
261 ERROR0 ("can't build a COFF file from scratch yet");
262 }
263 orig_data_scnptr = f_dhdr->s_scnptr;
264 orig_load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0;
265
266
267
268
269
270
271 f_hdr.f_flags |= (F_RELFLG | F_EXEC);
272
273 f_ohdr.dsize = bss_start - f_ohdr.data_start;
274 f_ohdr.bsize = 0;
275
276 f_dhdr->s_size = f_ohdr.dsize;
277 f_bhdr->s_size = f_ohdr.bsize;
278 f_bhdr->s_paddr = f_ohdr.data_start + f_ohdr.dsize;
279 f_bhdr->s_vaddr = f_ohdr.data_start + f_ohdr.dsize;
280
281
282 {
283 off_t ptr = section[0].s_scnptr;
284
285 bias = -1;
286 for (scns = 0; scns < f_hdr.f_nscns; scns++)
287 {
288 struct scnhdr *s = §ion[scns];
289
290 if (s->s_flags & STYP_PAD)
291 {
292
293
294
295
296 if (f_ohdr.text_start != 0)
297 {
298 s->s_size = 512 - (ptr % 512);
299 if (s->s_size == 512)
300 s->s_size = 0;
301 }
302 s->s_scnptr = ptr;
303 }
304 else if (s->s_flags & STYP_DATA)
305 s->s_scnptr = ptr;
306 else if (!(s->s_flags & (STYP_TEXT | STYP_BSS)))
307 {
308 if (bias == -1)
309 bias = ptr - s->s_scnptr;
310
311 s->s_scnptr += bias;
312 ptr = s->s_scnptr;
313 }
314
315 ptr = ptr + s->s_size;
316 }
317 }
318
319
320 for (scns = 0; scns < f_hdr.f_nscns; scns++)
321 {
322 struct scnhdr *s = §ion[scns];
323
324 if (s->s_relptr != 0)
325 {
326 s->s_relptr += bias;
327 }
328 if (s->s_lnnoptr != 0)
329 {
330 if (lnnoptr == 0) lnnoptr = s->s_lnnoptr;
331 s->s_lnnoptr += bias;
332 }
333 }
334
335 if (f_hdr.f_symptr > 0L)
336 {
337 f_hdr.f_symptr += bias;
338 }
339
340 text_scnptr = f_thdr->s_scnptr;
341 data_scnptr = f_dhdr->s_scnptr;
342 load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0;
343
344 if (write (new, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
345 {
346 PERROR (new_name);
347 }
348
349 if (f_hdr.f_opthdr > 0)
350 {
351 if (write (new, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
352 {
353 PERROR (new_name);
354 }
355 }
356
357 for (scns = 0; scns < f_hdr.f_nscns; scns++) {
358 struct scnhdr *s = §ion[scns];
359 if (write (new, s, sizeof (*s)) != sizeof (*s))
360 {
361 PERROR (new_name);
362 }
363 }
364
365 return (0);
366 }
367
368
369
370
371
372
373 static int
374 copy_text_and_data (int new)
375 {
376 char *end;
377 char *ptr;
378
379 lseek (new, text_scnptr, SEEK_SET);
380 ptr = _text;
381 end = ptr + f_ohdr.tsize;
382 write_segment (new, ptr, end);
383
384 lseek (new, data_scnptr, SEEK_SET);
385 ptr = (char *) (ptrdiff_t) f_ohdr.data_start;
386 end = ptr + f_ohdr.dsize;
387 write_segment (new, ptr, end);
388
389 return 0;
390 }
391
392 #define UnexBlockSz (1<<12)
393 static void
394 write_segment (int new, char *ptr, char *end)
395 {
396 int i, nwrite, ret;
397 char zeros[UnexBlockSz];
398
399 for (i = 0; ptr < end;)
400 {
401
402 nwrite = (((ptrdiff_t) ptr + UnexBlockSz) & -UnexBlockSz) - (ptrdiff_t) ptr;
403
404 if (nwrite > end - ptr) nwrite = end - ptr;
405 ret = write (new, ptr, nwrite);
406
407
408
409
410 if (ret == -1 && errno == EFAULT)
411 {
412 memset (zeros, 0, nwrite);
413 write (new, zeros, nwrite);
414 }
415 else if (nwrite != ret)
416 {
417 int write_errno = errno;
418 char buf[1000];
419 void *addr = ptr;
420 sprintf (buf,
421 "unexec write failure: addr %p, fileno %d, size 0x%x, wrote 0x%x, errno %d",
422 addr, new, nwrite, ret, errno);
423 errno = write_errno;
424 PERROR (buf);
425 }
426 i += nwrite;
427 ptr += nwrite;
428 }
429 }
430
431
432
433
434
435
436 static int
437 copy_sym (int new, int a_out, const char *a_name, const char *new_name)
438 {
439 char page[UnexBlockSz];
440 int n;
441
442 if (a_out < 0)
443 return 0;
444
445 if (orig_load_scnptr == 0L)
446 return 0;
447
448 if (lnnoptr && lnnoptr < orig_load_scnptr)
449 lseek (a_out, lnnoptr, SEEK_SET);
450 else
451 lseek (a_out, orig_load_scnptr, SEEK_SET);
452
453 while ((n = read (a_out, page, sizeof page)) > 0)
454 {
455 if (write (new, page, n) != n)
456 {
457 PERROR (new_name);
458 }
459 }
460 if (n < 0)
461 {
462 PERROR (a_name);
463 }
464 return 0;
465 }
466
467 static int
468 adjust_lnnoptrs (int writedesc, int readdesc, const char *new_name)
469 {
470 int nsyms;
471 int naux;
472 int new;
473 struct syment symentry;
474 union auxent auxentry;
475
476 if (!lnnoptr || !f_hdr.f_symptr)
477 return 0;
478
479 if ((new = emacs_open (new_name, O_RDWR, 0)) < 0)
480 {
481 PERROR (new_name);
482 return -1;
483 }
484
485 lseek (new, f_hdr.f_symptr, SEEK_SET);
486 for (nsyms = 0; nsyms < f_hdr.f_nsyms; nsyms++)
487 {
488 read (new, &symentry, SYMESZ);
489 if (symentry.n_sclass == C_BINCL || symentry.n_sclass == C_EINCL)
490 {
491 symentry.n_value += bias;
492 lseek (new, -SYMESZ, SEEK_CUR);
493 write (new, &symentry, SYMESZ);
494 }
495
496 for (naux = symentry.n_numaux; naux-- != 0; )
497 {
498 read (new, &auxentry, AUXESZ);
499 nsyms++;
500 if (naux != 0
501 && (symentry.n_sclass == C_EXT || symentry.n_sclass == C_HIDEXT))
502 {
503 auxentry.x_sym.x_fcnary.x_fcn.x_lnnoptr += bias;
504 lseek (new, -AUXESZ, SEEK_CUR);
505 write (new, &auxentry, AUXESZ);
506 }
507 }
508 }
509 emacs_close (new);
510
511 return 0;
512 }
513
514 static int
515 unrelocate_symbols (int new, int a_out,
516 const char *a_name, const char *new_name)
517 {
518 int i;
519 LDHDR ldhdr;
520 LDREL ldrel;
521 off_t t_reloc = (intptr_t) _text - f_ohdr.text_start;
522 #ifndef ALIGN_DATA_RELOC
523 off_t d_reloc = (intptr_t) _data - f_ohdr.data_start;
524 #else
525
526
527 off_t d_reloc = (intptr_t) _data - ALIGN (f_ohdr.data_start, 2);
528 #endif
529 int * p;
530
531 if (load_scnptr == 0)
532 return 0;
533
534 lseek (a_out, orig_load_scnptr, SEEK_SET);
535 if (read (a_out, &ldhdr, sizeof (ldhdr)) != sizeof (ldhdr))
536 {
537 PERROR (new_name);
538 }
539
540 #define SYMNDX_TEXT 0
541 #define SYMNDX_DATA 1
542 #define SYMNDX_BSS 2
543
544 for (i = 0; i < ldhdr.l_nreloc; i++)
545 {
546 lseek (a_out,
547 orig_load_scnptr + LDHDRSZ + LDSYMSZ*ldhdr.l_nsyms + LDRELSZ*i,
548 SEEK_SET);
549
550 if (read (a_out, &ldrel, LDRELSZ) != LDRELSZ)
551 {
552 PERROR (a_name);
553 }
554
555
556 if (ldrel.l_symndx == SYMNDX_BSS)
557 {
558 ldrel.l_symndx = SYMNDX_DATA;
559
560 lseek (new,
561 load_scnptr + LDHDRSZ + LDSYMSZ*ldhdr.l_nsyms + LDRELSZ*i,
562 SEEK_SET);
563
564 if (write (new, &ldrel, LDRELSZ) != LDRELSZ)
565 {
566 PERROR (new_name);
567 }
568 }
569
570 if (ldrel.l_rsecnm == f_ohdr.o_sndata)
571 {
572 int orig_int;
573
574 lseek (a_out,
575 orig_data_scnptr + (ldrel.l_vaddr - f_ohdr.data_start),
576 SEEK_SET);
577
578 if (read (a_out, (void *) &orig_int, sizeof (orig_int))
579 != sizeof (orig_int))
580 {
581 PERROR (a_name);
582 }
583
584 p = (int *) (intptr_t) (ldrel.l_vaddr + d_reloc);
585
586 switch (ldrel.l_symndx) {
587 case SYMNDX_TEXT:
588 orig_int = * p - t_reloc;
589 break;
590
591 case SYMNDX_DATA:
592 case SYMNDX_BSS:
593 orig_int = * p - d_reloc;
594 break;
595 }
596
597 if (orig_int != * p)
598 {
599 lseek (new,
600 data_scnptr + (ldrel.l_vaddr - f_ohdr.data_start),
601 SEEK_SET);
602 if (write (new, (void *) &orig_int, sizeof (orig_int))
603 != sizeof (orig_int))
604 {
605 PERROR (new_name);
606 }
607 }
608 }
609 }
610 return 0;
611 }