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
16namespace 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
24ElfLoader::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
38ElfLoader::~ElfLoader() {
39  if (phdr_mmap_) {
40    // Deallocate the temporary program header copy.
41    munmap(phdr_mmap_, phdr_size_);
42  }
43}
44
45bool 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
103bool 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.
151bool 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.
187bool 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.
224bool 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.
256bool 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.
277bool 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