1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "dso.h"
18
19#include <stdlib.h>
20#include <string.h>
21
22#include <algorithm>
23#include <limits>
24#include <vector>
25
26#include <android-base/logging.h>
27
28#include "environment.h"
29#include "read_apk.h"
30#include "read_elf.h"
31#include "utils.h"
32
33static OneTimeFreeAllocator symbol_name_allocator;
34
35Symbol::Symbol(const std::string& name, uint64_t addr, uint64_t len)
36    : addr(addr),
37      len(len),
38      name_(symbol_name_allocator.AllocateString(name)),
39      demangled_name_(nullptr) {
40}
41
42const char* Symbol::DemangledName() const {
43  if (demangled_name_ == nullptr) {
44    const std::string s = Dso::Demangle(name_);
45    if (s == name_) {
46      demangled_name_ = name_;
47    } else {
48      demangled_name_ = symbol_name_allocator.AllocateString(s);
49    }
50  }
51  return demangled_name_;
52}
53
54bool Dso::demangle_ = true;
55std::string Dso::symfs_dir_;
56std::string Dso::vmlinux_;
57std::unordered_map<std::string, BuildId> Dso::build_id_map_;
58size_t Dso::dso_count_;
59
60void Dso::SetDemangle(bool demangle) {
61  demangle_ = demangle;
62}
63
64extern "C" char* __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status);
65
66std::string Dso::Demangle(const std::string& name) {
67  if (!demangle_) {
68    return name;
69  }
70  int status;
71  bool is_linker_symbol = (name.find(linker_prefix) == 0);
72  const char* mangled_str = name.c_str();
73  if (is_linker_symbol) {
74    mangled_str += linker_prefix.size();
75  }
76  std::string result = name;
77  char* demangled_name = __cxa_demangle(mangled_str, nullptr, nullptr, &status);
78  if (status == 0) {
79    if (is_linker_symbol) {
80      result = std::string("[linker]") + demangled_name;
81    } else {
82      result = demangled_name;
83    }
84    free(demangled_name);
85  } else if (is_linker_symbol) {
86    result = std::string("[linker]") + mangled_str;
87  }
88  return result;
89}
90
91bool Dso::SetSymFsDir(const std::string& symfs_dir) {
92  std::string dirname = symfs_dir;
93  if (!dirname.empty()) {
94    if (dirname.back() != '/') {
95      dirname.push_back('/');
96    }
97    std::vector<std::string> files;
98    std::vector<std::string> subdirs;
99    GetEntriesInDir(symfs_dir, &files, &subdirs);
100    if (files.empty() && subdirs.empty()) {
101      LOG(ERROR) << "Invalid symfs_dir '" << symfs_dir << "'";
102      return false;
103    }
104  }
105  symfs_dir_ = dirname;
106  return true;
107}
108
109void Dso::SetVmlinux(const std::string& vmlinux) {
110  vmlinux_ = vmlinux;
111}
112
113void Dso::SetBuildIds(const std::vector<std::pair<std::string, BuildId>>& build_ids) {
114  std::unordered_map<std::string, BuildId> map;
115  for (auto& pair : build_ids) {
116    LOG(DEBUG) << "build_id_map: " << pair.first << ", " << pair.second.ToString();
117    map.insert(pair);
118  }
119  build_id_map_ = std::move(map);
120}
121
122BuildId Dso::GetExpectedBuildId(const std::string& filename) {
123  auto it = build_id_map_.find(filename);
124  if (it != build_id_map_.end()) {
125    return it->second;
126  }
127  return BuildId();
128}
129
130std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, const std::string& dso_path) {
131  std::string path = dso_path;
132  if (dso_type == DSO_KERNEL) {
133    path = "[kernel.kallsyms]";
134  }
135  return std::unique_ptr<Dso>(new Dso(dso_type, path));
136}
137
138Dso::Dso(DsoType type, const std::string& path)
139    : type_(type), path_(path), min_vaddr_(std::numeric_limits<uint64_t>::max()), is_loaded_(false) {
140  dso_count_++;
141}
142
143Dso::~Dso() {
144  if (--dso_count_ == 0) {
145    symbol_name_allocator.Clear();
146  }
147}
148
149struct SymbolComparator {
150  bool operator()(const Symbol& symbol1, const Symbol& symbol2) {
151    return symbol1.addr < symbol2.addr;
152  }
153};
154
155std::string Dso::GetAccessiblePath() const {
156  return symfs_dir_ + path_;
157}
158
159const Symbol* Dso::FindSymbol(uint64_t vaddr_in_dso) {
160  if (!is_loaded_) {
161    is_loaded_ = true;
162    if (!Load()) {
163      LOG(DEBUG) << "failed to load dso: " << path_;
164      return nullptr;
165    }
166  }
167
168  auto it = std::upper_bound(symbols_.begin(), symbols_.end(), Symbol("", vaddr_in_dso, 0),
169                             SymbolComparator());
170  if (it != symbols_.begin()) {
171    --it;
172    if (it->addr <= vaddr_in_dso && it->addr + it->len > vaddr_in_dso) {
173      return &*it;
174    }
175  }
176  return nullptr;
177}
178
179uint64_t Dso::MinVirtualAddress() {
180  if (min_vaddr_ == std::numeric_limits<uint64_t>::max()) {
181    min_vaddr_ = 0;
182    if (type_ == DSO_ELF_FILE) {
183      BuildId build_id = GetExpectedBuildId(GetAccessiblePath());
184
185      uint64_t addr;
186      if (ReadMinExecutableVirtualAddressFromElfFile(GetAccessiblePath(), build_id, &addr)) {
187        min_vaddr_ = addr;
188      }
189    }
190  }
191  return min_vaddr_;
192}
193
194bool Dso::Load() {
195  bool result = false;
196  switch (type_) {
197    case DSO_KERNEL:
198      result = LoadKernel();
199      break;
200    case DSO_KERNEL_MODULE:
201      result = LoadKernelModule();
202      break;
203    case DSO_ELF_FILE: {
204      if (std::get<0>(SplitUrlInApk(path_))) {
205        result = LoadEmbeddedElfFile();
206      } else {
207        result = LoadElfFile();
208      }
209      break;
210    }
211  }
212  if (result) {
213    std::sort(symbols_.begin(), symbols_.end(), SymbolComparator());
214    FixupSymbolLength();
215  }
216  return result;
217}
218
219static bool IsKernelFunctionSymbol(const KernelSymbol& symbol) {
220  return (symbol.type == 'T' || symbol.type == 't' || symbol.type == 'W' || symbol.type == 'w');
221}
222
223bool Dso::KernelSymbolCallback(const KernelSymbol& kernel_symbol, Dso* dso) {
224  if (IsKernelFunctionSymbol(kernel_symbol)) {
225    dso->InsertSymbol(Symbol(kernel_symbol.name, kernel_symbol.addr, 0));
226  }
227  return false;
228}
229
230void Dso::VmlinuxSymbolCallback(const ElfFileSymbol& elf_symbol, Dso* dso) {
231  if (elf_symbol.is_func) {
232    dso->InsertSymbol(Symbol(elf_symbol.name, elf_symbol.vaddr, elf_symbol.len));
233  }
234}
235
236bool Dso::LoadKernel() {
237  BuildId build_id = GetExpectedBuildId(DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID);
238  if (!vmlinux_.empty()) {
239    ParseSymbolsFromElfFile(vmlinux_, build_id,
240                            std::bind(VmlinuxSymbolCallback, std::placeholders::_1, this));
241  } else {
242    if (!build_id.IsEmpty()) {
243      BuildId real_build_id;
244      GetKernelBuildId(&real_build_id);
245      bool match = (build_id == real_build_id);
246      LOG(DEBUG) << "check kernel build id (" << (match ? "match" : "mismatch") << "): expected "
247                 << build_id.ToString() << ", real " << real_build_id.ToString();
248      if (!match) {
249        return false;
250      }
251    }
252
253    ProcessKernelSymbols("/proc/kallsyms",
254                         std::bind(&KernelSymbolCallback, std::placeholders::_1, this));
255    bool allZero = true;
256    for (auto& symbol : symbols_) {
257      if (symbol.addr != 0) {
258        allZero = false;
259        break;
260      }
261    }
262    if (allZero) {
263      LOG(WARNING) << "Symbol addresses in /proc/kallsyms are all zero. Check "
264                      "/proc/sys/kernel/kptr_restrict if possible.";
265      symbols_.clear();
266      return false;
267    }
268  }
269  return true;
270}
271
272void Dso::ElfFileSymbolCallback(const ElfFileSymbol& elf_symbol, Dso* dso,
273                                bool (*filter)(const ElfFileSymbol&)) {
274  if (filter(elf_symbol)) {
275    dso->InsertSymbol(Symbol(elf_symbol.name, elf_symbol.vaddr, elf_symbol.len));
276  }
277}
278
279static bool SymbolFilterForKernelModule(const ElfFileSymbol& elf_symbol) {
280  // TODO: Parse symbol outside of .text section.
281  return (elf_symbol.is_func && elf_symbol.is_in_text_section);
282}
283
284bool Dso::LoadKernelModule() {
285  BuildId build_id = GetExpectedBuildId(path_);
286  ParseSymbolsFromElfFile(
287      symfs_dir_ + path_, build_id,
288      std::bind(ElfFileSymbolCallback, std::placeholders::_1, this, SymbolFilterForKernelModule));
289  return true;
290}
291
292static bool SymbolFilterForDso(const ElfFileSymbol& elf_symbol) {
293  return elf_symbol.is_func || (elf_symbol.is_label && elf_symbol.is_in_text_section);
294}
295
296bool Dso::LoadElfFile() {
297  bool loaded = false;
298  BuildId build_id = GetExpectedBuildId(GetAccessiblePath());
299
300  if (symfs_dir_.empty()) {
301    // Linux host can store debug shared libraries in /usr/lib/debug.
302    loaded = ParseSymbolsFromElfFile(
303        "/usr/lib/debug" + path_, build_id,
304        std::bind(ElfFileSymbolCallback, std::placeholders::_1, this, SymbolFilterForDso));
305  }
306  if (!loaded) {
307    loaded = ParseSymbolsFromElfFile(
308        GetAccessiblePath(), build_id,
309        std::bind(ElfFileSymbolCallback, std::placeholders::_1, this, SymbolFilterForDso));
310  }
311  return loaded;
312}
313
314bool Dso::LoadEmbeddedElfFile() {
315  std::string path = GetAccessiblePath();
316  BuildId build_id = GetExpectedBuildId(path);
317  auto tuple = SplitUrlInApk(path);
318  CHECK(std::get<0>(tuple));
319  return ParseSymbolsFromApkFile(std::get<1>(tuple), std::get<2>(tuple), build_id,
320                                 std::bind(ElfFileSymbolCallback, std::placeholders::_1,
321                                           this, SymbolFilterForDso));
322}
323
324void Dso::InsertSymbol(const Symbol& symbol) {
325  symbols_.push_back(symbol);
326}
327
328void Dso::FixupSymbolLength() {
329  Symbol* prev_symbol = nullptr;
330  for (auto& symbol : symbols_) {
331    if (prev_symbol != nullptr && prev_symbol->len == 0) {
332      prev_symbol->len = symbol.addr - prev_symbol->addr;
333    }
334    prev_symbol = &symbol;
335  }
336  if (prev_symbol != nullptr && prev_symbol->len == 0) {
337    prev_symbol->len = std::numeric_limits<unsigned long long>::max() - prev_symbol->addr;
338  }
339}
340