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