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/cdefs.h> 28 #include <sys/queue.h> 29 #include <err.h> 30 #include <gelf.h> 31 #include <stdint.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 36 #include "elfcopy.h" 37 38 ELFTC_VCSID("$Id: segments.c 2542 2012-08-12 16:14:15Z kaiwang27 $"); 39 40 static void insert_to_inseg_list(struct segment *seg, struct section *sec); 41 42 /* 43 * elfcopy's segment handling is relatively simpler and less powerful than 44 * libbfd. Program headers are modified or copied from input to output objects, 45 * but never re-generated. As a result, if the input object has incorrect 46 * program headers, the output object's program headers will remain incorrect 47 * or become even worse. 48 */ 49 50 /* 51 * Check whether a section is "loadable". If so, add it to the 52 * corresponding segment list(s) and return 1. 53 */ 54 int 55 add_to_inseg_list(struct elfcopy *ecp, struct section *s) 56 { 57 struct segment *seg; 58 int loadable; 59 60 if (ecp->ophnum == 0) 61 return (0); 62 63 /* 64 * Segment is a different view of an ELF object. One segment can 65 * contain one or more sections, and one section can be included 66 * in one or more segments, or not included in any segment at all. 67 * We call those sections which can be found in one or more segments 68 * "loadable" sections, and call the rest "unloadable" sections. 69 * We keep track of "loadable" sections in their containing 70 * segment(s)' v_sec queue. These information are later used to 71 * recalculate the extents of segments, when sections are removed, 72 * for example. 73 */ 74 loadable = 0; 75 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { 76 if (s->off < seg->off) 77 continue; 78 if (s->off + s->sz > seg->off + seg->fsz && 79 s->type != SHT_NOBITS) 80 continue; 81 if (s->off + s->sz > seg->off + seg->msz) 82 continue; 83 84 insert_to_inseg_list(seg, s); 85 if (seg->type == PT_LOAD) 86 s->seg = seg; 87 s->lma = seg->addr + (s->off - seg->off); 88 loadable = 1; 89 } 90 91 return (loadable); 92 } 93 94 void 95 adjust_addr(struct elfcopy *ecp) 96 { 97 struct section *s, *s0; 98 struct segment *seg; 99 struct sec_action *sac; 100 uint64_t dl, lma, old_vma, start, end; 101 int found, i; 102 103 /* 104 * Apply VMA and global LMA changes in the first iteration. 105 */ 106 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { 107 108 /* Only adjust loadable section's address. */ 109 if (!s->loadable || s->seg == NULL) 110 continue; 111 112 /* Apply global LMA adjustment. */ 113 if (ecp->change_addr != 0) 114 s->lma += ecp->change_addr; 115 116 if (!s->pseudo) { 117 old_vma = s->vma; 118 119 /* Apply global VMA adjustment. */ 120 if (ecp->change_addr != 0) 121 s->vma += ecp->change_addr; 122 123 /* Apply section VMA adjustment. */ 124 sac = lookup_sec_act(ecp, s->name, 0); 125 if (sac == NULL) 126 continue; 127 if (sac->setvma) 128 s->vma = sac->vma; 129 if (sac->vma_adjust != 0) 130 s->vma += sac->vma_adjust; 131 } 132 } 133 134 /* 135 * Apply sections LMA change in the second iteration. 136 */ 137 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { 138 139 /* Only adjust loadable section's LMA. */ 140 if (!s->loadable || s->seg == NULL) 141 continue; 142 143 /* 144 * Check if there is a LMA change request for this 145 * section. 146 */ 147 sac = lookup_sec_act(ecp, s->name, 0); 148 if (sac == NULL) 149 continue; 150 if (!sac->setlma && sac->lma_adjust == 0) 151 continue; 152 lma = s->lma; 153 if (sac->setlma) 154 lma = sac->lma; 155 if (sac->lma_adjust != 0) 156 lma += sac->lma_adjust; 157 if (lma == s->lma) 158 continue; 159 160 /* 161 * Check if the LMA change is viable. 162 * 163 * 1. Check if the new LMA 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("LMA for section %s: %#jx\n", s->name, lma); 172 #endif 173 174 if (lma % s->align != 0) 175 errx(EXIT_FAILURE, "The load address %#jx for " 176 "section %s is not aligned to %ju", 177 (uintmax_t) lma, s->name, s->align); 178 179 if (lma < s->lma) { 180 /* Move section to lower address. */ 181 if (lma < s->lma - s->seg->addr) 182 errx(EXIT_FAILURE, "Not enough space to move " 183 "section %s load address to %#jx", s->name, 184 (uintmax_t) lma); 185 start = lma - (s->lma - s->seg->addr); 186 if (s == s->seg->v_sec[s->seg->nsec - 1]) 187 end = start + s->seg->msz; 188 else 189 end = s->seg->addr + s->seg->msz; 190 191 } else { 192 /* Move section to upper address. */ 193 if (s == s->seg->v_sec[0]) 194 start = lma; 195 else 196 start = s->seg->addr; 197 end = lma + (s->seg->addr + s->seg->msz - s->lma); 198 if (end < start) 199 errx(EXIT_FAILURE, "Not enough space to move " 200 "section %s load address to %#jx", s->name, 201 (uintmax_t) lma); 202 } 203 204 #ifdef DEBUG 205 printf("new extent for segment containing %s: (%#jx,%#jx)\n", 206 s->name, start, end); 207 #endif 208 209 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { 210 if (seg == s->seg || seg->type != PT_LOAD) 211 continue; 212 if (start > seg->addr + seg->msz) 213 continue; 214 if (end < seg->addr) 215 continue; 216 errx(EXIT_FAILURE, "The extent of segment containing " 217 "section %s overlaps with segment(%#jx,%#jx)", 218 s->name, seg->addr, seg->addr + seg->msz); 219 } 220 221 /* 222 * Update section LMA and file offset. 223 */ 224 225 if (lma < s->lma) { 226 /* 227 * To move a section to lower load address, we decrease 228 * the load addresses of the section and all the 229 * sections that are before it, and we increase the 230 * file offsets of all the sections that are after it. 231 */ 232 dl = s->lma - lma; 233 for (i = 0; i < s->seg->nsec; i++) { 234 s0 = s->seg->v_sec[i]; 235 s0->lma -= dl; 236 #ifdef DEBUG 237 printf("section %s LMA set to %#jx\n", 238 s0->name, (uintmax_t) s0->lma); 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 load address, we increase 254 * the load addresses of the section and all the 255 * sections that are after it, and we increase the 256 * their file offsets too unless the section in question 257 * is the first in its containing segment. 258 */ 259 dl = lma - s->lma; 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->lma += dl; 270 #ifdef DEBUG 271 printf("section %s LMA 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 load address. 293 */ 294 295 s = NULL; 296 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { 297 if (seg->type != PT_LOAD) 298 continue; 299 for (i = seg->nsec - 1; i >= 0; i--) 300 if (seg->v_sec[i]->type != SHT_NOBITS) 301 break; 302 if (i < 0) 303 continue; 304 if (s == NULL) 305 s = seg->v_sec[i]; 306 else { 307 s0 = seg->v_sec[i]; 308 if (s0->lma > s->lma) 309 s = s0; 310 } 311 } 312 313 if (s == NULL) 314 goto issue_warn; 315 316 /* No need to pad if the pad_to address is lower. */ 317 if (ecp->pad_to <= s->lma + s->sz) 318 goto issue_warn; 319 320 s->pad_sz = ecp->pad_to - (s->lma + s->sz); 321 #ifdef DEBUG 322 printf("pad section %s load to address %#jx by %#jx\n", s->name, 323 (uintmax_t) ecp->pad_to, (uintmax_t) s->pad_sz); 324 #endif 325 } 326 327 issue_warn: 328 329 /* 330 * Issue a warning if there are VMA/LMA adjust requests for 331 * some nonexistent sections. 332 */ 333 if ((ecp->flags & NO_CHANGE_WARN) == 0) { 334 STAILQ_FOREACH(sac, &ecp->v_sac, sac_list) { 335 if (!sac->setvma && !sac->setlma && 336 !sac->vma_adjust && !sac->lma_adjust) 337 continue; 338 found = 0; 339 TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { 340 if (s->pseudo || s->name == NULL) 341 continue; 342 if (!strcmp(s->name, sac->name)) { 343 found = 1; 344 break; 345 } 346 } 347 if (!found) 348 warnx("cannot find section `%s'", sac->name); 349 } 350 } 351 } 352 353 static void 354 insert_to_inseg_list(struct segment *seg, struct section *sec) 355 { 356 struct section *s; 357 int i; 358 359 seg->nsec++; 360 seg->v_sec = realloc(seg->v_sec, seg->nsec * sizeof(*seg->v_sec)); 361 if (seg->v_sec == NULL) 362 err(EXIT_FAILURE, "realloc failed"); 363 364 /* 365 * Sort the section in order of offset. 366 */ 367 368 for (i = seg->nsec - 1; i > 0; i--) { 369 s = seg->v_sec[i - 1]; 370 if (sec->off >= s->off) { 371 seg->v_sec[i] = sec; 372 break; 373 } else 374 seg->v_sec[i] = s; 375 } 376 if (i == 0) 377 seg->v_sec[0] = sec; 378 } 379 380 void 381 setup_phdr(struct elfcopy *ecp) 382 { 383 struct segment *seg; 384 GElf_Phdr iphdr; 385 size_t iphnum; 386 int i; 387 388 if (elf_getphnum(ecp->ein, &iphnum) == 0) 389 errx(EXIT_FAILURE, "elf_getphnum failed: %s", 390 elf_errmsg(-1)); 391 392 ecp->ophnum = ecp->iphnum = iphnum; 393 if (iphnum == 0) 394 return; 395 396 /* If --only-keep-debug is specified, discard all program headers. */ 397 if (ecp->strip == STRIP_NONDEBUG) { 398 ecp->ophnum = 0; 399 return; 400 } 401 402 for (i = 0; (size_t)i < iphnum; i++) { 403 if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr) 404 errx(EXIT_FAILURE, "gelf_getphdr failed: %s", 405 elf_errmsg(-1)); 406 if ((seg = calloc(1, sizeof(*seg))) == NULL) 407 err(EXIT_FAILURE, "calloc failed"); 408 seg->addr = iphdr.p_vaddr; 409 seg->off = iphdr.p_offset; 410 seg->fsz = iphdr.p_filesz; 411 seg->msz = iphdr.p_memsz; 412 seg->type = iphdr.p_type; 413 STAILQ_INSERT_TAIL(&ecp->v_seg, seg, seg_list); 414 } 415 } 416 417 void 418 copy_phdr(struct elfcopy *ecp) 419 { 420 struct segment *seg; 421 struct section *s; 422 GElf_Phdr iphdr, ophdr; 423 int i; 424 425 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { 426 if (seg->type == PT_PHDR) { 427 if (!TAILQ_EMPTY(&ecp->v_sec)) { 428 s = TAILQ_FIRST(&ecp->v_sec); 429 if (s->pseudo) 430 seg->addr = s->lma + 431 gelf_fsize(ecp->eout, ELF_T_EHDR, 432 1, EV_CURRENT); 433 } 434 seg->fsz = seg->msz = gelf_fsize(ecp->eout, ELF_T_PHDR, 435 ecp->ophnum, EV_CURRENT); 436 continue; 437 } 438 439 seg->fsz = seg->msz = 0; 440 for (i = 0; i < seg->nsec; i++) { 441 s = seg->v_sec[i]; 442 seg->msz = s->vma + s->sz - seg->addr; 443 if (s->type != SHT_NOBITS) 444 seg->fsz = seg->msz; 445 } 446 } 447 448 /* 449 * Allocate space for program headers, note that libelf keep 450 * track of the number in internal variable, and a call to 451 * elf_update is needed to update e_phnum of ehdr. 452 */ 453 if (gelf_newphdr(ecp->eout, ecp->ophnum) == NULL) 454 errx(EXIT_FAILURE, "gelf_newphdr() failed: %s", 455 elf_errmsg(-1)); 456 457 /* 458 * This elf_update() call is to update the e_phnum field in 459 * ehdr. It's necessary because later we will call gelf_getphdr(), 460 * which does sanity check by comparing ndx argument with e_phnum. 461 */ 462 if (elf_update(ecp->eout, ELF_C_NULL) < 0) 463 errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1)); 464 465 /* 466 * iphnum == ophnum, since we don't remove program headers even if 467 * they no longer contain sections. 468 */ 469 i = 0; 470 STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { 471 if (i >= ecp->iphnum) 472 break; 473 if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr) 474 errx(EXIT_FAILURE, "gelf_getphdr failed: %s", 475 elf_errmsg(-1)); 476 if (gelf_getphdr(ecp->eout, i, &ophdr) != &ophdr) 477 errx(EXIT_FAILURE, "gelf_getphdr failed: %s", 478 elf_errmsg(-1)); 479 480 ophdr.p_type = iphdr.p_type; 481 ophdr.p_vaddr = seg->addr; 482 ophdr.p_paddr = seg->addr; 483 ophdr.p_flags = iphdr.p_flags; 484 ophdr.p_align = iphdr.p_align; 485 ophdr.p_offset = seg->off; 486 ophdr.p_filesz = seg->fsz; 487 ophdr.p_memsz = seg->msz; 488 if (!gelf_update_phdr(ecp->eout, i, &ophdr)) 489 err(EXIT_FAILURE, "gelf_update_phdr failed :%s", 490 elf_errmsg(-1)); 491 492 i++; 493 } 494 } 495