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