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 3449 2016-05-03 13:59:29Z 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->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 83 insert_to_inseg_list(seg, s); 84 if (seg->type == PT_LOAD) 85 s->seg = seg; 86 else if (seg->type == PT_TLS) 87 s->seg_tls = seg; 88 if (s->pseudo) 89 s->vma = seg->vaddr + (s->off - seg->off); 90 if (seg->paddr > 0) 91 s->lma = seg->paddr + (s->off - seg->off); 92 else 93 s->lma = 0; 94 loadable = 1; 95 } 96 97 return (loadable); 98 } 99 100 void 101 adjust_addr(struct elfcopy *ecp) 102 { 103 struct section *s, *s0; 104 struct segment *seg; 105 struct sec_action *sac; 106 uint64_t dl, vma, lma, start, end; 107 int found, i; 108 109 /* 110 * Apply VMA and global LMA changes in the first iteration. 111 */ 112 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { 113 114 /* Only adjust loadable section's address. */ 115 if (!s->loadable) 116 continue; 117 118 /* Apply global VMA adjustment. */ 119 if (ecp->change_addr != 0) 120 s->vma += ecp->change_addr; 121 122 /* Apply global LMA adjustment. */ 123 if (ecp->change_addr != 0 && s->seg != NULL && 124 s->seg->paddr > 0) 125 s->lma += ecp->change_addr; 126 } 127 128 /* 129 * Apply sections VMA change in the second iteration. 130 */ 131 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { 132 133 if (!s->loadable) 134 continue; 135 136 /* 137 * Check if there is a VMA change request for this 138 * section. 139 */ 140 sac = lookup_sec_act(ecp, s->name, 0); 141 if (sac == NULL) 142 continue; 143 vma = s->vma; 144 if (sac->setvma) 145 vma = sac->vma; 146 if (sac->vma_adjust != 0) 147 vma += sac->vma_adjust; 148 if (vma == s->vma) 149 continue; 150 151 /* 152 * No need to make segment adjustment if the section doesn't 153 * belong to any segment. 154 */ 155 if (s->seg == NULL) { 156 s->vma = vma; 157 continue; 158 } 159 160 /* 161 * Check if the VMA change is viable. 162 * 163 * 1. Check if the new VMA is properly aligned accroding to 164 * section alignment. 165 * 166 * 2. Compute the new extent of segment that contains this 167 * section, make sure it doesn't overlap with other 168 * segments. 169 */ 170 #ifdef DEBUG 171 printf("VMA for section %s: %#jx\n", s->name, vma); 172 #endif 173 174 if (vma % s->align != 0) 175 errx(EXIT_FAILURE, "The VMA %#jx for " 176 "section %s is not aligned to %ju", 177 (uintmax_t) vma, s->name, (uintmax_t) s->align); 178 179 if (vma < s->vma) { 180 /* Move section to lower address. */ 181 if (vma < s->vma - s->seg->vaddr) 182 errx(EXIT_FAILURE, "Not enough space to move " 183 "section %s VMA to %#jx", s->name, 184 (uintmax_t) vma); 185 start = vma - (s->vma - s->seg->vaddr); 186 if (s == s->seg->v_sec[s->seg->nsec - 1]) 187 end = start + s->seg->msz; 188 else 189 end = s->seg->vaddr + s->seg->msz; 190 } else { 191 /* Move section to upper address. */ 192 if (s == s->seg->v_sec[0]) 193 start = vma; 194 else 195 start = s->seg->vaddr; 196 end = vma + (s->seg->vaddr + s->seg->msz - s->vma); 197 if (end < start) 198 errx(EXIT_FAILURE, "Not enough space to move " 199 "section %s VMA to %#jx", s->name, 200 (uintmax_t) vma); 201 } 202 203 #ifdef DEBUG 204 printf("new extent for segment containing %s: (%#jx,%#jx)\n", 205 s->name, start, end); 206 #endif 207 208 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { 209 if (seg == s->seg || seg->type != PT_LOAD) 210 continue; 211 if (start > seg->vaddr + seg->msz) 212 continue; 213 if (end < seg->vaddr) 214 continue; 215 errx(EXIT_FAILURE, "The extent of segment containing " 216 "section %s overlaps with segment(%#jx,%#jx)", 217 s->name, (uintmax_t) seg->vaddr, 218 (uintmax_t) (seg->vaddr + seg->msz)); 219 } 220 221 /* 222 * Update section VMA and file offset. 223 */ 224 225 if (vma < s->vma) { 226 /* 227 * To move a section to lower VMA, we decrease 228 * the VMA of the section and all the sections that 229 * are before it, and we increase the file offsets 230 * of all the sections that are after it. 231 */ 232 dl = s->vma - vma; 233 for (i = 0; i < s->seg->nsec; i++) { 234 s0 = s->seg->v_sec[i]; 235 s0->vma -= dl; 236 #ifdef DEBUG 237 printf("section %s VMA set to %#jx\n", 238 s0->name, (uintmax_t) s0->vma); 239 #endif 240 if (s0 == s) 241 break; 242 } 243 for (i = i + 1; i < s->seg->nsec; i++) { 244 s0 = s->seg->v_sec[i]; 245 s0->off += dl; 246 #ifdef DEBUG 247 printf("section %s offset set to %#jx\n", 248 s0->name, (uintmax_t) s0->off); 249 #endif 250 } 251 } else { 252 /* 253 * To move a section to upper VMA, we increase 254 * the VMA of the section and all the sections that 255 * are after it, and we increase the their file 256 * offsets too unless the section in question 257 * is the first in its containing segment. 258 */ 259 dl = vma - s->vma; 260 for (i = 0; i < s->seg->nsec; i++) 261 if (s->seg->v_sec[i] == s) 262 break; 263 if (i >= s->seg->nsec) 264 errx(EXIT_FAILURE, "Internal: section `%s' not" 265 " found in its containing segement", 266 s->name); 267 for (; i < s->seg->nsec; i++) { 268 s0 = s->seg->v_sec[i]; 269 s0->vma += dl; 270 #ifdef DEBUG 271 printf("section %s VMA set to %#jx\n", 272 s0->name, (uintmax_t) s0->lma); 273 #endif 274 if (s != s->seg->v_sec[0]) { 275 s0->off += dl; 276 #ifdef DEBUG 277 printf("section %s offset set to %#jx\n", 278 s0->name, (uintmax_t) s0->off); 279 #endif 280 } 281 } 282 } 283 } 284 285 /* 286 * Apply load address padding. 287 */ 288 289 if (ecp->pad_to != 0) { 290 291 /* 292 * Find the section with highest VMA. 293 */ 294 s = NULL; 295 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { 296 if (seg->type != PT_LOAD) 297 continue; 298 for (i = seg->nsec - 1; i >= 0; i--) 299 if (seg->v_sec[i]->type != SHT_NOBITS) 300 break; 301 if (i < 0) 302 continue; 303 if (s == NULL) 304 s = seg->v_sec[i]; 305 else { 306 s0 = seg->v_sec[i]; 307 if (s0->vma > s->vma) 308 s = s0; 309 } 310 } 311 312 if (s == NULL) 313 goto adjust_lma; 314 315 /* No need to pad if the pad_to address is lower. */ 316 if (ecp->pad_to <= s->vma + s->sz) 317 goto adjust_lma; 318 319 s->pad_sz = ecp->pad_to - (s->vma + s->sz); 320 #ifdef DEBUG 321 printf("pad section %s VMA to address %#jx by %#jx\n", s->name, 322 (uintmax_t) ecp->pad_to, (uintmax_t) s->pad_sz); 323 #endif 324 } 325 326 327 adjust_lma: 328 329 /* 330 * Apply sections LMA change in the third iteration. 331 */ 332 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { 333 334 /* 335 * Only loadable section that's inside a segment can have 336 * LMA adjusted. Also, if LMA of the containing segment is 337 * set to 0, it probably means we should ignore the LMA. 338 */ 339 if (!s->loadable || s->seg == NULL || s->seg->paddr == 0) 340 continue; 341 342 /* 343 * Check if there is a LMA change request for this 344 * section. 345 */ 346 sac = lookup_sec_act(ecp, s->name, 0); 347 if (sac == NULL) 348 continue; 349 if (!sac->setlma && sac->lma_adjust == 0) 350 continue; 351 lma = s->lma; 352 if (sac->setlma) 353 lma = sac->lma; 354 if (sac->lma_adjust != 0) 355 lma += sac->lma_adjust; 356 if (lma == s->lma) 357 continue; 358 359 #ifdef DEBUG 360 printf("LMA for section %s: %#jx\n", s->name, lma); 361 #endif 362 363 /* Check alignment. */ 364 if (lma % s->align != 0) 365 errx(EXIT_FAILURE, "The LMA %#jx for " 366 "section %s is not aligned to %ju", 367 (uintmax_t) lma, s->name, (uintmax_t) s->align); 368 369 /* 370 * Update section LMA. 371 */ 372 373 if (lma < s->lma) { 374 /* 375 * To move a section to lower LMA, we decrease 376 * the LMA of the section and all the sections that 377 * are before it. 378 */ 379 dl = s->lma - lma; 380 for (i = 0; i < s->seg->nsec; i++) { 381 s0 = s->seg->v_sec[i]; 382 s0->lma -= dl; 383 #ifdef DEBUG 384 printf("section %s LMA set to %#jx\n", 385 s0->name, (uintmax_t) s0->lma); 386 #endif 387 if (s0 == s) 388 break; 389 } 390 } else { 391 /* 392 * To move a section to upper LMA, we increase 393 * the LMA of the section and all the sections that 394 * are after it. 395 */ 396 dl = lma - s->lma; 397 for (i = 0; i < s->seg->nsec; i++) 398 if (s->seg->v_sec[i] == s) 399 break; 400 if (i >= s->seg->nsec) 401 errx(EXIT_FAILURE, "Internal: section `%s' not" 402 " found in its containing segement", 403 s->name); 404 for (; i < s->seg->nsec; i++) { 405 s0 = s->seg->v_sec[i]; 406 s0->lma += dl; 407 #ifdef DEBUG 408 printf("section %s LMA set to %#jx\n", 409 s0->name, (uintmax_t) s0->lma); 410 #endif 411 } 412 } 413 } 414 415 /* 416 * Issue a warning if there are VMA/LMA adjust requests for 417 * some nonexistent sections. 418 */ 419 if ((ecp->flags & NO_CHANGE_WARN) == 0) { 420 STAILQ_FOREACH(sac, &ecp->v_sac, sac_list) { 421 if (!sac->setvma && !sac->setlma && 422 !sac->vma_adjust && !sac->lma_adjust) 423 continue; 424 found = 0; 425 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { 426 if (s->pseudo || s->name == NULL) 427 continue; 428 if (!strcmp(s->name, sac->name)) { 429 found = 1; 430 break; 431 } 432 } 433 if (!found) 434 warnx("cannot find section `%s'", sac->name); 435 } 436 } 437 } 438 439 static void 440 insert_to_inseg_list(struct segment *seg, struct section *sec) 441 { 442 struct section *s; 443 int i; 444 445 seg->nsec++; 446 seg->v_sec = realloc(seg->v_sec, seg->nsec * sizeof(*seg->v_sec)); 447 if (seg->v_sec == NULL) 448 err(EXIT_FAILURE, "realloc failed"); 449 450 /* 451 * Sort the section in order of offset. 452 */ 453 454 for (i = seg->nsec - 1; i > 0; i--) { 455 s = seg->v_sec[i - 1]; 456 if (sec->off >= s->off) { 457 seg->v_sec[i] = sec; 458 break; 459 } else 460 seg->v_sec[i] = s; 461 } 462 if (i == 0) 463 seg->v_sec[0] = sec; 464 } 465 466 void 467 setup_phdr(struct elfcopy *ecp) 468 { 469 struct segment *seg; 470 GElf_Phdr iphdr; 471 size_t iphnum, i; 472 473 if (elf_getphnum(ecp->ein, &iphnum) == 0) 474 errx(EXIT_FAILURE, "elf_getphnum failed: %s", 475 elf_errmsg(-1)); 476 477 ecp->ophnum = ecp->iphnum = iphnum; 478 if (iphnum == 0) 479 return; 480 481 /* If --only-keep-debug is specified, discard all program headers. */ 482 if (ecp->strip == STRIP_NONDEBUG) { 483 ecp->ophnum = 0; 484 return; 485 } 486 487 for (i = 0; i < iphnum; i++) { 488 if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr) 489 errx(EXIT_FAILURE, "gelf_getphdr failed: %s", 490 elf_errmsg(-1)); 491 if ((seg = calloc(1, sizeof(*seg))) == NULL) 492 err(EXIT_FAILURE, "calloc failed"); 493 seg->vaddr = iphdr.p_vaddr; 494 seg->paddr = iphdr.p_paddr; 495 seg->off = iphdr.p_offset; 496 seg->fsz = iphdr.p_filesz; 497 seg->msz = iphdr.p_memsz; 498 seg->type = iphdr.p_type; 499 STAILQ_INSERT_TAIL(&ecp->v_seg, seg, seg_list); 500 } 501 } 502 503 void 504 copy_phdr(struct elfcopy *ecp) 505 { 506 struct segment *seg; 507 struct section *s; 508 GElf_Phdr iphdr, ophdr; 509 int i; 510 511 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { 512 if (seg->type == PT_PHDR) { 513 if (!TAILQ_EMPTY(&ecp->v_sec)) { 514 s = TAILQ_FIRST(&ecp->v_sec); 515 if (s->pseudo) { 516 seg->vaddr = s->vma + 517 gelf_fsize(ecp->eout, ELF_T_EHDR, 518 1, EV_CURRENT); 519 seg->paddr = s->lma + 520 gelf_fsize(ecp->eout, ELF_T_EHDR, 521 1, EV_CURRENT); 522 } 523 } 524 seg->fsz = seg->msz = gelf_fsize(ecp->eout, ELF_T_PHDR, 525 ecp->ophnum, EV_CURRENT); 526 continue; 527 } 528 529 if (seg->nsec > 0) { 530 s = seg->v_sec[0]; 531 seg->vaddr = s->vma; 532 seg->paddr = s->lma; 533 } 534 535 seg->fsz = seg->msz = 0; 536 for (i = 0; i < seg->nsec; i++) { 537 s = seg->v_sec[i]; 538 seg->msz = s->vma + s->sz - seg->vaddr; 539 if (s->type != SHT_NOBITS) 540 seg->fsz = s->off + s->sz - seg->off; 541 } 542 } 543 544 /* 545 * Allocate space for program headers, note that libelf keep 546 * track of the number in internal variable, and a call to 547 * elf_update is needed to update e_phnum of ehdr. 548 */ 549 if (gelf_newphdr(ecp->eout, ecp->ophnum) == NULL) 550 errx(EXIT_FAILURE, "gelf_newphdr() failed: %s", 551 elf_errmsg(-1)); 552 553 /* 554 * This elf_update() call is to update the e_phnum field in 555 * ehdr. It's necessary because later we will call gelf_getphdr(), 556 * which does sanity check by comparing ndx argument with e_phnum. 557 */ 558 if (elf_update(ecp->eout, ELF_C_NULL) < 0) 559 errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1)); 560 561 /* 562 * iphnum == ophnum, since we don't remove program headers even if 563 * they no longer contain sections. 564 */ 565 i = 0; 566 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { 567 if (i >= ecp->iphnum) 568 break; 569 if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr) 570 errx(EXIT_FAILURE, "gelf_getphdr failed: %s", 571 elf_errmsg(-1)); 572 if (gelf_getphdr(ecp->eout, i, &ophdr) != &ophdr) 573 errx(EXIT_FAILURE, "gelf_getphdr failed: %s", 574 elf_errmsg(-1)); 575 576 ophdr.p_type = iphdr.p_type; 577 ophdr.p_vaddr = seg->vaddr; 578 ophdr.p_paddr = seg->paddr; 579 ophdr.p_flags = iphdr.p_flags; 580 ophdr.p_align = iphdr.p_align; 581 ophdr.p_offset = seg->off; 582 ophdr.p_filesz = seg->fsz; 583 ophdr.p_memsz = seg->msz; 584 if (!gelf_update_phdr(ecp->eout, i, &ophdr)) 585 errx(EXIT_FAILURE, "gelf_update_phdr failed: %s", 586 elf_errmsg(-1)); 587 588 i++; 589 } 590 } 591