2 * Copyright (c) 2007-2010,2012 Kai Wang
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 #include <sys/queue.h>
38 ELFTC_VCSID("$Id: segments.c 3134 2014-12-23 10:43:59Z kaiwang27 $");
40 static void insert_to_inseg_list(struct segment *seg, struct section *sec);
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.
51 * Check whether a section is "loadable". If so, add it to the
52 * corresponding segment list(s) and return 1.
55 add_to_inseg_list(struct elfcopy *ecp, struct section *s)
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,
75 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
76 if (s->off < seg->off || (s->vma < seg->addr && !s->pseudo))
78 if (s->off + s->sz > seg->off + seg->fsz &&
79 s->type != SHT_NOBITS)
81 if (s->off + s->sz > seg->off + seg->msz)
83 if (s->vma + s->sz > seg->addr + seg->msz)
86 insert_to_inseg_list(seg, s);
87 if (seg->type == PT_LOAD)
89 else if (seg->type == PT_TLS)
91 s->lma = seg->addr + (s->off - seg->off);
99 adjust_addr(struct elfcopy *ecp)
101 struct section *s, *s0;
103 struct sec_action *sac;
104 uint64_t dl, lma, start, end;
108 * Apply VMA and global LMA changes in the first iteration.
110 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
112 /* Only adjust loadable section's address. */
113 if (!s->loadable || s->seg == NULL)
116 /* Apply global LMA adjustment. */
117 if (ecp->change_addr != 0)
118 s->lma += ecp->change_addr;
121 /* Apply global VMA adjustment. */
122 if (ecp->change_addr != 0)
123 s->vma += ecp->change_addr;
125 /* Apply section VMA adjustment. */
126 sac = lookup_sec_act(ecp, s->name, 0);
131 if (sac->vma_adjust != 0)
132 s->vma += sac->vma_adjust;
137 * Apply sections LMA change in the second iteration.
139 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
141 /* Only adjust loadable section's LMA. */
142 if (!s->loadable || s->seg == NULL)
146 * Check if there is a LMA change request for this
149 sac = lookup_sec_act(ecp, s->name, 0);
152 if (!sac->setlma && sac->lma_adjust == 0)
157 if (sac->lma_adjust != 0)
158 lma += sac->lma_adjust;
163 * Check if the LMA change is viable.
165 * 1. Check if the new LMA is properly aligned accroding to
168 * 2. Compute the new extent of segment that contains this
169 * section, make sure it doesn't overlap with other
173 printf("LMA for section %s: %#jx\n", s->name, lma);
176 if (lma % s->align != 0)
177 errx(EXIT_FAILURE, "The load address %#jx for "
178 "section %s is not aligned to %ju",
179 (uintmax_t) lma, s->name, s->align);
182 /* Move section to lower address. */
183 if (lma < s->lma - s->seg->addr)
184 errx(EXIT_FAILURE, "Not enough space to move "
185 "section %s load address to %#jx", s->name,
187 start = lma - (s->lma - s->seg->addr);
188 if (s == s->seg->v_sec[s->seg->nsec - 1])
189 end = start + s->seg->msz;
191 end = s->seg->addr + s->seg->msz;
194 /* Move section to upper address. */
195 if (s == s->seg->v_sec[0])
198 start = s->seg->addr;
199 end = lma + (s->seg->addr + s->seg->msz - s->lma);
201 errx(EXIT_FAILURE, "Not enough space to move "
202 "section %s load address to %#jx", s->name,
207 printf("new extent for segment containing %s: (%#jx,%#jx)\n",
208 s->name, start, end);
211 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
212 if (seg == s->seg || seg->type != PT_LOAD)
214 if (start > seg->addr + seg->msz)
218 errx(EXIT_FAILURE, "The extent of segment containing "
219 "section %s overlaps with segment(%#jx,%#jx)",
220 s->name, seg->addr, seg->addr + seg->msz);
224 * Update section LMA and file offset.
229 * To move a section to lower load address, we decrease
230 * the load addresses of the section and all the
231 * sections that are before it, and we increase the
232 * file offsets of all the sections that are after it.
235 for (i = 0; i < s->seg->nsec; i++) {
236 s0 = s->seg->v_sec[i];
239 printf("section %s LMA set to %#jx\n",
240 s0->name, (uintmax_t) s0->lma);
245 for (i = i + 1; i < s->seg->nsec; i++) {
246 s0 = s->seg->v_sec[i];
249 printf("section %s offset set to %#jx\n",
250 s0->name, (uintmax_t) s0->off);
255 * To move a section to upper load address, we increase
256 * the load addresses of the section and all the
257 * sections that are after it, and we increase the
258 * their file offsets too unless the section in question
259 * is the first in its containing segment.
262 for (i = 0; i < s->seg->nsec; i++)
263 if (s->seg->v_sec[i] == s)
265 if (i >= s->seg->nsec)
266 errx(EXIT_FAILURE, "Internal: section `%s' not"
267 " found in its containing segement",
269 for (; i < s->seg->nsec; i++) {
270 s0 = s->seg->v_sec[i];
273 printf("section %s LMA set to %#jx\n",
274 s0->name, (uintmax_t) s0->lma);
276 if (s != s->seg->v_sec[0]) {
279 printf("section %s offset set to %#jx\n",
280 s0->name, (uintmax_t) s0->off);
288 * Apply load address padding.
291 if (ecp->pad_to != 0) {
294 * Find the section with highest load address.
298 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
299 if (seg->type != PT_LOAD)
301 for (i = seg->nsec - 1; i >= 0; i--)
302 if (seg->v_sec[i]->type != SHT_NOBITS)
310 if (s0->lma > s->lma)
318 /* No need to pad if the pad_to address is lower. */
319 if (ecp->pad_to <= s->lma + s->sz)
322 s->pad_sz = ecp->pad_to - (s->lma + s->sz);
324 printf("pad section %s load to address %#jx by %#jx\n", s->name,
325 (uintmax_t) ecp->pad_to, (uintmax_t) s->pad_sz);
332 * Issue a warning if there are VMA/LMA adjust requests for
333 * some nonexistent sections.
335 if ((ecp->flags & NO_CHANGE_WARN) == 0) {
336 STAILQ_FOREACH(sac, &ecp->v_sac, sac_list) {
337 if (!sac->setvma && !sac->setlma &&
338 !sac->vma_adjust && !sac->lma_adjust)
341 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
342 if (s->pseudo || s->name == NULL)
344 if (!strcmp(s->name, sac->name)) {
350 warnx("cannot find section `%s'", sac->name);
356 insert_to_inseg_list(struct segment *seg, struct section *sec)
362 seg->v_sec = realloc(seg->v_sec, seg->nsec * sizeof(*seg->v_sec));
363 if (seg->v_sec == NULL)
364 err(EXIT_FAILURE, "realloc failed");
367 * Sort the section in order of offset.
370 for (i = seg->nsec - 1; i > 0; i--) {
371 s = seg->v_sec[i - 1];
372 if (sec->off >= s->off) {
383 setup_phdr(struct elfcopy *ecp)
390 if (elf_getphnum(ecp->ein, &iphnum) == 0)
391 errx(EXIT_FAILURE, "elf_getphnum failed: %s",
394 ecp->ophnum = ecp->iphnum = iphnum;
398 /* If --only-keep-debug is specified, discard all program headers. */
399 if (ecp->strip == STRIP_NONDEBUG) {
404 for (i = 0; (size_t)i < iphnum; i++) {
405 if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr)
406 errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
408 if ((seg = calloc(1, sizeof(*seg))) == NULL)
409 err(EXIT_FAILURE, "calloc failed");
410 seg->addr = iphdr.p_vaddr;
411 seg->off = iphdr.p_offset;
412 seg->fsz = iphdr.p_filesz;
413 seg->msz = iphdr.p_memsz;
414 seg->type = iphdr.p_type;
415 STAILQ_INSERT_TAIL(&ecp->v_seg, seg, seg_list);
420 copy_phdr(struct elfcopy *ecp)
424 GElf_Phdr iphdr, ophdr;
427 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
428 if (seg->type == PT_PHDR) {
429 if (!TAILQ_EMPTY(&ecp->v_sec)) {
430 s = TAILQ_FIRST(&ecp->v_sec);
433 gelf_fsize(ecp->eout, ELF_T_EHDR,
436 seg->fsz = seg->msz = gelf_fsize(ecp->eout, ELF_T_PHDR,
437 ecp->ophnum, EV_CURRENT);
441 seg->fsz = seg->msz = 0;
442 for (i = 0; i < seg->nsec; i++) {
444 seg->msz = s->vma + s->sz - seg->addr;
445 if (s->type != SHT_NOBITS)
451 * Allocate space for program headers, note that libelf keep
452 * track of the number in internal variable, and a call to
453 * elf_update is needed to update e_phnum of ehdr.
455 if (gelf_newphdr(ecp->eout, ecp->ophnum) == NULL)
456 errx(EXIT_FAILURE, "gelf_newphdr() failed: %s",
460 * This elf_update() call is to update the e_phnum field in
461 * ehdr. It's necessary because later we will call gelf_getphdr(),
462 * which does sanity check by comparing ndx argument with e_phnum.
464 if (elf_update(ecp->eout, ELF_C_NULL) < 0)
465 errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1));
468 * iphnum == ophnum, since we don't remove program headers even if
469 * they no longer contain sections.
472 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
473 if (i >= ecp->iphnum)
475 if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr)
476 errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
478 if (gelf_getphdr(ecp->eout, i, &ophdr) != &ophdr)
479 errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
482 ophdr.p_type = iphdr.p_type;
483 ophdr.p_vaddr = seg->addr;
484 ophdr.p_paddr = seg->addr;
485 ophdr.p_flags = iphdr.p_flags;
486 ophdr.p_align = iphdr.p_align;
487 ophdr.p_offset = seg->off;
488 ophdr.p_filesz = seg->fsz;
489 ophdr.p_memsz = seg->msz;
490 if (!gelf_update_phdr(ecp->eout, i, &ophdr))
491 err(EXIT_FAILURE, "gelf_update_phdr failed :%s",