• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_loader.h"
6 
7 #include <limits.h>  // For PAGE_SIZE and PAGE_MASK
8 
9 #include "crazy_linker_debug.h"
10 #include "linker_phdr.h"
11 
12 #define PAGE_START(x) ((x) & PAGE_MASK)
13 #define PAGE_OFFSET(x) ((x) & ~PAGE_MASK)
14 #define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE - 1))
15 
16 namespace crazy {
17 
18 #define MAYBE_MAP_FLAG(x, from, to) (((x) & (from)) ? (to) : 0)
19 #define PFLAGS_TO_PROT(x)                 \
20   (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \
21    MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \
22    MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE))
23 
ElfLoader()24 ElfLoader::ElfLoader()
25     : fd_(),
26       path_(NULL),
27       phdr_num_(0),
28       phdr_mmap_(NULL),
29       phdr_table_(NULL),
30       phdr_size_(0),
31       file_offset_(0),
32       wanted_load_address_(0),
33       load_start_(NULL),
34       load_size_(0),
35       load_bias_(0),
36       loaded_phdr_(NULL) {}
37 
~ElfLoader()38 ElfLoader::~ElfLoader() {
39   if (phdr_mmap_) {
40     // Deallocate the temporary program header copy.
41     munmap(phdr_mmap_, phdr_size_);
42   }
43 }
44 
LoadAt(const char * lib_path,off_t file_offset,uintptr_t wanted_address,Error * error)45 bool ElfLoader::LoadAt(const char* lib_path,
46                        off_t file_offset,
47                        uintptr_t wanted_address,
48                        Error* error) {
49 
50   LOG("%s: lib_path='%s', file_offset=%p, load_address=%p\n",
51       __FUNCTION__,
52       lib_path,
53       file_offset,
54       wanted_address);
55 
56   // Check that the load address is properly page-aligned.
57   if (wanted_address != PAGE_START(wanted_address)) {
58     error->Format("Load address is not page aligned (%08x)", wanted_address);
59     return false;
60   }
61   wanted_load_address_ = reinterpret_cast<void*>(wanted_address);
62 
63   // Check that the file offset is also properly page-aligned.
64   // PAGE_START() can't be used here due to the compiler complaining about
65   // comparing signed (off_t) and unsigned (size_t) values.
66   if ((file_offset & static_cast<off_t>(PAGE_SIZE - 1)) != 0) {
67     error->Format("File offset is not page aligned (%08x)", file_offset);
68     return false;
69   }
70   file_offset_ = file_offset;
71 
72   // Open the file.
73   if (!fd_.OpenReadOnly(lib_path)) {
74     error->Format("Can't open file: %s", strerror(errno));
75     return false;
76   }
77 
78   if (file_offset && fd_.SeekTo(file_offset) < 0) {
79     error->Format(
80         "Can't seek to file offset %08x: %s", file_offset, strerror(errno));
81     return false;
82   }
83 
84   path_ = lib_path;
85 
86   if (!ReadElfHeader(error) || !ReadProgramHeader(error) ||
87       !ReserveAddressSpace(error)) {
88     return false;
89   }
90 
91   if (!LoadSegments(error) || !FindPhdr(error)) {
92     // An error occured, cleanup the address space by un-mapping the
93     // range that was reserved by ReserveAddressSpace().
94     if (load_start_ && load_size_)
95       munmap(load_start_, load_size_);
96 
97     return false;
98   }
99 
100   return true;
101 }
102 
ReadElfHeader(Error * error)103 bool ElfLoader::ReadElfHeader(Error* error) {
104   int ret = fd_.Read(&header_, sizeof(header_));
105   if (ret < 0) {
106     error->Format("Can't read file: %s", strerror(errno));
107     return false;
108   }
109   if (ret != static_cast<int>(sizeof(header_))) {
110     error->Set("File too small to be ELF");
111     return false;
112   }
113 
114   if (memcmp(header_.e_ident, ELFMAG, SELFMAG) != 0) {
115     error->Set("Bad ELF magic");
116     return false;
117   }
118 
119   if (header_.e_ident[EI_CLASS] != ELF::kElfClass) {
120     error->Format("Not a %d-bit class: %d",
121                   ELF::kElfBits,
122                   header_.e_ident[EI_CLASS]);
123     return false;
124   }
125 
126   if (header_.e_ident[EI_DATA] != ELFDATA2LSB) {
127     error->Format("Not little-endian class: %d", header_.e_ident[EI_DATA]);
128     return false;
129   }
130 
131   if (header_.e_type != ET_DYN) {
132     error->Format("Not a shared library type: %d", header_.e_type);
133     return false;
134   }
135 
136   if (header_.e_version != EV_CURRENT) {
137     error->Format("Unexpected ELF version: %d", header_.e_version);
138     return false;
139   }
140 
141   if (header_.e_machine != ELF_MACHINE) {
142     error->Format("Unexpected ELF machine type: %d", header_.e_machine);
143     return false;
144   }
145 
146   return true;
147 }
148 
149 // Loads the program header table from an ELF file into a read-only private
150 // anonymous mmap-ed block.
ReadProgramHeader(Error * error)151 bool ElfLoader::ReadProgramHeader(Error* error) {
152   phdr_num_ = header_.e_phnum;
153 
154   // Like the kernel, only accept program header tables smaller than 64 KB.
155   if (phdr_num_ < 1 || phdr_num_ > 65536 / sizeof(ELF::Phdr)) {
156     error->Format("Invalid program header count: %d", phdr_num_);
157     return false;
158   }
159 
160   ELF::Addr page_min = PAGE_START(header_.e_phoff);
161   ELF::Addr page_max =
162       PAGE_END(header_.e_phoff + (phdr_num_ * sizeof(ELF::Phdr)));
163   ELF::Addr page_offset = PAGE_OFFSET(header_.e_phoff);
164 
165   phdr_size_ = page_max - page_min;
166 
167   void* mmap_result = fd_.Map(
168       NULL, phdr_size_, PROT_READ, MAP_PRIVATE, page_min + file_offset_);
169   if (mmap_result == MAP_FAILED) {
170     error->Format("Phdr mmap failed: %s", strerror(errno));
171     return false;
172   }
173 
174   phdr_mmap_ = mmap_result;
175   phdr_table_ = reinterpret_cast<ELF::Phdr*>(
176       reinterpret_cast<char*>(mmap_result) + page_offset);
177   return true;
178 }
179 
180 // Reserve a virtual address range big enough to hold all loadable
181 // segments of a program header table. This is done by creating a
182 // private anonymous mmap() with PROT_NONE.
183 //
184 // This will use the wanted_load_address_ value. Fails if the requested
185 // address range cannot be reserved. Typically this would be because
186 // it overlaps an existing, possibly system, mapping.
ReserveAddressSpace(Error * error)187 bool ElfLoader::ReserveAddressSpace(Error* error) {
188   ELF::Addr min_vaddr;
189   load_size_ =
190       phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr, NULL);
191   if (load_size_ == 0) {
192     error->Set("No loadable segments");
193     return false;
194   }
195 
196   uint8_t* addr = reinterpret_cast<uint8_t*>(min_vaddr);
197   int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
198 
199   // Support loading at a fixed address.
200   if (wanted_load_address_) {
201     addr = static_cast<uint8_t*>(wanted_load_address_);
202   }
203 
204   LOG("%s: address=%p size=%p\n", __FUNCTION__, addr, load_size_);
205   void* start = mmap(addr, load_size_, PROT_NONE, mmap_flags, -1, 0);
206   if (start == MAP_FAILED) {
207     error->Format("Could not reserve %d bytes of address space", load_size_);
208     return false;
209   }
210   if (wanted_load_address_ && start != addr) {
211     error->Format("Could not map at %p requested, backing out", addr);
212     munmap(start, load_size_);
213     return false;
214   }
215 
216   load_start_ = start;
217   load_bias_ = reinterpret_cast<ELF::Addr>(start) - min_vaddr;
218   return true;
219 }
220 
221 // Returns the address of the program header table as it appears in the loaded
222 // segments in memory. This is in contrast with 'phdr_table_' which
223 // is temporary and will be released before the library is relocated.
FindPhdr(Error * error)224 bool ElfLoader::FindPhdr(Error* error) {
225   const ELF::Phdr* phdr_limit = phdr_table_ + phdr_num_;
226 
227   // If there is a PT_PHDR, use it directly.
228   for (const ELF::Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
229     if (phdr->p_type == PT_PHDR) {
230       return CheckPhdr(load_bias_ + phdr->p_vaddr, error);
231     }
232   }
233 
234   // Otherwise, check the first loadable segment. If its file offset
235   // is 0, it starts with the ELF header, and we can trivially find the
236   // loaded program header from it.
237   for (const ELF::Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
238     if (phdr->p_type == PT_LOAD) {
239       if (phdr->p_offset == 0) {
240         ELF::Addr elf_addr = load_bias_ + phdr->p_vaddr;
241         const ELF::Ehdr* ehdr = (const ELF::Ehdr*)(void*)elf_addr;
242         ELF::Addr offset = ehdr->e_phoff;
243         return CheckPhdr((ELF::Addr)ehdr + offset, error);
244       }
245       break;
246     }
247   }
248 
249   error->Set("Can't find loaded program header");
250   return false;
251 }
252 
253 // Ensures that our program header is actually within a loadable
254 // segment. This should help catch badly-formed ELF files that
255 // would cause the linker to crash later when trying to access it.
CheckPhdr(ELF::Addr loaded,Error * error)256 bool ElfLoader::CheckPhdr(ELF::Addr loaded, Error* error) {
257   const ELF::Phdr* phdr_limit = phdr_table_ + phdr_num_;
258   ELF::Addr loaded_end = loaded + (phdr_num_ * sizeof(ELF::Phdr));
259   for (ELF::Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
260     if (phdr->p_type != PT_LOAD) {
261       continue;
262     }
263     ELF::Addr seg_start = phdr->p_vaddr + load_bias_;
264     ELF::Addr seg_end = phdr->p_filesz + seg_start;
265     if (seg_start <= loaded && loaded_end <= seg_end) {
266       loaded_phdr_ = reinterpret_cast<const ELF::Phdr*>(loaded);
267       return true;
268     }
269   }
270   error->Format("Loaded program header %x not in loadable segment", loaded);
271   return false;
272 }
273 
274 // Map all loadable segments in process' address space.
275 // This assumes you already called phdr_table_reserve_memory to
276 // reserve the address space range for the library.
LoadSegments(Error * error)277 bool ElfLoader::LoadSegments(Error* error) {
278   for (size_t i = 0; i < phdr_num_; ++i) {
279     const ELF::Phdr* phdr = &phdr_table_[i];
280 
281     if (phdr->p_type != PT_LOAD) {
282       continue;
283     }
284 
285     // Segment addresses in memory.
286     ELF::Addr seg_start = phdr->p_vaddr + load_bias_;
287     ELF::Addr seg_end = seg_start + phdr->p_memsz;
288 
289     ELF::Addr seg_page_start = PAGE_START(seg_start);
290     ELF::Addr seg_page_end = PAGE_END(seg_end);
291 
292     ELF::Addr seg_file_end = seg_start + phdr->p_filesz;
293 
294     // File offsets.
295     ELF::Addr file_start = phdr->p_offset;
296     ELF::Addr file_end = file_start + phdr->p_filesz;
297 
298     ELF::Addr file_page_start = PAGE_START(file_start);
299     ELF::Addr file_length = file_end - file_page_start;
300 
301     LOG("%s: file_offset=%p file_length=%p start_address=%p end_address=%p\n",
302         __FUNCTION__,
303         file_offset_ + file_page_start,
304         file_length,
305         seg_page_start,
306         seg_page_start + PAGE_END(file_length));
307 
308     if (file_length != 0) {
309       void* seg_addr = fd_.Map((void*)seg_page_start,
310                                file_length,
311                                PFLAGS_TO_PROT(phdr->p_flags),
312                                MAP_FIXED | MAP_PRIVATE,
313                                file_page_start + file_offset_);
314       if (seg_addr == MAP_FAILED) {
315         if (errno == EACCES) {
316           error->Format("Could not map segment %d: %s. "
317                         "If you are running L-preview, please upgrade to L.",
318                         i, strerror(errno));
319         } else {
320           error->Format("Could not map segment %d: %s", i, strerror(errno));
321         }
322         return false;
323       }
324     }
325 
326     // if the segment is writable, and does not end on a page boundary,
327     // zero-fill it until the page limit.
328     if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) {
329       memset((void*)seg_file_end, 0, PAGE_SIZE - PAGE_OFFSET(seg_file_end));
330     }
331 
332     seg_file_end = PAGE_END(seg_file_end);
333 
334     // seg_file_end is now the first page address after the file
335     // content. If seg_end is larger, we need to zero anything
336     // between them. This is done by using a private anonymous
337     // map for all extra pages.
338     if (seg_page_end > seg_file_end) {
339       void* zeromap = mmap((void*)seg_file_end,
340                            seg_page_end - seg_file_end,
341                            PFLAGS_TO_PROT(phdr->p_flags),
342                            MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE,
343                            -1,
344                            0);
345       if (zeromap == MAP_FAILED) {
346         error->Format("Could not zero-fill gap: %s", strerror(errno));
347         return false;
348       }
349     }
350   }
351   return true;
352 }
353 
354 }  // namespace crazy
355