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
13namespace crazy {
14
15bool 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
115bool 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