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