1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Implementation notes: 6 // 7 // We need to remove a piece from the ELF shared library. However, we also 8 // want to avoid fixing DWARF cfi data and relative relocation addresses. 9 // So after packing we shift offets and starting address of the RX segment 10 // while preserving code/data vaddrs location. 11 // This requires some fixups for symtab/hash/gnu_hash dynamic section addresses. 12 13 #include "elf_file.h" 14 15 #include <stdlib.h> 16 #include <sys/types.h> 17 #include <unistd.h> 18 #include <algorithm> 19 #include <string> 20 #include <vector> 21 22 #include "debug.h" 23 #include "elf_traits.h" 24 #include "libelf.h" 25 #include "packer.h" 26 27 namespace relocation_packer { 28 29 // Out-of-band dynamic tags used to indicate the offset and size of the 30 // android packed relocations section. 31 static constexpr int32_t DT_ANDROID_REL = DT_LOOS + 2; 32 static constexpr int32_t DT_ANDROID_RELSZ = DT_LOOS + 3; 33 34 static constexpr int32_t DT_ANDROID_RELA = DT_LOOS + 4; 35 static constexpr int32_t DT_ANDROID_RELASZ = DT_LOOS + 5; 36 37 static constexpr uint32_t SHT_ANDROID_REL = SHT_LOOS + 1; 38 static constexpr uint32_t SHT_ANDROID_RELA = SHT_LOOS + 2; 39 40 static const size_t kPageSize = 4096; 41 42 // Alignment to preserve, in bytes. This must be at least as large as the 43 // largest d_align and sh_addralign values found in the loaded file. 44 // Out of caution for RELRO page alignment, we preserve to a complete target 45 // page. See http://www.airs.com/blog/archives/189. 46 static const size_t kPreserveAlignment = kPageSize; 47 48 // Get section data. Checks that the section has exactly one data entry, 49 // so that the section size and the data size are the same. True in 50 // practice for all sections we resize when packing or unpacking. Done 51 // by ensuring that a call to elf_getdata(section, data) returns NULL as 52 // the next data entry. 53 static Elf_Data* GetSectionData(Elf_Scn* section) { 54 Elf_Data* data = elf_getdata(section, NULL); 55 CHECK(data && elf_getdata(section, data) == NULL); 56 return data; 57 } 58 59 // Rewrite section data. Allocates new data and makes it the data element's 60 // buffer. Relies on program exit to free allocated data. 61 static void RewriteSectionData(Elf_Scn* section, 62 const void* section_data, 63 size_t size) { 64 Elf_Data* data = GetSectionData(section); 65 CHECK(size == data->d_size); 66 uint8_t* area = new uint8_t[size]; 67 memcpy(area, section_data, size); 68 data->d_buf = area; 69 } 70 71 // Verbose ELF header logging. 72 template <typename Ehdr> 73 static void VerboseLogElfHeader(const Ehdr* elf_header) { 74 VLOG(1) << "e_phoff = " << elf_header->e_phoff; 75 VLOG(1) << "e_shoff = " << elf_header->e_shoff; 76 VLOG(1) << "e_ehsize = " << elf_header->e_ehsize; 77 VLOG(1) << "e_phentsize = " << elf_header->e_phentsize; 78 VLOG(1) << "e_phnum = " << elf_header->e_phnum; 79 VLOG(1) << "e_shnum = " << elf_header->e_shnum; 80 VLOG(1) << "e_shstrndx = " << elf_header->e_shstrndx; 81 } 82 83 // Verbose ELF program header logging. 84 template <typename Phdr> 85 static void VerboseLogProgramHeader(size_t program_header_index, 86 const Phdr* program_header) { 87 std::string type; 88 switch (program_header->p_type) { 89 case PT_NULL: type = "NULL"; break; 90 case PT_LOAD: type = "LOAD"; break; 91 case PT_DYNAMIC: type = "DYNAMIC"; break; 92 case PT_INTERP: type = "INTERP"; break; 93 case PT_PHDR: type = "PHDR"; break; 94 case PT_GNU_RELRO: type = "GNU_RELRO"; break; 95 case PT_GNU_STACK: type = "GNU_STACK"; break; 96 case PT_ARM_EXIDX: type = "EXIDX"; break; 97 default: type = "(OTHER)"; break; 98 } 99 VLOG(1) << "phdr[" << program_header_index << "] : " << type; 100 VLOG(1) << " p_offset = " << program_header->p_offset; 101 VLOG(1) << " p_vaddr = " << program_header->p_vaddr; 102 VLOG(1) << " p_paddr = " << program_header->p_paddr; 103 VLOG(1) << " p_filesz = " << program_header->p_filesz; 104 VLOG(1) << " p_memsz = " << program_header->p_memsz; 105 VLOG(1) << " p_flags = " << program_header->p_flags; 106 VLOG(1) << " p_align = " << program_header->p_align; 107 } 108 109 // Verbose ELF section header logging. 110 template <typename Shdr> 111 static void VerboseLogSectionHeader(const std::string& section_name, 112 const Shdr* section_header) { 113 VLOG(1) << "section " << section_name; 114 VLOG(1) << " sh_addr = " << section_header->sh_addr; 115 VLOG(1) << " sh_offset = " << section_header->sh_offset; 116 VLOG(1) << " sh_size = " << section_header->sh_size; 117 VLOG(1) << " sh_entsize = " << section_header->sh_entsize; 118 VLOG(1) << " sh_addralign = " << section_header->sh_addralign; 119 } 120 121 // Verbose ELF section data logging. 122 static void VerboseLogSectionData(const Elf_Data* data) { 123 VLOG(1) << " data"; 124 VLOG(1) << " d_buf = " << data->d_buf; 125 VLOG(1) << " d_off = " << data->d_off; 126 VLOG(1) << " d_size = " << data->d_size; 127 VLOG(1) << " d_align = " << data->d_align; 128 } 129 130 // Load the complete ELF file into a memory image in libelf, and identify 131 // the .rel.dyn or .rela.dyn, .dynamic, and .android.rel.dyn or 132 // .android.rela.dyn sections. No-op if the ELF file has already been loaded. 133 template <typename ELF> 134 bool ElfFile<ELF>::Load() { 135 if (elf_) 136 return true; 137 138 Elf* elf = elf_begin(fd_, ELF_C_RDWR, NULL); 139 CHECK(elf); 140 141 if (elf_kind(elf) != ELF_K_ELF) { 142 LOG(ERROR) << "File not in ELF format"; 143 return false; 144 } 145 146 auto elf_header = ELF::getehdr(elf); 147 if (!elf_header) { 148 LOG(ERROR) << "Failed to load ELF header: " << elf_errmsg(elf_errno()); 149 return false; 150 } 151 152 if (elf_header->e_type != ET_DYN) { 153 LOG(ERROR) << "ELF file is not a shared object"; 154 return false; 155 } 156 157 // Require that our endianness matches that of the target, and that both 158 // are little-endian. Safe for all current build/target combinations. 159 const int endian = elf_header->e_ident[EI_DATA]; 160 CHECK(endian == ELFDATA2LSB); 161 CHECK(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__); 162 163 const int file_class = elf_header->e_ident[EI_CLASS]; 164 VLOG(1) << "endian = " << endian << ", file class = " << file_class; 165 VerboseLogElfHeader(elf_header); 166 167 auto elf_program_header = ELF::getphdr(elf); 168 CHECK(elf_program_header != nullptr); 169 170 const typename ELF::Phdr* dynamic_program_header = NULL; 171 for (size_t i = 0; i < elf_header->e_phnum; ++i) { 172 auto program_header = &elf_program_header[i]; 173 VerboseLogProgramHeader(i, program_header); 174 175 if (program_header->p_type == PT_DYNAMIC) { 176 CHECK(dynamic_program_header == NULL); 177 dynamic_program_header = program_header; 178 } 179 } 180 CHECK(dynamic_program_header != nullptr); 181 182 size_t string_index; 183 elf_getshdrstrndx(elf, &string_index); 184 185 // Notes of the dynamic relocations, packed relocations, and .dynamic 186 // sections. Found while iterating sections, and later stored in class 187 // attributes. 188 Elf_Scn* found_relocations_section = nullptr; 189 Elf_Scn* found_dynamic_section = nullptr; 190 191 // Notes of relocation section types seen. We require one or the other of 192 // these; both is unsupported. 193 bool has_rel_relocations = false; 194 bool has_rela_relocations = false; 195 bool has_android_relocations = false; 196 197 Elf_Scn* section = NULL; 198 while ((section = elf_nextscn(elf, section)) != nullptr) { 199 auto section_header = ELF::getshdr(section); 200 std::string name = elf_strptr(elf, string_index, section_header->sh_name); 201 VerboseLogSectionHeader(name, section_header); 202 203 // Note relocation section types. 204 if (section_header->sh_type == SHT_REL || section_header->sh_type == SHT_ANDROID_REL) { 205 has_rel_relocations = true; 206 } 207 if (section_header->sh_type == SHT_RELA || section_header->sh_type == SHT_ANDROID_RELA) { 208 has_rela_relocations = true; 209 } 210 211 // Note special sections as we encounter them. 212 if ((name == ".rel.dyn" || name == ".rela.dyn") && 213 section_header->sh_size > 0) { 214 found_relocations_section = section; 215 216 // Note if relocation section is already packed 217 has_android_relocations = 218 section_header->sh_type == SHT_ANDROID_REL || 219 section_header->sh_type == SHT_ANDROID_RELA; 220 } 221 222 if (section_header->sh_offset == dynamic_program_header->p_offset) { 223 found_dynamic_section = section; 224 } 225 226 // Ensure we preserve alignment, repeated later for the data block(s). 227 CHECK(section_header->sh_addralign <= kPreserveAlignment); 228 229 Elf_Data* data = NULL; 230 while ((data = elf_getdata(section, data)) != NULL) { 231 CHECK(data->d_align <= kPreserveAlignment); 232 VerboseLogSectionData(data); 233 } 234 } 235 236 // Loading failed if we did not find the required special sections. 237 if (!found_dynamic_section) { 238 LOG(ERROR) << "Missing .dynamic section"; 239 return false; 240 } 241 242 if (found_relocations_section != nullptr) { 243 // Loading failed if we could not identify the relocations type. 244 if (!has_rel_relocations && !has_rela_relocations) { 245 LOG(ERROR) << "No relocations sections found"; 246 return false; 247 } 248 if (has_rel_relocations && has_rela_relocations) { 249 LOG(ERROR) << "Multiple relocations sections with different types found, " 250 << "not currently supported"; 251 return false; 252 } 253 } 254 255 elf_ = elf; 256 relocations_section_ = found_relocations_section; 257 dynamic_section_ = found_dynamic_section; 258 relocations_type_ = has_rel_relocations ? REL : RELA; 259 has_android_relocations_ = has_android_relocations; 260 return true; 261 } 262 263 // Helper for ResizeSection(). Adjust the main ELF header for the hole. 264 template <typename ELF> 265 static void AdjustElfHeaderForHole(typename ELF::Ehdr* elf_header, 266 typename ELF::Off hole_start, 267 ssize_t hole_size) { 268 if (elf_header->e_phoff > hole_start) { 269 elf_header->e_phoff += hole_size; 270 VLOG(1) << "e_phoff adjusted to " << elf_header->e_phoff; 271 } 272 if (elf_header->e_shoff > hole_start) { 273 elf_header->e_shoff += hole_size; 274 VLOG(1) << "e_shoff adjusted to " << elf_header->e_shoff; 275 } 276 } 277 278 // Helper for ResizeSection(). Adjust all section headers for the hole. 279 template <typename ELF> 280 static void AdjustSectionHeadersForHole(Elf* elf, 281 typename ELF::Off hole_start, 282 ssize_t hole_size) { 283 size_t string_index; 284 elf_getshdrstrndx(elf, &string_index); 285 286 Elf_Scn* section = NULL; 287 while ((section = elf_nextscn(elf, section)) != NULL) { 288 auto section_header = ELF::getshdr(section); 289 std::string name = elf_strptr(elf, string_index, section_header->sh_name); 290 291 if (section_header->sh_offset > hole_start) { 292 section_header->sh_offset += hole_size; 293 VLOG(1) << "section " << name 294 << " sh_offset adjusted to " << section_header->sh_offset; 295 } else { 296 section_header->sh_addr -= hole_size; 297 VLOG(1) << "section " << name 298 << " sh_addr adjusted to " << section_header->sh_addr; 299 } 300 } 301 } 302 303 // Helpers for ResizeSection(). On packing, reduce p_align for LOAD segments 304 // to 4kb if larger. On unpacking, restore p_align for LOAD segments if 305 // packing reduced it to 4kb. Return true if p_align was changed. 306 template <typename ELF> 307 static bool ClampLoadSegmentAlignment(typename ELF::Phdr* program_header) { 308 CHECK(program_header->p_type == PT_LOAD); 309 310 // If large, reduce p_align for a LOAD segment to page size on packing. 311 if (program_header->p_align > kPageSize) { 312 program_header->p_align = kPageSize; 313 return true; 314 } 315 return false; 316 } 317 318 template <typename ELF> 319 static bool RestoreLoadSegmentAlignment(typename ELF::Phdr* program_headers, 320 size_t count, 321 typename ELF::Phdr* program_header) { 322 CHECK(program_header->p_type == PT_LOAD); 323 324 // If p_align was reduced on packing, restore it to its previous value 325 // on unpacking. We do this by searching for a different LOAD segment 326 // and setting p_align to that of the other LOAD segment found. 327 // 328 // Relies on the following observations: 329 // - a packable ELF executable has more than one LOAD segment; 330 // - before packing all LOAD segments have the same p_align; 331 // - on packing we reduce only one LOAD segment's p_align. 332 if (program_header->p_align == kPageSize) { 333 for (size_t i = 0; i < count; ++i) { 334 typename ELF::Phdr* other_header = &program_headers[i]; 335 if (other_header->p_type == PT_LOAD && other_header != program_header) { 336 program_header->p_align = other_header->p_align; 337 return true; 338 } 339 } 340 LOG(WARNING) << "Cannot find a LOAD segment from which to restore p_align"; 341 } 342 return false; 343 } 344 345 template <typename ELF> 346 static bool AdjustLoadSegmentAlignment(typename ELF::Phdr* program_headers, 347 size_t count, 348 typename ELF::Phdr* program_header, 349 ssize_t hole_size) { 350 CHECK(program_header->p_type == PT_LOAD); 351 352 bool status = false; 353 if (hole_size < 0) { 354 status = ClampLoadSegmentAlignment<ELF>(program_header); 355 } else if (hole_size > 0) { 356 status = RestoreLoadSegmentAlignment<ELF>(program_headers, 357 count, 358 program_header); 359 } 360 return status; 361 } 362 363 // Helper for ResizeSection(). Adjust the offsets of any program headers 364 // that have offsets currently beyond the hole start, and adjust the 365 // virtual and physical addrs (and perhaps alignment) of the others. 366 template <typename ELF> 367 static void AdjustProgramHeaderFields(typename ELF::Phdr* program_headers, 368 size_t count, 369 typename ELF::Off hole_start, 370 ssize_t hole_size) { 371 int alignment_changes = 0; 372 for (size_t i = 0; i < count; ++i) { 373 typename ELF::Phdr* program_header = &program_headers[i]; 374 375 // Do not adjust PT_GNU_STACK - it confuses gdb and results 376 // in incorrect unwinding if the executable is stripped after 377 // packing. 378 if (program_header->p_type == PT_GNU_STACK) { 379 continue; 380 } 381 382 if (program_header->p_offset > hole_start) { 383 // The hole start is past this segment, so adjust offset. 384 program_header->p_offset += hole_size; 385 VLOG(1) << "phdr[" << i 386 << "] p_offset adjusted to "<< program_header->p_offset; 387 } else { 388 program_header->p_vaddr -= hole_size; 389 program_header->p_paddr -= hole_size; 390 391 // If packing, clamp LOAD segment alignment to 4kb to prevent strip 392 // from adjusting it unnecessarily if run on a packed file. If 393 // unpacking, attempt to restore a reduced alignment to its previous 394 // value. Ensure that we do this on at most one LOAD segment. 395 if (program_header->p_type == PT_LOAD) { 396 alignment_changes += AdjustLoadSegmentAlignment<ELF>(program_headers, 397 count, 398 program_header, 399 hole_size); 400 LOG_IF(FATAL, alignment_changes > 1) 401 << "Changed p_align on more than one LOAD segment"; 402 } 403 404 VLOG(1) << "phdr[" << i 405 << "] p_vaddr adjusted to "<< program_header->p_vaddr 406 << "; p_paddr adjusted to "<< program_header->p_paddr 407 << "; p_align adjusted to "<< program_header->p_align; 408 } 409 } 410 } 411 412 // Helper for ResizeSection(). Find the first loadable segment in the 413 // file. We expect it to map from file offset zero. 414 template <typename ELF> 415 static typename ELF::Phdr* FindLoadSegmentForHole(typename ELF::Phdr* program_headers, 416 size_t count, 417 typename ELF::Off hole_start) { 418 for (size_t i = 0; i < count; ++i) { 419 typename ELF::Phdr* program_header = &program_headers[i]; 420 421 if (program_header->p_type == PT_LOAD && 422 program_header->p_offset <= hole_start && 423 (program_header->p_offset + program_header->p_filesz) >= hole_start ) { 424 return program_header; 425 } 426 } 427 LOG(FATAL) << "Cannot locate a LOAD segment with hole_start=0x" << std::hex << hole_start; 428 NOTREACHED(); 429 430 return nullptr; 431 } 432 433 // Helper for ResizeSection(). Rewrite program headers. 434 template <typename ELF> 435 static void RewriteProgramHeadersForHole(Elf* elf, 436 typename ELF::Off hole_start, 437 ssize_t hole_size) { 438 const typename ELF::Ehdr* elf_header = ELF::getehdr(elf); 439 CHECK(elf_header); 440 441 typename ELF::Phdr* elf_program_header = ELF::getphdr(elf); 442 CHECK(elf_program_header); 443 444 const size_t program_header_count = elf_header->e_phnum; 445 446 // Locate the segment that we can overwrite to form the new LOAD entry, 447 // and the segment that we are going to split into two parts. 448 typename ELF::Phdr* target_load_header = 449 FindLoadSegmentForHole<ELF>(elf_program_header, program_header_count, hole_start); 450 451 VLOG(1) << "phdr[" << target_load_header - elf_program_header << "] adjust"; 452 // Adjust PT_LOAD program header memsz and filesz 453 target_load_header->p_filesz += hole_size; 454 target_load_header->p_memsz += hole_size; 455 456 // Adjust the offsets and p_vaddrs 457 AdjustProgramHeaderFields<ELF>(elf_program_header, 458 program_header_count, 459 hole_start, 460 hole_size); 461 } 462 463 // Helper for ResizeSection(). Locate and return the dynamic section. 464 template <typename ELF> 465 static Elf_Scn* GetDynamicSection(Elf* elf) { 466 const typename ELF::Ehdr* elf_header = ELF::getehdr(elf); 467 CHECK(elf_header); 468 469 const typename ELF::Phdr* elf_program_header = ELF::getphdr(elf); 470 CHECK(elf_program_header); 471 472 // Find the program header that describes the dynamic section. 473 const typename ELF::Phdr* dynamic_program_header = NULL; 474 for (size_t i = 0; i < elf_header->e_phnum; ++i) { 475 const typename ELF::Phdr* program_header = &elf_program_header[i]; 476 477 if (program_header->p_type == PT_DYNAMIC) { 478 dynamic_program_header = program_header; 479 } 480 } 481 CHECK(dynamic_program_header); 482 483 // Now find the section with the same offset as this program header. 484 Elf_Scn* dynamic_section = NULL; 485 Elf_Scn* section = NULL; 486 while ((section = elf_nextscn(elf, section)) != NULL) { 487 typename ELF::Shdr* section_header = ELF::getshdr(section); 488 489 if (section_header->sh_offset == dynamic_program_header->p_offset) { 490 dynamic_section = section; 491 } 492 } 493 CHECK(dynamic_section != NULL); 494 495 return dynamic_section; 496 } 497 498 // Helper for ResizeSection(). Adjust the .dynamic section for the hole. 499 template <typename ELF> 500 void ElfFile<ELF>::AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, 501 typename ELF::Off hole_start, 502 ssize_t hole_size, 503 relocations_type_t relocations_type) { 504 CHECK(relocations_type != NONE); 505 Elf_Data* data = GetSectionData(dynamic_section); 506 507 auto dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf); 508 std::vector<typename ELF::Dyn> dynamics( 509 dynamic_base, 510 dynamic_base + data->d_size / sizeof(dynamics[0])); 511 512 if (hole_size > 0) { // expanding 513 hole_start += hole_size; 514 } 515 516 for (size_t i = 0; i < dynamics.size(); ++i) { 517 typename ELF::Dyn* dynamic = &dynamics[i]; 518 const typename ELF::Sword tag = dynamic->d_tag; 519 520 // Any tags that hold offsets are adjustment candidates. 521 const bool is_adjustable = (tag == DT_PLTGOT || 522 tag == DT_HASH || 523 tag == DT_GNU_HASH || 524 tag == DT_STRTAB || 525 tag == DT_SYMTAB || 526 tag == DT_RELA || 527 tag == DT_INIT || 528 tag == DT_FINI || 529 tag == DT_REL || 530 tag == DT_JMPREL || 531 tag == DT_INIT_ARRAY || 532 tag == DT_FINI_ARRAY || 533 tag == DT_VERSYM || 534 tag == DT_VERNEED || 535 tag == DT_VERDEF || 536 tag == DT_ANDROID_REL|| 537 tag == DT_ANDROID_RELA); 538 539 if (is_adjustable && dynamic->d_un.d_ptr <= hole_start) { 540 dynamic->d_un.d_ptr -= hole_size; 541 VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag 542 << " d_ptr adjusted to " << dynamic->d_un.d_ptr; 543 } 544 545 // DT_RELSZ or DT_RELASZ indicate the overall size of relocations. 546 // Only one will be present. Adjust by hole size. 547 if (tag == DT_RELSZ || tag == DT_RELASZ || tag == DT_ANDROID_RELSZ || tag == DT_ANDROID_RELASZ) { 548 dynamic->d_un.d_val += hole_size; 549 VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag 550 << " d_val adjusted to " << dynamic->d_un.d_val; 551 } 552 553 // Special case: DT_MIPS_RLD_MAP_REL stores the difference between dynamic 554 // entry address and the address of the _r_debug (used by GDB) 555 // since the dynamic section and target address are on the 556 // different sides of the hole it needs to be adjusted accordingly 557 if (tag == DT_MIPS_RLD_MAP_REL) { 558 dynamic->d_un.d_val += hole_size; 559 VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag 560 << " d_val adjusted to " << dynamic->d_un.d_val; 561 } 562 563 // Ignore DT_RELCOUNT and DT_RELACOUNT: (1) nobody uses them and 564 // technically (2) the relative relocation count is not changed. 565 566 // DT_RELENT and DT_RELAENT don't change, ignore them as well. 567 } 568 569 void* section_data = &dynamics[0]; 570 size_t bytes = dynamics.size() * sizeof(dynamics[0]); 571 RewriteSectionData(dynamic_section, section_data, bytes); 572 } 573 574 // Resize a section. If the new size is larger than the current size, open 575 // up a hole by increasing file offsets that come after the hole. If smaller 576 // than the current size, remove the hole by decreasing those offsets. 577 template <typename ELF> 578 void ElfFile<ELF>::ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size, 579 typename ELF::Word new_sh_type, 580 relocations_type_t relocations_type) { 581 582 size_t string_index; 583 elf_getshdrstrndx(elf, &string_index); 584 auto section_header = ELF::getshdr(section); 585 std::string name = elf_strptr(elf, string_index, section_header->sh_name); 586 587 if (section_header->sh_size == new_size) { 588 return; 589 } 590 591 // Require that the section size and the data size are the same. True 592 // in practice for all sections we resize when packing or unpacking. 593 Elf_Data* data = GetSectionData(section); 594 CHECK(data->d_off == 0 && data->d_size == section_header->sh_size); 595 596 // Require that the section is not zero-length (that is, has allocated 597 // data that we can validly expand). 598 CHECK(data->d_size && data->d_buf); 599 600 const auto hole_start = section_header->sh_offset; 601 const ssize_t hole_size = new_size - data->d_size; 602 603 VLOG_IF(1, (hole_size > 0)) << "expand section (" << name << ") size: " << 604 data->d_size << " -> " << (data->d_size + hole_size); 605 VLOG_IF(1, (hole_size < 0)) << "shrink section (" << name << ") size: " << 606 data->d_size << " -> " << (data->d_size + hole_size); 607 608 // libelf overrides sh_entsize for known sh_types, so it does not matter what we set 609 // for SHT_REL/SHT_RELA. 610 typename ELF::Xword new_entsize = 611 (new_sh_type == SHT_ANDROID_REL || new_sh_type == SHT_ANDROID_RELA) ? 1 : 0; 612 613 VLOG(1) << "Update section (" << name << ") entry size: " << 614 section_header->sh_entsize << " -> " << new_entsize; 615 616 // Resize the data and the section header. 617 data->d_size += hole_size; 618 section_header->sh_size += hole_size; 619 section_header->sh_entsize = new_entsize; 620 section_header->sh_type = new_sh_type; 621 622 // Add the hole size to all offsets in the ELF file that are after the 623 // start of the hole. If the hole size is positive we are expanding the 624 // section to create a new hole; if negative, we are closing up a hole. 625 626 // Start with the main ELF header. 627 typename ELF::Ehdr* elf_header = ELF::getehdr(elf); 628 AdjustElfHeaderForHole<ELF>(elf_header, hole_start, hole_size); 629 630 // Adjust all section headers. 631 AdjustSectionHeadersForHole<ELF>(elf, hole_start, hole_size); 632 633 // Rewrite the program headers to either split or coalesce segments, 634 // and adjust dynamic entries to match. 635 RewriteProgramHeadersForHole<ELF>(elf, hole_start, hole_size); 636 637 Elf_Scn* dynamic_section = GetDynamicSection<ELF>(elf); 638 AdjustDynamicSectionForHole(dynamic_section, hole_start, hole_size, relocations_type); 639 } 640 641 // Find the first slot in a dynamics array with the given tag. The array 642 // always ends with a free (unused) element, and which we exclude from the 643 // search. Returns dynamics->size() if not found. 644 template <typename ELF> 645 static size_t FindDynamicEntry(typename ELF::Sword tag, 646 std::vector<typename ELF::Dyn>* dynamics) { 647 // Loop until the penultimate entry. We exclude the end sentinel. 648 for (size_t i = 0; i < dynamics->size() - 1; ++i) { 649 if (dynamics->at(i).d_tag == tag) { 650 return i; 651 } 652 } 653 654 // The tag was not found. 655 return dynamics->size(); 656 } 657 658 // Replace dynamic entry. 659 template <typename ELF> 660 static void ReplaceDynamicEntry(typename ELF::Sword tag, 661 const typename ELF::Dyn& dyn, 662 std::vector<typename ELF::Dyn>* dynamics) { 663 const size_t slot = FindDynamicEntry<ELF>(tag, dynamics); 664 if (slot == dynamics->size()) { 665 LOG(FATAL) << "Dynamic slot is not found for tag=" << tag; 666 } 667 668 // Replace this entry with the one supplied. 669 dynamics->at(slot) = dyn; 670 VLOG(1) << "dynamic[" << slot << "] overwritten with " << dyn.d_tag; 671 } 672 673 // Remove relative entries from dynamic relocations and write as packed 674 // data into android packed relocations. 675 template <typename ELF> 676 bool ElfFile<ELF>::PackRelocations() { 677 // Load the ELF file into libelf. 678 if (!Load()) { 679 LOG(ERROR) << "Failed to load as ELF"; 680 return false; 681 } 682 683 if (relocations_section_ == nullptr) { 684 // There is nothing to do 685 return true; 686 } 687 688 // Retrieve the current dynamic relocations section data. 689 Elf_Data* data = GetSectionData(relocations_section_); 690 // we always pack rela, because packed format is pretty much the same 691 std::vector<typename ELF::Rela> relocations; 692 693 if (relocations_type_ == REL) { 694 // Convert data to a vector of relocations. 695 const typename ELF::Rel* relocations_base = reinterpret_cast<typename ELF::Rel*>(data->d_buf); 696 ConvertRelArrayToRelaVector(relocations_base, 697 data->d_size / sizeof(typename ELF::Rel), &relocations); 698 VLOG(1) << "Relocations : REL"; 699 } else if (relocations_type_ == RELA) { 700 // Convert data to a vector of relocations with addends. 701 const typename ELF::Rela* relocations_base = reinterpret_cast<typename ELF::Rela*>(data->d_buf); 702 relocations = std::vector<typename ELF::Rela>( 703 relocations_base, 704 relocations_base + data->d_size / sizeof(relocations[0])); 705 706 VLOG(1) << "Relocations : RELA"; 707 } else { 708 NOTREACHED(); 709 } 710 711 return PackTypedRelocations(&relocations); 712 } 713 714 // Helper for PackRelocations(). Rel type is one of ELF::Rel or ELF::Rela. 715 template <typename ELF> 716 bool ElfFile<ELF>::PackTypedRelocations(std::vector<typename ELF::Rela>* relocations) { 717 typedef typename ELF::Rela Rela; 718 719 if (has_android_relocations_) { 720 LOG(INFO) << "Relocation table is already packed"; 721 return true; 722 } 723 724 // If no relocations then we have nothing packable. Perhaps 725 // the shared object has already been packed? 726 if (relocations->empty()) { 727 LOG(ERROR) << "No relocations found"; 728 return false; 729 } 730 731 const size_t rel_size = 732 relocations_type_ == RELA ? sizeof(typename ELF::Rela) : sizeof(typename ELF::Rel); 733 const size_t initial_bytes = relocations->size() * rel_size; 734 735 VLOG(1) << "Unpacked : " << initial_bytes << " bytes"; 736 std::vector<uint8_t> packed; 737 RelocationPacker<ELF> packer; 738 739 // Pack relocations: dry run to estimate memory savings. 740 packer.PackRelocations(*relocations, &packed); 741 const size_t packed_bytes_estimate = packed.size() * sizeof(packed[0]); 742 VLOG(1) << "Packed (no padding): " << packed_bytes_estimate << " bytes"; 743 744 if (packed.empty()) { 745 VLOG(1) << "Too few relocations to pack"; 746 return true; 747 } 748 749 // Pre-calculate the size of the hole we will close up when we rewrite 750 // dynamic relocations. We have to adjust relocation addresses to 751 // account for this. 752 typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_); 753 ssize_t hole_size = initial_bytes - packed_bytes_estimate; 754 755 // hole_size needs to be page_aligned. 756 hole_size -= hole_size % kPreserveAlignment; 757 758 VLOG(1) << "Compaction : " << hole_size << " bytes"; 759 760 // Adjusting for alignment may have removed any packing benefit. 761 if (hole_size == 0) { 762 VLOG(1) << "Too few relocations to pack after alignment"; 763 return true; 764 } 765 766 if (hole_size <= 0) { 767 VLOG(1) << "Packing relocations saves no space"; 768 return true; 769 } 770 771 size_t data_padding_bytes = is_padding_relocations_ ? 772 initial_bytes - packed_bytes_estimate : 773 initial_bytes - hole_size - packed_bytes_estimate; 774 775 // pad data 776 std::vector<uint8_t> padding(data_padding_bytes, 0); 777 packed.insert(packed.end(), padding.begin(), padding.end()); 778 779 const void* packed_data = &packed[0]; 780 781 // Run a loopback self-test as a check that packing is lossless. 782 std::vector<Rela> unpacked; 783 packer.UnpackRelocations(packed, &unpacked); 784 CHECK(unpacked.size() == relocations->size()); 785 CHECK(!memcmp(&unpacked[0], 786 &relocations->at(0), 787 unpacked.size() * sizeof(unpacked[0]))); 788 789 // Rewrite the current dynamic relocations section with packed one then shrink it to size. 790 const size_t bytes = packed.size() * sizeof(packed[0]); 791 ResizeSection(elf_, relocations_section_, bytes, 792 relocations_type_ == REL ? SHT_ANDROID_REL : SHT_ANDROID_RELA, relocations_type_); 793 RewriteSectionData(relocations_section_, packed_data, bytes); 794 795 // TODO (dimitry): fix string table and replace .rel.dyn/plt with .android.rel.dyn/plt 796 797 // Rewrite .dynamic and rename relocation tags describing the packed android 798 // relocations. 799 Elf_Data* data = GetSectionData(dynamic_section_); 800 const typename ELF::Dyn* dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf); 801 std::vector<typename ELF::Dyn> dynamics( 802 dynamic_base, 803 dynamic_base + data->d_size / sizeof(dynamics[0])); 804 section_header = ELF::getshdr(relocations_section_); 805 { 806 typename ELF::Dyn dyn; 807 dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA; 808 dyn.d_un.d_ptr = section_header->sh_addr; 809 ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_REL : DT_RELA, dyn, &dynamics); 810 } 811 { 812 typename ELF::Dyn dyn; 813 dyn.d_tag = relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ; 814 dyn.d_un.d_val = section_header->sh_size; 815 ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_RELSZ : DT_RELASZ, dyn, &dynamics); 816 } 817 818 const void* dynamics_data = &dynamics[0]; 819 const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]); 820 RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes); 821 822 Flush(); 823 return true; 824 } 825 826 // Find packed relative relocations in the packed android relocations 827 // section, unpack them, and rewrite the dynamic relocations section to 828 // contain unpacked data. 829 template <typename ELF> 830 bool ElfFile<ELF>::UnpackRelocations() { 831 // Load the ELF file into libelf. 832 if (!Load()) { 833 LOG(ERROR) << "Failed to load as ELF"; 834 return false; 835 } 836 837 if (relocations_section_ == nullptr) { 838 // There is nothing to do 839 return true; 840 } 841 842 typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_); 843 // Retrieve the current packed android relocations section data. 844 Elf_Data* data = GetSectionData(relocations_section_); 845 846 // Convert data to a vector of bytes. 847 const uint8_t* packed_base = reinterpret_cast<uint8_t*>(data->d_buf); 848 std::vector<uint8_t> packed( 849 packed_base, 850 packed_base + data->d_size / sizeof(packed[0])); 851 852 if ((section_header->sh_type == SHT_ANDROID_RELA || section_header->sh_type == SHT_ANDROID_REL) && 853 packed.size() > 3 && 854 packed[0] == 'A' && 855 packed[1] == 'P' && 856 packed[2] == 'S' && 857 packed[3] == '2') { 858 LOG(INFO) << "Relocations : " << (relocations_type_ == REL ? "REL" : "RELA"); 859 } else { 860 LOG(ERROR) << "Packed relocations not found (not packed?)"; 861 return false; 862 } 863 864 return UnpackTypedRelocations(packed); 865 } 866 867 // Helper for UnpackRelocations(). Rel type is one of ELF::Rel or ELF::Rela. 868 template <typename ELF> 869 bool ElfFile<ELF>::UnpackTypedRelocations(const std::vector<uint8_t>& packed) { 870 // Unpack the data to re-materialize the relative relocations. 871 const size_t packed_bytes = packed.size() * sizeof(packed[0]); 872 LOG(INFO) << "Packed : " << packed_bytes << " bytes"; 873 std::vector<typename ELF::Rela> unpacked_relocations; 874 RelocationPacker<ELF> packer; 875 packer.UnpackRelocations(packed, &unpacked_relocations); 876 877 const size_t relocation_entry_size = 878 relocations_type_ == REL ? sizeof(typename ELF::Rel) : sizeof(typename ELF::Rela); 879 const size_t unpacked_bytes = unpacked_relocations.size() * relocation_entry_size; 880 LOG(INFO) << "Unpacked : " << unpacked_bytes << " bytes"; 881 882 // Retrieve the current dynamic relocations section data. 883 Elf_Data* data = GetSectionData(relocations_section_); 884 885 LOG(INFO) << "Relocations : " << unpacked_relocations.size() << " entries"; 886 887 // If we found the same number of null relocation entries in the dynamic 888 // relocations section as we hold as unpacked relative relocations, then 889 // this is a padded file. 890 891 const bool is_padded = packed_bytes == unpacked_bytes; 892 893 // Unless padded, pre-apply relative relocations to account for the 894 // hole, and pre-adjust all relocation offsets accordingly. 895 typename ELF::Shdr* section_header = ELF::getshdr(relocations_section_); 896 897 if (!is_padded) { 898 LOG(INFO) << "Expansion : " << unpacked_bytes - packed_bytes << " bytes"; 899 } 900 901 // Rewrite the current dynamic relocations section with unpacked version of 902 // relocations. 903 const void* section_data = nullptr; 904 std::vector<typename ELF::Rel> unpacked_rel_relocations; 905 if (relocations_type_ == RELA) { 906 section_data = &unpacked_relocations[0]; 907 } else if (relocations_type_ == REL) { 908 ConvertRelaVectorToRelVector(unpacked_relocations, &unpacked_rel_relocations); 909 section_data = &unpacked_rel_relocations[0]; 910 } else { 911 NOTREACHED(); 912 } 913 914 ResizeSection(elf_, relocations_section_, unpacked_bytes, 915 relocations_type_ == REL ? SHT_REL : SHT_RELA, relocations_type_); 916 RewriteSectionData(relocations_section_, section_data, unpacked_bytes); 917 918 // Rewrite .dynamic to remove two tags describing packed android relocations. 919 data = GetSectionData(dynamic_section_); 920 const typename ELF::Dyn* dynamic_base = reinterpret_cast<typename ELF::Dyn*>(data->d_buf); 921 std::vector<typename ELF::Dyn> dynamics( 922 dynamic_base, 923 dynamic_base + data->d_size / sizeof(dynamics[0])); 924 { 925 typename ELF::Dyn dyn; 926 dyn.d_tag = relocations_type_ == REL ? DT_REL : DT_RELA; 927 dyn.d_un.d_ptr = section_header->sh_addr; 928 ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_ANDROID_REL : DT_ANDROID_RELA, 929 dyn, &dynamics); 930 } 931 932 { 933 typename ELF::Dyn dyn; 934 dyn.d_tag = relocations_type_ == REL ? DT_RELSZ : DT_RELASZ; 935 dyn.d_un.d_val = section_header->sh_size; 936 ReplaceDynamicEntry<ELF>(relocations_type_ == REL ? DT_ANDROID_RELSZ : DT_ANDROID_RELASZ, 937 dyn, &dynamics); 938 } 939 940 const void* dynamics_data = &dynamics[0]; 941 const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]); 942 RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes); 943 944 Flush(); 945 return true; 946 } 947 948 // Flush rewritten shared object file data. 949 template <typename ELF> 950 void ElfFile<ELF>::Flush() { 951 // Flag all ELF data held in memory as needing to be written back to the 952 // file, and tell libelf that we have controlled the file layout. 953 elf_flagelf(elf_, ELF_C_SET, ELF_F_DIRTY); 954 elf_flagelf(elf_, ELF_C_SET, ELF_F_LAYOUT); 955 956 // Write ELF data back to disk. 957 const off_t file_bytes = elf_update(elf_, ELF_C_WRITE); 958 if (file_bytes == -1) { 959 LOG(ERROR) << "elf_update failed: " << elf_errmsg(elf_errno()); 960 } 961 962 CHECK(file_bytes > 0); 963 VLOG(1) << "elf_update returned: " << file_bytes; 964 965 // Clean up libelf, and truncate the output file to the number of bytes 966 // written by elf_update(). 967 elf_end(elf_); 968 elf_ = NULL; 969 const int truncate = ftruncate(fd_, file_bytes); 970 CHECK(truncate == 0); 971 } 972 973 template <typename ELF> 974 void ElfFile<ELF>::ConvertRelArrayToRelaVector(const typename ELF::Rel* rel_array, 975 size_t rel_array_size, 976 std::vector<typename ELF::Rela>* rela_vector) { 977 for (size_t i = 0; i<rel_array_size; ++i) { 978 typename ELF::Rela rela; 979 rela.r_offset = rel_array[i].r_offset; 980 rela.r_info = rel_array[i].r_info; 981 rela.r_addend = 0; 982 rela_vector->push_back(rela); 983 } 984 } 985 986 template <typename ELF> 987 void ElfFile<ELF>::ConvertRelaVectorToRelVector(const std::vector<typename ELF::Rela>& rela_vector, 988 std::vector<typename ELF::Rel>* rel_vector) { 989 for (auto rela : rela_vector) { 990 typename ELF::Rel rel; 991 rel.r_offset = rela.r_offset; 992 rel.r_info = rela.r_info; 993 CHECK(rela.r_addend == 0); 994 rel_vector->push_back(rel); 995 } 996 } 997 998 template class ElfFile<ELF32_traits>; 999 template class ElfFile<ELF64_traits>; 1000 1001 } // namespace relocation_packer 1002