• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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