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