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