1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *  * Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 *  * Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in
12 *    the documentation and/or other materials provided with the
13 *    distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <ctype.h>
30#include <elf.h>
31#include <inttypes.h>
32#include <link.h>
33#include <stdio.h>
34#include <string.h>
35#include <stdlib.h>
36
37#include <vector>
38
39#include "MapData.h"
40
41// Format of /proc/<PID>/maps:
42//   6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so
43static MapEntry* parse_line(char* line) {
44  uintptr_t start;
45  uintptr_t end;
46  uintptr_t offset;
47  char permissions[5];
48  int name_pos;
49  if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %*x:%*x %*d %n", &start,
50             &end, permissions, &offset, &name_pos) < 2) {
51    return nullptr;
52  }
53
54  const char* name = line + name_pos;
55  size_t name_len = strlen(name);
56  if (name_len && name[name_len - 1] == '\n') {
57    name_len -= 1;
58  }
59
60  MapEntry* entry = new MapEntry(start, end, offset, name, name_len);
61  if (permissions[0] != 'r') {
62    // Any unreadable map will just get a zero load base.
63    entry->load_base = 0;
64    entry->load_base_read = true;
65  }
66  return entry;
67}
68
69template<typename T>
70static inline bool get_val(MapEntry* entry, uintptr_t addr, T* store) {
71  if (addr < entry->start || addr + sizeof(T) > entry->end) {
72    return false;
73  }
74  // Make sure the address is aligned properly.
75  if (addr & (sizeof(T)-1)) {
76    return false;
77  }
78  *store = *reinterpret_cast<T*>(addr);
79  return true;
80}
81
82static void read_loadbase(MapEntry* entry) {
83  entry->load_base = 0;
84  entry->load_base_read = true;
85  uintptr_t addr = entry->start;
86  ElfW(Ehdr) ehdr;
87  if (!get_val<ElfW(Half)>(entry, addr + offsetof(ElfW(Ehdr), e_phnum), &ehdr.e_phnum)) {
88    return;
89  }
90  if (!get_val<ElfW(Off)>(entry, addr + offsetof(ElfW(Ehdr), e_phoff), &ehdr.e_phoff)) {
91    return;
92  }
93  addr += ehdr.e_phoff;
94  for (size_t i = 0; i < ehdr.e_phnum; i++) {
95    ElfW(Phdr) phdr;
96    if (!get_val<ElfW(Word)>(entry, addr + offsetof(ElfW(Phdr), p_type), &phdr.p_type)) {
97      return;
98    }
99    if (!get_val<ElfW(Off)>(entry, addr + offsetof(ElfW(Phdr), p_offset), &phdr.p_offset)) {
100      return;
101    }
102    if (phdr.p_type == PT_LOAD && phdr.p_offset == entry->offset) {
103      if (!get_val<ElfW(Addr)>(entry, addr + offsetof(ElfW(Phdr), p_vaddr), &phdr.p_vaddr)) {
104        return;
105      }
106      entry->load_base = phdr.p_vaddr;
107      return;
108    }
109    addr += sizeof(phdr);
110  }
111}
112
113bool MapData::ReadMaps() {
114  FILE* fp = fopen("/proc/self/maps", "re");
115  if (fp == nullptr) {
116    return false;
117  }
118
119  std::vector<char> buffer(1024);
120  while (fgets(buffer.data(), buffer.size(), fp) != nullptr) {
121    MapEntry* entry = parse_line(buffer.data());
122    if (entry == nullptr) {
123      fclose(fp);
124      return false;
125    }
126
127    auto it = entries_.find(entry);
128    if (it == entries_.end()) {
129      entries_.insert(entry);
130    } else {
131      delete entry;
132    }
133  }
134  fclose(fp);
135  return true;
136}
137
138MapData::~MapData() {
139  for (auto* entry : entries_) {
140    delete entry;
141  }
142  entries_.clear();
143}
144
145// Find the containing map info for the PC.
146const MapEntry* MapData::find(uintptr_t pc, uintptr_t* rel_pc) {
147  MapEntry pc_entry(pc);
148
149  std::lock_guard<std::mutex> lock(m_);
150
151  auto it = entries_.find(&pc_entry);
152  if (it == entries_.end()) {
153    ReadMaps();
154  }
155  it = entries_.find(&pc_entry);
156  if (it == entries_.end()) {
157    return nullptr;
158  }
159
160  MapEntry *entry = *it;
161  if (!entry->load_base_read) {
162    read_loadbase(entry);
163  }
164  if (rel_pc) {
165    *rel_pc = pc - entry->start + entry->load_base;
166  }
167  return entry;
168}
169