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 #include "crazy_linker_elf_view.h" 6 7 #include <errno.h> 8 9 #include "crazy_linker_debug.h" 10 #include "crazy_linker_error.h" 11 #include "linker_phdr.h" 12 13 namespace crazy { 14 15 bool ElfView::InitUnmapped(ELF::Addr load_address, 16 const ELF::Phdr* phdr, 17 size_t phdr_count, 18 Error* error) { 19 // Compute load size and bias. 20 ELF::Addr min_vaddr = 0; 21 load_size_ = phdr_table_get_load_size(phdr, phdr_count, &min_vaddr, NULL); 22 if (load_size_ == 0) { 23 *error = "Invalid program header table"; 24 return false; 25 } 26 load_address_ = (load_address ? load_address : min_vaddr); 27 load_bias_ = load_address - min_vaddr; 28 29 // Extract the dynamic table information. 30 phdr_table_get_dynamic_section(phdr, 31 phdr_count, 32 load_bias_, 33 &dynamic_, 34 &dynamic_count_, 35 &dynamic_flags_); 36 if (!dynamic_) { 37 *error = "No PT_DYNAMIC section!"; 38 return false; 39 } 40 41 // Compute the program header table address relative to load_address. 42 // This is different from |phdr|..|phdr + phdr_count| which can actually 43 // be at a different location. 44 const ELF::Phdr* phdr0 = NULL; 45 46 // First, if there is a PT_PHDR, use it directly. 47 for (size_t n = 0; n < phdr_count; ++n) { 48 const ELF::Phdr* entry = &phdr[n]; 49 if (entry->p_type == PT_PHDR) { 50 phdr0 = entry; 51 break; 52 } 53 } 54 55 // Otherwise, check the first loadable segment. If its file offset 56 // is 0, it starts with the ELF header, and we can trivially find the 57 // loaded program header from it. 58 if (!phdr0) { 59 for (size_t n = 0; n < phdr_count; ++n) { 60 const ELF::Phdr* entry = &phdr[n]; 61 if (entry->p_type == PT_LOAD) { 62 if (entry->p_offset == 0) { 63 ELF::Addr elf_addr = load_bias_ + entry->p_vaddr; 64 const ELF::Ehdr* ehdr = reinterpret_cast<const ELF::Ehdr*>(elf_addr); 65 ELF::Addr offset = ehdr->e_phoff; 66 phdr0 = reinterpret_cast<const ELF::Phdr*>(elf_addr + offset); 67 } 68 break; 69 } 70 } 71 } 72 73 // Check that the program header table is indeed in a loadable segment, 74 // this helps catching malformed ELF binaries. 75 if (phdr0) { 76 ELF::Addr phdr0_addr = reinterpret_cast<ELF::Addr>(phdr0); 77 ELF::Addr phdr0_limit = phdr0_addr + sizeof(ELF::Phdr) * phdr_count; 78 bool found = false; 79 for (size_t n = 0; n < phdr_count; ++n) { 80 size_t seg_start = load_bias_ + phdr[n].p_vaddr; 81 size_t seg_end = seg_start + phdr[n].p_filesz; 82 83 if (seg_start <= phdr0_addr && phdr0_limit <= seg_end) { 84 found = true; 85 break; 86 } 87 } 88 89 if (!found) 90 phdr0 = NULL; 91 } 92 93 if (!phdr0) { 94 *error = "Malformed ELF binary"; 95 return false; 96 } 97 98 phdr_ = phdr0; 99 phdr_count_ = phdr_count; 100 101 LOG("%s: New ELF view [load_address:%p, load_size:%p, load_bias:%p, phdr:%p, " 102 "phdr_count:%d, dynamic:%p, dynamic_count:%d, dynamic_flags:%d\n", 103 __FUNCTION__, 104 load_address_, 105 load_size_, 106 load_bias_, 107 phdr_, 108 phdr_count_, 109 dynamic_, 110 dynamic_count_, 111 dynamic_flags_); 112 return true; 113 } 114 115 bool ElfView::ProtectRelroSection(Error* error) { 116 LOG("%s: Enabling GNU RELRO protection\n", __FUNCTION__); 117 118 if (phdr_table_protect_gnu_relro(phdr_, phdr_count_, load_bias_) < 0) { 119 error->Format("Can't enable GNU RELRO protection: %s", strerror(errno)); 120 return false; 121 } 122 return true; 123 } 124 125 } // namespace crazy 126