Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/elftoolchain/elfcopy/segments.c
39507 views
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
}
593
594