1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <cstdio>
16 #define HILOG_TAG "RuntimeThread"
17
18 #include "virtual_thread.h"
19
20 #include <cinttypes>
21 #include <iostream>
22 #include <sstream>
23 #if !is_mingw
24 #include <sys/mman.h>
25 #endif
26
27 #include "symbols.h"
28 #include "utilities.h"
29 #include "virtual_runtime.h"
30 namespace OHOS {
31 namespace Developtools {
32 namespace NativeDaemon {
VirtualThread(pid_t pid,pid_t tid,const std::set<std::unique_ptr<SymbolsFile>,CCompareSymbolsFile> & symbolsFiles,VirtualRuntime * runtime,bool parseFlag)33 VirtualThread::VirtualThread(pid_t pid,
34 pid_t tid,
35 const std::set<std::unique_ptr<SymbolsFile>, CCompareSymbolsFile>& symbolsFiles,
36 VirtualRuntime* runtime,
37 bool parseFlag)
38 : pid_(pid), tid_(tid), symbolsFiles_(symbolsFiles), virtualruntime_(runtime)
39 {
40 if (parseFlag) {
41 pthread_mutex_lock(&virtualruntime_->threadMemMapsLock_);
42 if (virtualruntime_->processMemMaps_.size() == 0) {
43 this->ParseMap(virtualruntime_->processMemMaps_);
44 }
45 pthread_mutex_unlock(&virtualruntime_->threadMemMapsLock_);
46 }
47
48 memMaps_ = &virtualruntime_->processMemMaps_;
49 this->name_ = ReadThreadName(pid);
50 reg_nr = RegisterGetCount();
51 if (reg_nr <= 0) {
52 HLOGE("Getting register count failed");
53 reg_nr = 0;
54 user_regs = nullptr;
55 } else if (reg_nr != std::numeric_limits<size_t>::max()) {
56 user_regs = new (std::nothrow) u64[reg_nr];
57 if (!user_regs) {
58 HLOGM("new regs failed");
59 }
60 if (memset_s(user_regs, sizeof(u64) * reg_nr, 0, sizeof(u64) * reg_nr) != EOK) {
61 HLOGM("memset_s regs failed");
62 }
63 } else {
64 reg_nr = 0;
65 user_regs = nullptr;
66 }
67 HLOGM("%d %d map from parent size is %zu", pid, tid, memMaps_->size());
68 }
69
ReadThreadName(pid_t tid)70 std::string VirtualThread::ReadThreadName(pid_t tid)
71 {
72 std::string comm = ReadFileToString(StringPrintf("/proc/%d/comm", tid)).c_str();
73 comm.erase(std::remove(comm.begin(), comm.end(), '\r'), comm.end());
74 comm.erase(std::remove(comm.begin(), comm.end(), '\n'), comm.end());
75 return comm;
76 }
77
FindMapByAddr(uint64_t addr,MemMapItem & outMap) const78 bool VirtualThread::FindMapByAddr(uint64_t addr, MemMapItem &outMap) const
79 {
80 HLOGM("try found vaddr 0x%" PRIx64 " in maps %zu ", addr, memMaps_->size());
81 for (auto &map : *memMaps_) {
82 if (addr >= map.begin_ && addr < map.end_) {
83 outMap = map;
84 HLOGMMM("found vaddr 0x%" PRIx64 " in map fileoffset 0x%" PRIx64 " (0x%" PRIx64
85 " - 0x%" PRIx64 " pageoffset 0x%" PRIx64 ") from %s",
86 addr, outMap.FileOffsetFromAddr(addr), map.begin_, map.end_, map.pageoffset_,
87 map.name_.c_str());
88 return true;
89 }
90 }
91 HLOGM("NOT found vaddr 0x%" PRIx64 " in maps %zu ", addr, memMaps_->size());
92 return false;
93 }
94
FindMapByFileInfo(const std::string name,uint64_t offset,MemMapItem & outMap) const95 bool VirtualThread::FindMapByFileInfo(const std::string name, uint64_t offset,
96 MemMapItem& outMap) const
97 {
98 for (auto &map : *memMaps_) {
99 if (name != map.name_) {
100 continue;
101 }
102 // check begin and length
103 if (offset >= map.pageoffset_ && (offset - map.pageoffset_) < (map.end_ - map.begin_)) {
104 outMap = map;
105 HLOGMMM("found fileoffset 0x%" PRIx64 " in map (0x%" PRIx64 " - 0x%" PRIx64
106 " pageoffset 0x%" PRIx64 ") from %s",
107 offset, map.begin_, map.end_, map.pageoffset_, map.name_.c_str());
108 return true;
109 } else {
110 HLOGM("* fail to found fileoffset 0x%" PRIx64 " in map (0x%" PRIx64 " - 0x%" PRIx64
111 " pageoffset 0x%" PRIx64 ") from %s",
112 offset, map.begin_, map.end_, map.pageoffset_, map.name_.c_str());
113 }
114 }
115 HLOGM("NOT found offset 0x%" PRIx64 " in maps %zu ", offset, memMaps_->size());
116 return false;
117 }
118
FindSymbolsFileByMap(const MemMapItem & inMap) const119 SymbolsFile *VirtualThread::FindSymbolsFileByMap(const MemMapItem &inMap) const
120 {
121 for (auto &symbolsFile : symbolsFiles_) {
122 if (symbolsFile->filePath_ == inMap.name_) {
123 HLOGM("found symbol for map '%s'", inMap.name_.c_str());
124 if (!symbolsFile->Loaded()) {
125 symbolsFile->LoadSymbols();
126 }
127 return symbolsFile.get();
128 }
129 }
130
131 if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), inMap.name_) ==
132 missedSymbolFile_.end()) {
133 missedSymbolFile_.emplace_back(inMap.name_);
134 HLOGW("NOT found symbol for map '%s'", inMap.name_.c_str());
135 for (auto &file : symbolsFiles_) {
136 HLOGW(" we have '%s'", file->filePath_.c_str());
137 }
138 }
139
140 return nullptr;
141 }
142
ReadRoMemory(uint64_t addr,uint8_t * data,size_t size) const143 bool VirtualThread::ReadRoMemory(uint64_t addr, uint8_t *data, size_t size) const
144 {
145 MemMapItem map {};
146 if (FindMapByAddr(addr, map)) {
147 // found symbols by file name
148 SymbolsFile *symbolsFile = FindSymbolsFileByMap(map);
149 if (symbolsFile != nullptr) {
150 HLOGM("read vaddr from addr is 0x%" PRIx64 " at '%s'", addr - map.begin_,
151 map.name_.c_str());
152 if (size == symbolsFile->ReadRoMemory(map.FileOffsetFromAddr(addr), data, size)) {
153 return true;
154 } else {
155 return false;
156 }
157 } else {
158 HLOGW("found addr %" PRIx64 " in map but not loaded symbole %s", addr,
159 map.name_.c_str());
160 }
161 } else {
162 HLOGW("have not found addr %" PRIx64 " in any map", addr);
163 }
164 return false;
165 }
166
IsLegalFileName(const std::string & fileName)167 bool VirtualThread::IsLegalFileName(const std::string &fileName)
168 {
169 if (fileName.empty() or StringStartsWith(fileName, "/dev/") or
170 fileName.find(':') != std::string::npos or fileName.front() == '[' or
171 fileName.back() == ']' or StringEndsWith(fileName, ".ttf") or
172 fileName == MMAP_ANONYMOUS_OHOS_NAME) {
173 return false;
174 }
175 return true;
176 }
177
178 #if is_mingw
ParseMap()179 void VirtualThread::ParseMap()
180 {
181 // only linux support read maps in runtime
182 return;
183 }
184 #else
185 constexpr const int MMAP_LINE_MIN_TOKEN = 5;
186 constexpr const int MMAP_LINE_TOKEN_INDEX_FLAG = 1;
187 constexpr const int MMAP_LINE_TOKEN_INDEX_OFFSET = 2;
188 constexpr const int MMAP_LINE_TOKEN_INDEX_MM = 3;
189 constexpr const int MMAP_LINE_TOKEN_INDEX_INODE = 4;
190 constexpr const int MMAP_LINE_TOKEN_INDEX_NAME = 5;
191 constexpr const int MMAP_LINE_MAX_TOKEN = 6;
192
ParseMap(std::vector<MemMapItem> & memMaps)193 void VirtualThread::ParseMap(std::vector<MemMapItem>& memMaps)
194 {
195 std::string mapPath = StringPrintf("/proc/%d/maps", pid_);
196 std::string mapContent = ReadFileToString(mapPath);
197 if (mapContent.size() > 0) {
198 std::istringstream s(mapContent);
199 std::string line;
200 while (std::getline(s, line)) {
201 HLOGM("map line: %s", line.c_str());
202 // b0023000-b0024000 r--p 00000000 b3:05 959 /system/lib/libdl.so
203 // 0 1 2 3 4 5
204 MemMapItem memMapItem;
205 std::vector<std::string> mapTokens = StringSplit(line, " ");
206
207 if (mapTokens.size() < MMAP_LINE_MIN_TOKEN) {
208 // maybe file name is empty
209 continue;
210 }
211
212 // b0023000-b0024000
213 constexpr const int MMAP_ADDR_RANGE_TOKEN = 2;
214 std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
215 if (addrRanges.size() != MMAP_ADDR_RANGE_TOKEN) {
216 continue;
217 }
218
219 // b0023000 / b0024000
220 try {
221 memMapItem.begin_ = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE);
222 memMapItem.end_ = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE);
223 } catch (...) {
224 // next line
225 continue;
226 }
227
228 constexpr const int MMAP_PROT_CHARS = 4;
229 int index = 0;
230 // rwxp
231 memMapItem.type_ = 0;
232 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG].size() != MMAP_PROT_CHARS) {
233 continue;
234 }
235 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'r') {
236 memMapItem.type_ |= PROT_READ;
237 }
238 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'w') {
239 memMapItem.type_ |= PROT_WRITE;
240 }
241 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'x') {
242 memMapItem.type_ |= PROT_EXEC;
243 }
244
245 if ((memMapItem.type_ & PROT_EXEC) || (memMapItem.type_ & PROT_READ)) {
246 /*
247 we need record the read hen exec map address
248 callstackk need r map to check the ehframe addrssss
249 Section Headers:
250 [Nr] Name Type Address Offset
251 Size EntSize Flags Link Info Align
252
253 [12] .eh_frame_hdr PROGBITS 00000000002929a0 000929a0
254 00000000000071fc 0000000000000000 A 0 0 4
255 [13] .eh_frame PROGBITS 0000000000299ba0 00099ba0
256 000000000002a8f4 0000000000000000 A 0 0 8
257 [14] .text PROGBITS 00000000002c5000 000c5000
258 00000000001caa4a 0000000000000000 AX 0 0 16
259
260 00200000-002c5000 r--p 00000000 08:02 46400311
261 002c5000-00490000 r-xp 000c5000 08:02 46400311
262 */
263 } else {
264 continue;
265 }
266
267 // MAP_PRIVATE or MAP_SHARED
268 constexpr const int MAP_FLAG_ATTR_INDEX = 3;
269 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 'p') {
270 memMapItem.flags = MAP_PRIVATE;
271 } else if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 's') {
272 memMapItem.flags = MAP_SHARED;
273 }
274
275 try {
276 // 00000000
277 memMapItem.pageoffset_ = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_OFFSET],
278 nullptr, NUMBER_FORMAT_HEX_BASE);
279
280 // major:minor
281 std::vector<std::string> mm = StringSplit(mapTokens[MMAP_LINE_TOKEN_INDEX_MM], ":");
282
283 // b3:05
284 memMapItem.major_ = std::stoull(mm.at(0), nullptr, NUMBER_FORMAT_HEX_BASE);
285 memMapItem.minor_ = std::stoull(mm.at(1), nullptr, NUMBER_FORMAT_HEX_BASE);
286
287 // 959
288 memMapItem.inode = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_INODE], nullptr,
289 NUMBER_FORMAT_HEX_BASE);
290 } catch (...) {
291 // next line
292 continue;
293 }
294
295 // system/lib/libdl.so
296 if (mapTokens.size() == MMAP_LINE_MAX_TOKEN) {
297 memMapItem.name_ = mapTokens[MMAP_LINE_TOKEN_INDEX_NAME];
298 }
299 if (!IsLegalFileName(memMapItem.name_)) {
300 continue;
301 }
302
303 memMaps.push_back(memMapItem);
304 virtualruntime_->UpdateSymbols(memMapItem.name_);
305 pid_t pid = getpid();
306 HLOGD("%d %d memMap add '%s'", pid_, tid_, memMapItem.name_.c_str());
307 }
308 }
309 }
310 #endif
311
CreateMapItem(const std::string filename,uint64_t begin,uint64_t len,uint64_t offset)312 void VirtualThread::CreateMapItem(const std::string filename, uint64_t begin, uint64_t len,
313 uint64_t offset)
314 {
315 MemMapItem &map = memMaps_->emplace_back();
316 map.name_ = filename;
317 map.begin_ = begin;
318 map.end_ = begin + len;
319 map.pageoffset_ = offset;
320 HLOGD(" %u:%u create a new map(total %zu) at '%s' (0x%" PRIx64 "-0x%" PRIx64 ")@0x%" PRIx64 " ",
321 pid_, tid_, memMaps_->size(), map.name_.c_str(), map.begin_, map.end_, map.pageoffset_);
322 }
323 } // namespace NativeDaemon
324 } // namespace Developtools
325 } // namespace OHOS