xref: /freebsd/contrib/elftoolchain/elfcopy/segments.c (revision bd81e07d2761cf1c13063eb49a5c0cb4a6951318)
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 3196 2015-05-12 17:33:48Z emaste $");
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
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->addr && !s->pseudo))
76 			continue;
77 		if (s->off + s->sz > seg->off + seg->fsz &&
78 		    s->type != SHT_NOBITS)
79 			continue;
80 		if (s->off + s->sz > seg->off + seg->msz)
81 			continue;
82 		if (s->vma + s->sz > seg->addr + seg->msz)
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 		s->lma = seg->addr + (s->off - seg->off);
91 		loadable = 1;
92 	}
93 
94 	return (loadable);
95 }
96 
97 void
98 adjust_addr(struct elfcopy *ecp)
99 {
100 	struct section *s, *s0;
101 	struct segment *seg;
102 	struct sec_action *sac;
103 	uint64_t dl, lma, start, end;
104 	int found, i;
105 
106 	/*
107 	 * Apply VMA and global LMA changes in the first iteration.
108 	 */
109 	TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
110 
111 		/* Only adjust loadable section's address. */
112 		if (!s->loadable || s->seg == NULL)
113 			continue;
114 
115 		/* Apply global LMA adjustment. */
116 		if (ecp->change_addr != 0)
117 			s->lma += ecp->change_addr;
118 
119 		if (!s->pseudo) {
120 			/* Apply global VMA adjustment. */
121 			if (ecp->change_addr != 0)
122 				s->vma += ecp->change_addr;
123 
124 			/* Apply section VMA adjustment. */
125 			sac = lookup_sec_act(ecp, s->name, 0);
126 			if (sac == NULL)
127 				continue;
128 			if (sac->setvma)
129 				s->vma = sac->vma;
130 			if (sac->vma_adjust != 0)
131 				s->vma += sac->vma_adjust;
132 		}
133 	}
134 
135 	/*
136 	 * Apply sections LMA change in the second iteration.
137 	 */
138 	TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
139 
140 		/* Only adjust loadable section's LMA. */
141 		if (!s->loadable || s->seg == NULL)
142 			continue;
143 
144 		/*
145 		 * Check if there is a LMA change request for this
146 		 * section.
147 		 */
148 		sac = lookup_sec_act(ecp, s->name, 0);
149 		if (sac == NULL)
150 			continue;
151 		if (!sac->setlma && sac->lma_adjust == 0)
152 			continue;
153 		lma = s->lma;
154 		if (sac->setlma)
155 			lma = sac->lma;
156 		if (sac->lma_adjust != 0)
157 			lma += sac->lma_adjust;
158 		if (lma == s->lma)
159 			continue;
160 
161 		/*
162 		 * Check if the LMA change is viable.
163 		 *
164 		 * 1. Check if the new LMA is properly aligned accroding to
165 		 *    section alignment.
166 		 *
167 		 * 2. Compute the new extent of segment that contains this
168 		 *    section, make sure it doesn't overlap with other
169 		 *    segments.
170 		 */
171 #ifdef	DEBUG
172 		printf("LMA for section %s: %#jx\n", s->name, lma);
173 #endif
174 
175 		if (lma % s->align != 0)
176 			errx(EXIT_FAILURE, "The load address %#jx for "
177 			    "section %s is not aligned to %ju",
178 			    (uintmax_t) lma, s->name, s->align);
179 
180 		if (lma < s->lma) {
181 			/* Move section to lower address. */
182 			if (lma < s->lma - s->seg->addr)
183 				errx(EXIT_FAILURE, "Not enough space to move "
184 				    "section %s load address to %#jx", s->name,
185 				    (uintmax_t) lma);
186 			start = lma - (s->lma - s->seg->addr);
187 			if (s == s->seg->v_sec[s->seg->nsec - 1])
188 				end = start + s->seg->msz;
189 			else
190 				end = s->seg->addr + s->seg->msz;
191 
192 		} else {
193 			/* Move section to upper address. */
194 			if (s == s->seg->v_sec[0])
195 				start = lma;
196 			else
197 				start = s->seg->addr;
198 			end = lma + (s->seg->addr + s->seg->msz - s->lma);
199 			if (end < start)
200 				errx(EXIT_FAILURE, "Not enough space to move "
201 				    "section %s load address to %#jx", s->name,
202 				    (uintmax_t) lma);
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->addr + seg->msz)
214 				continue;
215 			if (end < seg->addr)
216 				continue;
217 			errx(EXIT_FAILURE, "The extent of segment containing "
218 			    "section %s overlaps with segment(%#jx,%#jx)",
219 			    s->name, seg->addr, seg->addr + seg->msz);
220 		}
221 
222 		/*
223 		 * Update section LMA and file offset.
224 		 */
225 
226 		if (lma < s->lma) {
227 			/*
228 			 * To move a section to lower load address, we decrease
229 			 * the load addresses of the section and all the
230 			 * sections that are before it, and we increase the
231 			 * file offsets of all the sections that are after it.
232 			 */
233 			dl = s->lma - lma;
234 			for (i = 0; i < s->seg->nsec; i++) {
235 				s0 = s->seg->v_sec[i];
236 				s0->lma -= dl;
237 #ifdef	DEBUG
238 				printf("section %s LMA set to %#jx\n",
239 				    s0->name, (uintmax_t) s0->lma);
240 #endif
241 				if (s0 == s)
242 					break;
243 			}
244 			for (i = i + 1; i < s->seg->nsec; i++) {
245 				s0 = s->seg->v_sec[i];
246 				s0->off += dl;
247 #ifdef	DEBUG
248 				printf("section %s offset set to %#jx\n",
249 				    s0->name, (uintmax_t) s0->off);
250 #endif
251 			}
252 		} else {
253 			/*
254 			 * To move a section to upper load address, we increase
255 			 * the load addresses of the section and all the
256 			 * sections that are after it, and we increase the
257 			 * their file offsets too unless the section in question
258 			 * is the first in its containing segment.
259 			 */
260 			dl = lma - s->lma;
261 			for (i = 0; i < s->seg->nsec; i++)
262 				if (s->seg->v_sec[i] == s)
263 					break;
264 			if (i >= s->seg->nsec)
265 				errx(EXIT_FAILURE, "Internal: section `%s' not"
266 				    " found in its containing segement",
267 				    s->name);
268 			for (; i < s->seg->nsec; i++) {
269 				s0 = s->seg->v_sec[i];
270 				s0->lma += dl;
271 #ifdef	DEBUG
272 				printf("section %s LMA set to %#jx\n",
273 				    s0->name, (uintmax_t) s0->lma);
274 #endif
275 				if (s != s->seg->v_sec[0]) {
276 					s0->off += dl;
277 #ifdef	DEBUG
278 					printf("section %s offset set to %#jx\n",
279 					    s0->name, (uintmax_t) s0->off);
280 #endif
281 				}
282 			}
283 		}
284 	}
285 
286 	/*
287 	 * Apply load address padding.
288 	 */
289 
290 	if (ecp->pad_to != 0) {
291 
292 		/*
293 		 * Find the section with highest load address.
294 		 */
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->lma > s->lma)
310 					s = s0;
311 			}
312 		}
313 
314 		if (s == NULL)
315 			goto issue_warn;
316 
317 		/* No need to pad if the pad_to address is lower. */
318 		if (ecp->pad_to <= s->lma + s->sz)
319 			goto issue_warn;
320 
321 		s->pad_sz = ecp->pad_to - (s->lma + s->sz);
322 #ifdef	DEBUG
323 		printf("pad section %s load to address %#jx by %#jx\n", s->name,
324 		    (uintmax_t) ecp->pad_to, (uintmax_t) s->pad_sz);
325 #endif
326 	}
327 
328 issue_warn:
329 
330 	/*
331 	 * Issue a warning if there are VMA/LMA adjust requests for
332 	 * some nonexistent sections.
333 	 */
334 	if ((ecp->flags & NO_CHANGE_WARN) == 0) {
335 		STAILQ_FOREACH(sac, &ecp->v_sac, sac_list) {
336 			if (!sac->setvma && !sac->setlma &&
337 			    !sac->vma_adjust && !sac->lma_adjust)
338 				continue;
339 			found = 0;
340 			TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
341 				if (s->pseudo || s->name == NULL)
342 					continue;
343 				if (!strcmp(s->name, sac->name)) {
344 					found = 1;
345 					break;
346 				}
347 			}
348 			if (!found)
349 				warnx("cannot find section `%s'", sac->name);
350 		}
351 	}
352 }
353 
354 static void
355 insert_to_inseg_list(struct segment *seg, struct section *sec)
356 {
357 	struct section *s;
358 	int i;
359 
360 	seg->nsec++;
361 	seg->v_sec = realloc(seg->v_sec, seg->nsec * sizeof(*seg->v_sec));
362 	if (seg->v_sec == NULL)
363 		err(EXIT_FAILURE, "realloc failed");
364 
365 	/*
366 	 * Sort the section in order of offset.
367 	 */
368 
369 	for (i = seg->nsec - 1; i > 0; i--) {
370 		s = seg->v_sec[i - 1];
371 		if (sec->off >= s->off) {
372 			seg->v_sec[i] = sec;
373 			break;
374 		} else
375 			seg->v_sec[i] = s;
376 	}
377 	if (i == 0)
378 		seg->v_sec[0] = sec;
379 }
380 
381 void
382 setup_phdr(struct elfcopy *ecp)
383 {
384 	struct segment	*seg;
385 	GElf_Phdr	 iphdr;
386 	size_t		 iphnum;
387 	int		 i;
388 
389 	if (elf_getphnum(ecp->ein, &iphnum) == 0)
390 		errx(EXIT_FAILURE, "elf_getphnum failed: %s",
391 		    elf_errmsg(-1));
392 
393 	ecp->ophnum = ecp->iphnum = iphnum;
394 	if (iphnum == 0)
395 		return;
396 
397 	/* If --only-keep-debug is specified, discard all program headers. */
398 	if (ecp->strip == STRIP_NONDEBUG) {
399 		ecp->ophnum = 0;
400 		return;
401 	}
402 
403 	for (i = 0; (size_t)i < iphnum; i++) {
404 		if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr)
405 			errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
406 			    elf_errmsg(-1));
407 		if ((seg = calloc(1, sizeof(*seg))) == NULL)
408 			err(EXIT_FAILURE, "calloc failed");
409 		seg->addr	= iphdr.p_vaddr;
410 		seg->off	= iphdr.p_offset;
411 		seg->fsz	= iphdr.p_filesz;
412 		seg->msz	= iphdr.p_memsz;
413 		seg->type	= iphdr.p_type;
414 		STAILQ_INSERT_TAIL(&ecp->v_seg, seg, seg_list);
415 	}
416 }
417 
418 void
419 copy_phdr(struct elfcopy *ecp)
420 {
421 	struct segment	*seg;
422 	struct section	*s;
423 	GElf_Phdr	 iphdr, ophdr;
424 	int		 i;
425 
426 	STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
427 		if (seg->type == PT_PHDR) {
428 			if (!TAILQ_EMPTY(&ecp->v_sec)) {
429 				s = TAILQ_FIRST(&ecp->v_sec);
430 				if (s->pseudo)
431 					seg->addr = s->lma +
432 					    gelf_fsize(ecp->eout, ELF_T_EHDR,
433 						1, EV_CURRENT);
434 			}
435 			seg->fsz = seg->msz = gelf_fsize(ecp->eout, ELF_T_PHDR,
436 			    ecp->ophnum, EV_CURRENT);
437 			continue;
438 		}
439 
440 		seg->fsz = seg->msz = 0;
441 		for (i = 0; i < seg->nsec; i++) {
442 			s = seg->v_sec[i];
443 			seg->msz = s->vma + s->sz - seg->addr;
444 			if (s->type != SHT_NOBITS)
445 				seg->fsz = s->off + s->sz - seg->off;
446 		}
447 	}
448 
449 	/*
450 	 * Allocate space for program headers, note that libelf keep
451 	 * track of the number in internal variable, and a call to
452 	 * elf_update is needed to update e_phnum of ehdr.
453 	 */
454 	if (gelf_newphdr(ecp->eout, ecp->ophnum) == NULL)
455 		errx(EXIT_FAILURE, "gelf_newphdr() failed: %s",
456 		    elf_errmsg(-1));
457 
458 	/*
459 	 * This elf_update() call is to update the e_phnum field in
460 	 * ehdr. It's necessary because later we will call gelf_getphdr(),
461 	 * which does sanity check by comparing ndx argument with e_phnum.
462 	 */
463 	if (elf_update(ecp->eout, ELF_C_NULL) < 0)
464 		errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1));
465 
466 	/*
467 	 * iphnum == ophnum, since we don't remove program headers even if
468 	 * they no longer contain sections.
469 	 */
470 	i = 0;
471 	STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
472 		if (i >= ecp->iphnum)
473 			break;
474 		if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr)
475 			errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
476 			    elf_errmsg(-1));
477 		if (gelf_getphdr(ecp->eout, i, &ophdr) != &ophdr)
478 			errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
479 			    elf_errmsg(-1));
480 
481 		ophdr.p_type = iphdr.p_type;
482 		ophdr.p_vaddr = seg->addr;
483 		ophdr.p_paddr = seg->addr;
484 		ophdr.p_flags = iphdr.p_flags;
485 		ophdr.p_align = iphdr.p_align;
486 		ophdr.p_offset = seg->off;
487 		ophdr.p_filesz = seg->fsz;
488 		ophdr.p_memsz = seg->msz;
489 		if (!gelf_update_phdr(ecp->eout, i, &ophdr))
490 			err(EXIT_FAILURE, "gelf_update_phdr failed :%s",
491 			    elf_errmsg(-1));
492 
493 		i++;
494 	}
495 }
496