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