1 /*-
2 * Copyright (c) 2007-2010,2012 Kai Wang
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/queue.h>
28 #include <err.h>
29 #include <gelf.h>
30 #include <stdint.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "elfcopy.h"
36
37 ELFTC_VCSID("$Id: segments.c 3615 2018-05-17 04:12:24Z kaiwang27 $");
38
39 static void insert_to_inseg_list(struct segment *seg, struct section *sec);
40
41 /*
42 * elfcopy's segment handling is relatively simpler and less powerful than
43 * libbfd. Program headers are modified or copied from input to output objects,
44 * but never re-generated. As a result, if the input object has incorrect
45 * program headers, the output object's program headers will remain incorrect
46 * or become even worse.
47 */
48
49 /*
50 * Check whether a section is "loadable". If so, add it to the
51 * corresponding segment list(s) and return 1.
52 */
53 int
add_to_inseg_list(struct elfcopy * ecp,struct section * s)54 add_to_inseg_list(struct elfcopy *ecp, struct section *s)
55 {
56 struct segment *seg;
57 int loadable;
58
59 if (ecp->ophnum == 0)
60 return (0);
61
62 /*
63 * Segment is a different view of an ELF object. One segment can
64 * contain one or more sections, and one section can be included
65 * in one or more segments, or not included in any segment at all.
66 * We call those sections which can be found in one or more segments
67 * "loadable" sections, and call the rest "unloadable" sections.
68 * We keep track of "loadable" sections in their containing
69 * segment(s)' v_sec queue. These information are later used to
70 * recalculate the extents of segments, when sections are removed,
71 * for example.
72 */
73 loadable = 0;
74 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
75 if (s->off < seg->off || (s->vma < seg->vaddr && !s->pseudo))
76 continue;
77 if (s->off + s->sz > seg->off + seg->fsz &&
78 s->type != SHT_NOBITS)
79 continue;
80 if (s->vma + s->sz > seg->vaddr + seg->msz)
81 continue;
82 if (seg->type == PT_TLS && ((s->flags & SHF_TLS) == 0))
83 continue;
84
85 insert_to_inseg_list(seg, s);
86 if (seg->type == PT_LOAD)
87 s->seg = seg;
88 else if (seg->type == PT_TLS)
89 s->seg_tls = seg;
90 if (s->pseudo)
91 s->vma = seg->vaddr + (s->off - seg->off);
92 if (seg->paddr > 0)
93 s->lma = seg->paddr + (s->off - seg->off);
94 else
95 s->lma = 0;
96 loadable = 1;
97 }
98
99 return (loadable);
100 }
101
102 void
adjust_addr(struct elfcopy * ecp)103 adjust_addr(struct elfcopy *ecp)
104 {
105 struct section *s, *s0;
106 struct segment *seg;
107 struct sec_action *sac;
108 uint64_t dl, vma, lma, start, end;
109 int found, i;
110
111 /*
112 * Apply VMA and global LMA changes in the first iteration.
113 */
114 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
115
116 /* Only adjust loadable section's address. */
117 if (!s->loadable)
118 continue;
119
120 /* Apply global VMA adjustment. */
121 if (ecp->change_addr != 0)
122 s->vma += ecp->change_addr;
123
124 /* Apply global LMA adjustment. */
125 if (ecp->change_addr != 0 && s->seg != NULL &&
126 s->seg->paddr > 0)
127 s->lma += ecp->change_addr;
128 }
129
130 /*
131 * Apply sections VMA change in the second iteration.
132 */
133 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
134
135 if (!s->loadable)
136 continue;
137
138 /*
139 * Check if there is a VMA change request for this
140 * section.
141 */
142 sac = lookup_sec_act(ecp, s->name, 0);
143 if (sac == NULL)
144 continue;
145 vma = s->vma;
146 if (sac->setvma)
147 vma = sac->vma;
148 if (sac->vma_adjust != 0)
149 vma += sac->vma_adjust;
150 if (vma == s->vma)
151 continue;
152
153 /*
154 * No need to make segment adjustment if the section doesn't
155 * belong to any segment.
156 */
157 if (s->seg == NULL) {
158 s->vma = vma;
159 continue;
160 }
161
162 /*
163 * Check if the VMA change is viable.
164 *
165 * 1. Check if the new VMA is properly aligned accroding to
166 * section alignment.
167 *
168 * 2. Compute the new extent of segment that contains this
169 * section, make sure it doesn't overlap with other
170 * segments.
171 */
172 #ifdef DEBUG
173 printf("VMA for section %s: %#jx\n", s->name, vma);
174 #endif
175
176 if (vma % s->align != 0)
177 errx(EXIT_FAILURE, "The VMA %#jx for "
178 "section %s is not aligned to %ju",
179 (uintmax_t) vma, s->name, (uintmax_t) s->align);
180
181 if (vma < s->vma) {
182 /* Move section to lower address. */
183 if (vma < s->vma - s->seg->vaddr)
184 errx(EXIT_FAILURE, "Not enough space to move "
185 "section %s VMA to %#jx", s->name,
186 (uintmax_t) vma);
187 start = vma - (s->vma - s->seg->vaddr);
188 if (s == s->seg->v_sec[s->seg->nsec - 1])
189 end = start + s->seg->msz;
190 else
191 end = s->seg->vaddr + s->seg->msz;
192 } else {
193 /* Move section to upper address. */
194 if (s == s->seg->v_sec[0])
195 start = vma;
196 else
197 start = s->seg->vaddr;
198 end = vma + (s->seg->vaddr + s->seg->msz - s->vma);
199 if (end < start)
200 errx(EXIT_FAILURE, "Not enough space to move "
201 "section %s VMA to %#jx", s->name,
202 (uintmax_t) vma);
203 }
204
205 #ifdef DEBUG
206 printf("new extent for segment containing %s: (%#jx,%#jx)\n",
207 s->name, start, end);
208 #endif
209
210 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
211 if (seg == s->seg || seg->type != PT_LOAD)
212 continue;
213 if (start > seg->vaddr + seg->msz)
214 continue;
215 if (end < seg->vaddr)
216 continue;
217 errx(EXIT_FAILURE, "The extent of segment containing "
218 "section %s overlaps with segment(%#jx,%#jx)",
219 s->name, (uintmax_t) seg->vaddr,
220 (uintmax_t) (seg->vaddr + seg->msz));
221 }
222
223 /*
224 * Update section VMA and file offset.
225 */
226
227 if (vma < s->vma) {
228 /*
229 * To move a section to lower VMA, we decrease
230 * the VMA of the section and all the sections that
231 * are before it, and we increase the file offsets
232 * of all the sections that are after it.
233 */
234 dl = s->vma - vma;
235 for (i = 0; i < s->seg->nsec; i++) {
236 s0 = s->seg->v_sec[i];
237 s0->vma -= dl;
238 #ifdef DEBUG
239 printf("section %s VMA set to %#jx\n",
240 s0->name, (uintmax_t) s0->vma);
241 #endif
242 if (s0 == s)
243 break;
244 }
245 for (i = i + 1; i < s->seg->nsec; i++) {
246 s0 = s->seg->v_sec[i];
247 s0->off += dl;
248 #ifdef DEBUG
249 printf("section %s offset set to %#jx\n",
250 s0->name, (uintmax_t) s0->off);
251 #endif
252 }
253 } else {
254 /*
255 * To move a section to upper VMA, we increase
256 * the VMA of the section and all the sections that
257 * are after it, and we increase the their file
258 * offsets too unless the section in question
259 * is the first in its containing segment.
260 */
261 dl = vma - s->vma;
262 for (i = 0; i < s->seg->nsec; i++)
263 if (s->seg->v_sec[i] == s)
264 break;
265 if (i >= s->seg->nsec)
266 errx(EXIT_FAILURE, "Internal: section `%s' not"
267 " found in its containing segement",
268 s->name);
269 for (; i < s->seg->nsec; i++) {
270 s0 = s->seg->v_sec[i];
271 s0->vma += dl;
272 #ifdef DEBUG
273 printf("section %s VMA set to %#jx\n",
274 s0->name, (uintmax_t) s0->lma);
275 #endif
276 if (s != s->seg->v_sec[0]) {
277 s0->off += dl;
278 #ifdef DEBUG
279 printf("section %s offset set to %#jx\n",
280 s0->name, (uintmax_t) s0->off);
281 #endif
282 }
283 }
284 }
285 }
286
287 /*
288 * Apply load address padding.
289 */
290
291 if (ecp->pad_to != 0) {
292
293 /*
294 * Find the section with highest VMA.
295 */
296 s = NULL;
297 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
298 if (seg->type != PT_LOAD)
299 continue;
300 for (i = seg->nsec - 1; i >= 0; i--)
301 if (seg->v_sec[i]->type != SHT_NOBITS)
302 break;
303 if (i < 0)
304 continue;
305 if (s == NULL)
306 s = seg->v_sec[i];
307 else {
308 s0 = seg->v_sec[i];
309 if (s0->vma > s->vma)
310 s = s0;
311 }
312 }
313
314 if (s == NULL)
315 goto adjust_lma;
316
317 /* No need to pad if the pad_to address is lower. */
318 if (ecp->pad_to <= s->vma + s->sz)
319 goto adjust_lma;
320
321 s->pad_sz = ecp->pad_to - (s->vma + s->sz);
322 #ifdef DEBUG
323 printf("pad section %s VMA to address %#jx by %#jx\n", s->name,
324 (uintmax_t) ecp->pad_to, (uintmax_t) s->pad_sz);
325 #endif
326 }
327
328
329 adjust_lma:
330
331 /*
332 * Apply sections LMA change in the third iteration.
333 */
334 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
335
336 /*
337 * Only loadable section that's inside a segment can have
338 * LMA adjusted. Also, if LMA of the containing segment is
339 * set to 0, it probably means we should ignore the LMA.
340 */
341 if (!s->loadable || s->seg == NULL || s->seg->paddr == 0)
342 continue;
343
344 /*
345 * Check if there is a LMA change request for this
346 * section.
347 */
348 sac = lookup_sec_act(ecp, s->name, 0);
349 if (sac == NULL)
350 continue;
351 if (!sac->setlma && sac->lma_adjust == 0)
352 continue;
353 lma = s->lma;
354 if (sac->setlma)
355 lma = sac->lma;
356 if (sac->lma_adjust != 0)
357 lma += sac->lma_adjust;
358 if (lma == s->lma)
359 continue;
360
361 #ifdef DEBUG
362 printf("LMA for section %s: %#jx\n", s->name, lma);
363 #endif
364
365 /* Check alignment. */
366 if (lma % s->align != 0)
367 errx(EXIT_FAILURE, "The LMA %#jx for "
368 "section %s is not aligned to %ju",
369 (uintmax_t) lma, s->name, (uintmax_t) s->align);
370
371 /*
372 * Update section LMA.
373 */
374
375 if (lma < s->lma) {
376 /*
377 * To move a section to lower LMA, we decrease
378 * the LMA of the section and all the sections that
379 * are before it.
380 */
381 dl = s->lma - lma;
382 for (i = 0; i < s->seg->nsec; i++) {
383 s0 = s->seg->v_sec[i];
384 s0->lma -= dl;
385 #ifdef DEBUG
386 printf("section %s LMA set to %#jx\n",
387 s0->name, (uintmax_t) s0->lma);
388 #endif
389 if (s0 == s)
390 break;
391 }
392 } else {
393 /*
394 * To move a section to upper LMA, we increase
395 * the LMA of the section and all the sections that
396 * are after it.
397 */
398 dl = lma - s->lma;
399 for (i = 0; i < s->seg->nsec; i++)
400 if (s->seg->v_sec[i] == s)
401 break;
402 if (i >= s->seg->nsec)
403 errx(EXIT_FAILURE, "Internal: section `%s' not"
404 " found in its containing segement",
405 s->name);
406 for (; i < s->seg->nsec; i++) {
407 s0 = s->seg->v_sec[i];
408 s0->lma += dl;
409 #ifdef DEBUG
410 printf("section %s LMA set to %#jx\n",
411 s0->name, (uintmax_t) s0->lma);
412 #endif
413 }
414 }
415 }
416
417 /*
418 * Issue a warning if there are VMA/LMA adjust requests for
419 * some nonexistent sections.
420 */
421 if ((ecp->flags & NO_CHANGE_WARN) == 0) {
422 STAILQ_FOREACH(sac, &ecp->v_sac, sac_list) {
423 if (!sac->setvma && !sac->setlma &&
424 !sac->vma_adjust && !sac->lma_adjust)
425 continue;
426 found = 0;
427 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
428 if (s->pseudo || s->name == NULL)
429 continue;
430 if (!strcmp(s->name, sac->name)) {
431 found = 1;
432 break;
433 }
434 }
435 if (!found)
436 warnx("cannot find section `%s'", sac->name);
437 }
438 }
439 }
440
441 static void
insert_to_inseg_list(struct segment * seg,struct section * sec)442 insert_to_inseg_list(struct segment *seg, struct section *sec)
443 {
444 struct section *s;
445 int i;
446
447 seg->nsec++;
448 seg->v_sec = realloc(seg->v_sec, seg->nsec * sizeof(*seg->v_sec));
449 if (seg->v_sec == NULL)
450 err(EXIT_FAILURE, "realloc failed");
451
452 /*
453 * Sort the section in order of offset.
454 */
455
456 for (i = seg->nsec - 1; i > 0; i--) {
457 s = seg->v_sec[i - 1];
458 if (sec->off >= s->off) {
459 seg->v_sec[i] = sec;
460 break;
461 } else
462 seg->v_sec[i] = s;
463 }
464 if (i == 0)
465 seg->v_sec[0] = sec;
466 }
467
468 void
setup_phdr(struct elfcopy * ecp)469 setup_phdr(struct elfcopy *ecp)
470 {
471 struct segment *seg;
472 GElf_Phdr iphdr;
473 size_t iphnum, i;
474
475 if (elf_getphnum(ecp->ein, &iphnum) == 0)
476 errx(EXIT_FAILURE, "elf_getphnum failed: %s",
477 elf_errmsg(-1));
478
479 ecp->ophnum = ecp->iphnum = iphnum;
480 if (iphnum == 0)
481 return;
482
483 /* If --only-keep-debug is specified, discard all program headers. */
484 if (ecp->strip == STRIP_NONDEBUG) {
485 ecp->ophnum = 0;
486 return;
487 }
488
489 for (i = 0; i < iphnum; i++) {
490 if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr)
491 errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
492 elf_errmsg(-1));
493 if ((seg = calloc(1, sizeof(*seg))) == NULL)
494 err(EXIT_FAILURE, "calloc failed");
495 seg->vaddr = iphdr.p_vaddr;
496 seg->paddr = iphdr.p_paddr;
497 seg->off = iphdr.p_offset;
498 seg->fsz = iphdr.p_filesz;
499 seg->msz = iphdr.p_memsz;
500 seg->type = iphdr.p_type;
501 STAILQ_INSERT_TAIL(&ecp->v_seg, seg, seg_list);
502 }
503 }
504
505 void
copy_phdr(struct elfcopy * ecp)506 copy_phdr(struct elfcopy *ecp)
507 {
508 struct segment *seg;
509 struct section *s;
510 GElf_Phdr iphdr, ophdr;
511 int i;
512
513 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
514 if (seg->type == PT_PHDR) {
515 if (!TAILQ_EMPTY(&ecp->v_sec)) {
516 s = TAILQ_FIRST(&ecp->v_sec);
517 if (s->pseudo) {
518 seg->vaddr = s->vma +
519 gelf_fsize(ecp->eout, ELF_T_EHDR,
520 1, EV_CURRENT);
521 seg->paddr = s->lma +
522 gelf_fsize(ecp->eout, ELF_T_EHDR,
523 1, EV_CURRENT);
524 }
525 }
526 seg->fsz = seg->msz = gelf_fsize(ecp->eout, ELF_T_PHDR,
527 ecp->ophnum, EV_CURRENT);
528 continue;
529 }
530
531 if (seg->nsec > 0) {
532 s = seg->v_sec[0];
533 seg->vaddr = s->vma;
534 seg->paddr = s->lma;
535 }
536
537 seg->fsz = seg->msz = 0;
538 for (i = 0; i < seg->nsec; i++) {
539 s = seg->v_sec[i];
540 seg->msz = s->vma + s->sz - seg->vaddr;
541 if (s->type != SHT_NOBITS)
542 seg->fsz = s->off + s->sz - seg->off;
543 }
544 }
545
546 /*
547 * Allocate space for program headers, note that libelf keep
548 * track of the number in internal variable, and a call to
549 * elf_update is needed to update e_phnum of ehdr.
550 */
551 if (gelf_newphdr(ecp->eout, ecp->ophnum) == NULL)
552 errx(EXIT_FAILURE, "gelf_newphdr() failed: %s",
553 elf_errmsg(-1));
554
555 /*
556 * This elf_update() call is to update the e_phnum field in
557 * ehdr. It's necessary because later we will call gelf_getphdr(),
558 * which does sanity check by comparing ndx argument with e_phnum.
559 */
560 if (elf_update(ecp->eout, ELF_C_NULL) < 0)
561 errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1));
562
563 /*
564 * iphnum == ophnum, since we don't remove program headers even if
565 * they no longer contain sections.
566 */
567 i = 0;
568 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
569 if (i >= ecp->iphnum)
570 break;
571 if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr)
572 errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
573 elf_errmsg(-1));
574 if (gelf_getphdr(ecp->eout, i, &ophdr) != &ophdr)
575 errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
576 elf_errmsg(-1));
577
578 ophdr.p_type = iphdr.p_type;
579 ophdr.p_vaddr = seg->vaddr;
580 ophdr.p_paddr = seg->paddr;
581 ophdr.p_flags = iphdr.p_flags;
582 ophdr.p_align = iphdr.p_align;
583 ophdr.p_offset = seg->off;
584 ophdr.p_filesz = seg->fsz;
585 ophdr.p_memsz = seg->msz;
586 if (!gelf_update_phdr(ecp->eout, i, &ophdr))
587 errx(EXIT_FAILURE, "gelf_update_phdr failed: %s",
588 elf_errmsg(-1));
589
590 i++;
591 }
592 }
593