]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/elftoolchain/elfcopy/segments.c
Update to bmake-20200902
[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/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 3615 2018-05-17 04:12:24Z kaiwang27 $");
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->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
102 void
103 adjust_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
329 adjust_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
441 static void
442 insert_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
468 void
469 setup_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
505 void
506 copy_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 }