blob: ded81a2d5c8c72dc3716d6e43a7f9e4a3a80ddbb [file] [log] [blame]
Christopher Ferris63860cb2015-11-16 17:30:32 -08001/*
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>
Christopher Ferris63860cb2015-11-16 17:30:32 -080034#include <stdlib.h>
Christopher Ferris4da25032018-03-07 13:38:48 -080035#include <string.h>
Christopher Ferrisb233fab2018-12-18 16:44:42 -080036#include <sys/mman.h>
Christopher Ferris63860cb2015-11-16 17:30:32 -080037
38#include <vector>
39
Christopher Ferris63860cb2015-11-16 17:30:32 -080040#include "MapData.h"
41
42// Format of /proc/<PID>/maps:
43// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
44static MapEntry* parse_line(char* line) {
45 uintptr_t start;
46 uintptr_t end;
47 uintptr_t offset;
Christopher Ferrisb233fab2018-12-18 16:44:42 -080048 int flags;
Christopher Ferrisf499dc92016-02-19 18:13:29 -080049 char permissions[5];
Christopher Ferris63860cb2015-11-16 17:30:32 -080050 int name_pos;
Christopher Ferris4da25032018-03-07 13:38:48 -080051 if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %*x:%*x %*d %n", &start, &end,
52 permissions, &offset, &name_pos) < 2) {
Christopher Ferris63860cb2015-11-16 17:30:32 -080053 return nullptr;
54 }
55
56 const char* name = line + name_pos;
57 size_t name_len = strlen(name);
58 if (name_len && name[name_len - 1] == '\n') {
59 name_len -= 1;
60 }
61
Christopher Ferrisb233fab2018-12-18 16:44:42 -080062 flags = 0;
63 if (permissions[0] == 'r') {
64 flags |= PROT_READ;
65 }
66 if (permissions[2] == 'x') {
67 flags |= PROT_EXEC;
68 }
69
70 MapEntry* entry = new MapEntry(start, end, offset, name, name_len, flags);
71 if (!(flags & PROT_READ)) {
72 // Any unreadable map will just get a zero load bias.
73 entry->load_bias = 0;
74 entry->init = true;
75 entry->valid = false;
Christopher Ferris63860cb2015-11-16 17:30:32 -080076 }
77 return entry;
78}
79
Christopher Ferris4da25032018-03-07 13:38:48 -080080template <typename T>
Christopher Ferris63860cb2015-11-16 17:30:32 -080081static inline bool get_val(MapEntry* entry, uintptr_t addr, T* store) {
Christopher Ferrisb233fab2018-12-18 16:44:42 -080082 if (!(entry->flags & PROT_READ) || addr < entry->start || addr + sizeof(T) > entry->end) {
Christopher Ferris63860cb2015-11-16 17:30:32 -080083 return false;
84 }
85 // Make sure the address is aligned properly.
Christopher Ferris4da25032018-03-07 13:38:48 -080086 if (addr & (sizeof(T) - 1)) {
Christopher Ferris63860cb2015-11-16 17:30:32 -080087 return false;
88 }
89 *store = *reinterpret_cast<T*>(addr);
90 return true;
91}
92
Christopher Ferrisb233fab2018-12-18 16:44:42 -080093static bool valid_elf(MapEntry* entry) {
94 uintptr_t addr = entry->start;
95 uintptr_t end;
96 if (__builtin_add_overflow(addr, SELFMAG, &end) || end >= entry->end) {
97 return false;
98 }
99
100 return memcmp(reinterpret_cast<void*>(addr), ELFMAG, SELFMAG) == 0;
101}
102
103static void read_loadbias(MapEntry* entry) {
104 entry->load_bias = 0;
Christopher Ferris63860cb2015-11-16 17:30:32 -0800105 uintptr_t addr = entry->start;
106 ElfW(Ehdr) ehdr;
107 if (!get_val<ElfW(Half)>(entry, addr + offsetof(ElfW(Ehdr), e_phnum), &ehdr.e_phnum)) {
108 return;
109 }
110 if (!get_val<ElfW(Off)>(entry, addr + offsetof(ElfW(Ehdr), e_phoff), &ehdr.e_phoff)) {
111 return;
112 }
113 addr += ehdr.e_phoff;
114 for (size_t i = 0; i < ehdr.e_phnum; i++) {
115 ElfW(Phdr) phdr;
116 if (!get_val<ElfW(Word)>(entry, addr + offsetof(ElfW(Phdr), p_type), &phdr.p_type)) {
117 return;
118 }
Weiwei.Zhang8d01fac2020-09-28 14:43:51 +0800119 if (!get_val<ElfW(Word)>(entry, addr + offsetof(ElfW(Phdr), p_flags), &phdr.p_flags)) {
120 return;
121 }
Christopher Ferris63860cb2015-11-16 17:30:32 -0800122 if (!get_val<ElfW(Off)>(entry, addr + offsetof(ElfW(Phdr), p_offset), &phdr.p_offset)) {
123 return;
124 }
Weiwei.Zhang8d01fac2020-09-28 14:43:51 +0800125 if ((phdr.p_type == PT_LOAD) && (phdr.p_flags & PF_X) ) {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800126 if (!get_val<ElfW(Addr)>(entry, addr + offsetof(ElfW(Phdr), p_vaddr), &phdr.p_vaddr)) {
127 return;
128 }
Weiwei.Zhang8d01fac2020-09-28 14:43:51 +0800129 entry->load_bias = phdr.p_vaddr - phdr.p_offset;
Christopher Ferris63860cb2015-11-16 17:30:32 -0800130 return;
131 }
132 addr += sizeof(phdr);
133 }
134}
135
Christopher Ferrisb233fab2018-12-18 16:44:42 -0800136static void inline init(MapEntry* entry) {
137 if (entry->init) {
138 return;
139 }
140 entry->init = true;
141 if (valid_elf(entry)) {
142 entry->valid = true;
143 read_loadbias(entry);
144 }
145}
146
Colin Crossd75d4be2016-02-08 14:29:03 -0800147bool MapData::ReadMaps() {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800148 FILE* fp = fopen("/proc/self/maps", "re");
149 if (fp == nullptr) {
150 return false;
151 }
152
153 std::vector<char> buffer(1024);
154 while (fgets(buffer.data(), buffer.size(), fp) != nullptr) {
155 MapEntry* entry = parse_line(buffer.data());
156 if (entry == nullptr) {
Colin Crossd75d4be2016-02-08 14:29:03 -0800157 fclose(fp);
Christopher Ferris63860cb2015-11-16 17:30:32 -0800158 return false;
159 }
Colin Crossd75d4be2016-02-08 14:29:03 -0800160
161 auto it = entries_.find(entry);
162 if (it == entries_.end()) {
163 entries_.insert(entry);
164 } else {
165 delete entry;
166 }
Christopher Ferris63860cb2015-11-16 17:30:32 -0800167 }
168 fclose(fp);
169 return true;
170}
171
Christopher Ferris63860cb2015-11-16 17:30:32 -0800172MapData::~MapData() {
Christopher Ferris63860cb2015-11-16 17:30:32 -0800173 for (auto* entry : entries_) {
174 delete entry;
175 }
176 entries_.clear();
177}
178
179// Find the containing map info for the PC.
180const MapEntry* MapData::find(uintptr_t pc, uintptr_t* rel_pc) {
Colin Crossd75d4be2016-02-08 14:29:03 -0800181 MapEntry pc_entry(pc);
182
183 std::lock_guard<std::mutex> lock(m_);
184
185 auto it = entries_.find(&pc_entry);
186 if (it == entries_.end()) {
187 ReadMaps();
188 }
189 it = entries_.find(&pc_entry);
190 if (it == entries_.end()) {
191 return nullptr;
192 }
193
Christopher Ferris4da25032018-03-07 13:38:48 -0800194 MapEntry* entry = *it;
Christopher Ferrisb233fab2018-12-18 16:44:42 -0800195 init(entry);
196
197 if (rel_pc != nullptr) {
198 // Need to check to see if this is a read-execute map and the read-only
199 // map is the previous one.
200 if (!entry->valid && it != entries_.begin()) {
201 MapEntry* prev_entry = *--it;
202 if (prev_entry->flags == PROT_READ && prev_entry->offset < entry->offset &&
203 prev_entry->name == entry->name) {
204 init(prev_entry);
205
206 if (prev_entry->valid) {
207 entry->elf_start_offset = prev_entry->offset;
208 *rel_pc = pc - entry->start + entry->offset + prev_entry->load_bias;
209 return entry;
210 }
211 }
212 }
213 *rel_pc = pc - entry->start + entry->load_bias;
Christopher Ferris63860cb2015-11-16 17:30:32 -0800214 }
Colin Crossd75d4be2016-02-08 14:29:03 -0800215 return entry;
Christopher Ferris63860cb2015-11-16 17:30:32 -0800216}