]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/elftoolchain/elfcopy/segments.c
Update nvi to 2.1.3 which fixes the data corruption when locale conversion
[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 3177 2015-03-30 18:19:41Z 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 = seg->msz;
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 }