]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/elftoolchain/elfcopy/segments.c
MFV r276568:
[FreeBSD/FreeBSD.git] / contrib / elftoolchain / elfcopy / segments.c
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 3134 2014-12-23 10:43:59Z 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 || (s->vma < seg->addr && !s->pseudo))
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                 if (s->vma + s->sz > seg->addr + seg->msz)
84                         continue;
85
86                 insert_to_inseg_list(seg, s);
87                 if (seg->type == PT_LOAD)
88                         s->seg = seg;
89                 else if (seg->type == PT_TLS)
90                         s->seg_tls = seg;
91                 s->lma = seg->addr + (s->off - seg->off);
92                 loadable = 1;
93         }
94
95         return (loadable);
96 }
97
98 void
99 adjust_addr(struct elfcopy *ecp)
100 {
101         struct section *s, *s0;
102         struct segment *seg;
103         struct sec_action *sac;
104         uint64_t dl, lma, start, end;
105         int found, i;
106
107         /*
108          * Apply VMA and global LMA changes in the first iteration.
109          */
110         TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
111
112                 /* Only adjust loadable section's address. */
113                 if (!s->loadable || s->seg == NULL)
114                         continue;
115
116                 /* Apply global LMA adjustment. */
117                 if (ecp->change_addr != 0)
118                         s->lma += ecp->change_addr;
119
120                 if (!s->pseudo) {
121                         /* Apply global VMA adjustment. */
122                         if (ecp->change_addr != 0)
123                                 s->vma += ecp->change_addr;
124
125                         /* Apply section VMA adjustment. */
126                         sac = lookup_sec_act(ecp, s->name, 0);
127                         if (sac == NULL)
128                                 continue;
129                         if (sac->setvma)
130                                 s->vma = sac->vma;
131                         if (sac->vma_adjust != 0)
132                                 s->vma += sac->vma_adjust;
133                 }
134         }
135
136         /*
137          * Apply sections LMA change in the second iteration.
138          */
139         TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
140
141                 /* Only adjust loadable section's LMA. */
142                 if (!s->loadable || s->seg == NULL)
143                         continue;
144
145                 /*
146                  * Check if there is a LMA change request for this
147                  * section.
148                  */
149                 sac = lookup_sec_act(ecp, s->name, 0);
150                 if (sac == NULL)
151                         continue;
152                 if (!sac->setlma && sac->lma_adjust == 0)
153                         continue;
154                 lma = s->lma;
155                 if (sac->setlma)
156                         lma = sac->lma;
157                 if (sac->lma_adjust != 0)
158                         lma += sac->lma_adjust;
159                 if (lma == s->lma)
160                         continue;
161
162                 /*
163                  * Check if the LMA change is viable.
164                  *
165                  * 1. Check if the new LMA 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("LMA for section %s: %#jx\n", s->name, lma);
174 #endif
175
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);
180
181                 if (lma < s->lma) {
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,
186                                     (uintmax_t) lma);
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;
190                         else
191                                 end = s->seg->addr + s->seg->msz;
192
193                 } else {
194                         /* Move section to upper address. */
195                         if (s == s->seg->v_sec[0])
196                                 start = lma;
197                         else
198                                 start = s->seg->addr;
199                         end = lma + (s->seg->addr + s->seg->msz - s->lma);
200                         if (end < start)
201                                 errx(EXIT_FAILURE, "Not enough space to move "
202                                     "section %s load address to %#jx", s->name,
203                                     (uintmax_t) lma);
204                 }
205
206 #ifdef  DEBUG
207                 printf("new extent for segment containing %s: (%#jx,%#jx)\n",
208                     s->name, start, end);
209 #endif
210
211                 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
212                         if (seg == s->seg || seg->type != PT_LOAD)
213                                 continue;
214                         if (start > seg->addr + seg->msz)
215                                 continue;
216                         if (end < seg->addr)
217                                 continue;
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);
221                 }
222
223                 /*
224                  * Update section LMA and file offset.
225                  */
226
227                 if (lma < s->lma) {
228                         /*
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.
233                          */
234                         dl = s->lma - lma;
235                         for (i = 0; i < s->seg->nsec; i++) {
236                                 s0 = s->seg->v_sec[i];
237                                 s0->lma -= dl;
238 #ifdef  DEBUG
239                                 printf("section %s LMA set to %#jx\n",
240                                     s0->name, (uintmax_t) s0->lma);
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 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.
260                          */
261                         dl = lma - s->lma;
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->lma += dl;
272 #ifdef  DEBUG
273                                 printf("section %s LMA 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 load address.
295                  */
296
297                 s = NULL;
298                 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
299                         if (seg->type != PT_LOAD)
300                                 continue;
301                         for (i = seg->nsec - 1; i >= 0; i--)
302                                 if (seg->v_sec[i]->type != SHT_NOBITS)
303                                         break;
304                         if (i < 0)
305                                 continue;
306                         if (s == NULL)
307                                 s = seg->v_sec[i];
308                         else {
309                                 s0 = seg->v_sec[i];
310                                 if (s0->lma > s->lma)
311                                         s = s0;
312                         }
313                 }
314
315                 if (s == NULL)
316                         goto issue_warn;
317
318                 /* No need to pad if the pad_to address is lower. */
319                 if (ecp->pad_to <= s->lma + s->sz)
320                         goto issue_warn;
321
322                 s->pad_sz = ecp->pad_to - (s->lma + s->sz);
323 #ifdef  DEBUG
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);
326 #endif
327         }
328
329 issue_warn:
330
331         /*
332          * Issue a warning if there are VMA/LMA adjust requests for
333          * some nonexistent sections.
334          */
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)
339                                 continue;
340                         found = 0;
341                         TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
342                                 if (s->pseudo || s->name == NULL)
343                                         continue;
344                                 if (!strcmp(s->name, sac->name)) {
345                                         found = 1;
346                                         break;
347                                 }
348                         }
349                         if (!found)
350                                 warnx("cannot find section `%s'", sac->name);
351                 }
352         }
353 }
354
355 static void
356 insert_to_inseg_list(struct segment *seg, struct section *sec)
357 {
358         struct section *s;
359         int i;
360
361         seg->nsec++;
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");
365
366         /*
367          * Sort the section in order of offset.
368          */
369
370         for (i = seg->nsec - 1; i > 0; i--) {
371                 s = seg->v_sec[i - 1];
372                 if (sec->off >= s->off) {
373                         seg->v_sec[i] = sec;
374                         break;
375                 } else
376                         seg->v_sec[i] = s;
377         }
378         if (i == 0)
379                 seg->v_sec[0] = sec;
380 }
381
382 void
383 setup_phdr(struct elfcopy *ecp)
384 {
385         struct segment  *seg;
386         GElf_Phdr        iphdr;
387         size_t           iphnum;
388         int              i;
389
390         if (elf_getphnum(ecp->ein, &iphnum) == 0)
391                 errx(EXIT_FAILURE, "elf_getphnum failed: %s",
392                     elf_errmsg(-1));
393
394         ecp->ophnum = ecp->iphnum = iphnum;
395         if (iphnum == 0)
396                 return;
397
398         /* If --only-keep-debug is specified, discard all program headers. */
399         if (ecp->strip == STRIP_NONDEBUG) {
400                 ecp->ophnum = 0;
401                 return;
402         }
403
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",
407                             elf_errmsg(-1));
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);
416         }
417 }
418
419 void
420 copy_phdr(struct elfcopy *ecp)
421 {
422         struct segment  *seg;
423         struct section  *s;
424         GElf_Phdr        iphdr, ophdr;
425         int              i;
426
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);
431                                 if (s->pseudo)
432                                         seg->addr = s->lma +
433                                             gelf_fsize(ecp->eout, ELF_T_EHDR,
434                                                 1, EV_CURRENT);
435                         }
436                         seg->fsz = seg->msz = gelf_fsize(ecp->eout, ELF_T_PHDR,
437                             ecp->ophnum, EV_CURRENT);
438                         continue;
439                 }
440
441                 seg->fsz = seg->msz = 0;
442                 for (i = 0; i < seg->nsec; i++) {
443                         s = seg->v_sec[i];
444                         seg->msz = s->vma + s->sz - seg->addr;
445                         if (s->type != SHT_NOBITS)
446                                 seg->fsz = seg->msz;
447                 }
448         }
449
450         /*
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.
454          */
455         if (gelf_newphdr(ecp->eout, ecp->ophnum) == NULL)
456                 errx(EXIT_FAILURE, "gelf_newphdr() failed: %s",
457                     elf_errmsg(-1));
458
459         /*
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.
463          */
464         if (elf_update(ecp->eout, ELF_C_NULL) < 0)
465                 errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1));
466
467         /*
468          * iphnum == ophnum, since we don't remove program headers even if
469          * they no longer contain sections.
470          */
471         i = 0;
472         STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
473                 if (i >= ecp->iphnum)
474                         break;
475                 if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr)
476                         errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
477                             elf_errmsg(-1));
478                 if (gelf_getphdr(ecp->eout, i, &ophdr) != &ophdr)
479                         errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
480                             elf_errmsg(-1));
481
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",
492                             elf_errmsg(-1));
493
494                 i++;
495         }
496 }