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
37ELFTC_VCSID("$Id: segments.c 3615 2018-05-17 04:12:24Z kaiwang27 $");
38
39static 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 */
53int
54add_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
102void
103adjust_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
329adjust_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
441static void
442insert_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
468void
469setup_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
505void
506copy_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