1 /*
2  * Copyright (c) 2023-2024 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 
16 #include "dfx_map.h"
17 
18 #include <algorithm>
19 #include <securec.h>
20 #include <sstream>
21 #if is_mingw
22 #include "dfx_nonlinux_define.h"
23 #else
24 #include <sys/mman.h>
25 #endif
26 
27 #include "dfx_define.h"
28 #include "dfx_elf.h"
29 #include "dfx_hap.h"
30 #include "dfx_log.h"
31 #include "dfx_util.h"
32 #include "string_util.h"
33 
34 namespace OHOS {
35 namespace HiviewDFX {
36 namespace {
37 #undef LOG_DOMAIN
38 #undef LOG_TAG
39 #define LOG_DOMAIN 0xD002D11
40 #define LOG_TAG "DfxMap"
41 
42 #if defined(is_ohos) && is_ohos
SkipWhiteSpace(const char * cp)43 AT_ALWAYS_INLINE const char* SkipWhiteSpace(const char *cp)
44 {
45     if (cp == nullptr) {
46         return nullptr;
47     }
48 
49     while (*cp == ' ' || *cp == '\t') {
50         ++cp;
51     }
52     return cp;
53 }
54 
ScanHex(const char * cp,unsigned long & valp)55 AT_ALWAYS_INLINE const char* ScanHex(const char *cp, unsigned long &valp)
56 {
57     cp = SkipWhiteSpace(cp);
58     if (cp == nullptr) {
59         return nullptr;
60     }
61 
62     unsigned long cnt = 0;
63     unsigned long val = 0;
64     while (1) {
65         unsigned long digit = *cp;
66         if ((digit - '0') <= 9) { // 9 : max 9
67             digit -= '0';
68         } else if ((digit - 'a') < 6) { // 6 : 16 - 10
69             digit -= 'a' - 10; // 10 : base 10
70         } else if ((digit - 'A') < 6) { // 6 : 16 - 10
71             digit -= 'A' - 10; // 10 : base 10
72         } else {
73             break;
74         }
75         val = (val << 4) | digit; // 4 : hex
76         ++cnt;
77         ++cp;
78     }
79     if (cnt == 0) {
80         return nullptr;
81     }
82 
83     valp = val;
84     return cp;
85 }
86 
ScanDec(const char * cp,unsigned long & valp)87 AT_ALWAYS_INLINE const char* ScanDec(const char *cp, unsigned long &valp)
88 {
89     cp = SkipWhiteSpace(cp);
90     if (cp == nullptr) {
91         return nullptr;
92     }
93 
94     unsigned long cnt = 0;
95     unsigned long digit = 0;
96     unsigned long val = 0;
97     while (1) {
98         digit = *cp;
99         if ((digit - '0') <= 9) { // 9 : max 9
100             digit -= '0';
101             ++cp;
102         } else {
103             break;
104         }
105 
106         val = (10 * val) + digit; // 10 : base 10
107         ++cnt;
108     }
109     if (cnt == 0) {
110         return nullptr;
111     }
112 
113     valp = val;
114     return cp;
115 }
116 
ScanChar(const char * cp,char & valp)117 AT_ALWAYS_INLINE const char* ScanChar(const char *cp, char &valp)
118 {
119     cp = SkipWhiteSpace(cp);
120     if (cp == nullptr) {
121         return nullptr;
122     }
123 
124     valp = *cp;
125 
126     /* don't step over NUL terminator */
127     if (*cp) {
128         ++cp;
129     }
130     return cp;
131 }
132 
ScanString(const char * cp,char * valp,size_t size)133 AT_ALWAYS_INLINE const char* ScanString(const char *cp, char *valp, size_t size)
134 {
135     cp = SkipWhiteSpace(cp);
136     if (cp == nullptr) {
137         return nullptr;
138     }
139 
140     size_t i = 0;
141     while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
142         if ((valp != nullptr) && (i < size - 1)) {
143             valp[i++] = *cp;
144         }
145         ++cp;
146     }
147     if (i == 0 || i >= size) {
148         return nullptr;
149     }
150     valp[i] = '\0';
151     return cp;
152 }
153 
PermsToProtsAndFlag(const char * permChs,const size_t sz,uint32_t & prots,uint32_t & flag)154 AT_ALWAYS_INLINE bool PermsToProtsAndFlag(const char* permChs, const size_t sz, uint32_t& prots, uint32_t& flag)
155 {
156     if (permChs == nullptr || sz < 4) { // 4 : min perms size
157         return false;
158     }
159 
160     size_t i = 0;
161     if (permChs[i] == 'r') {
162         prots |= PROT_READ;
163     } else if (permChs[i] != '-') {
164         return false;
165     }
166     i++;
167 
168     if (permChs[i] == 'w') {
169         prots |= PROT_WRITE;
170     } else if (permChs[i] != '-') {
171         return false;
172     }
173     i++;
174 
175     if (permChs[i] == 'x') {
176         prots |= PROT_EXEC;
177     } else if (permChs[i] != '-') {
178         return false;
179     }
180     i++;
181 
182     if (permChs[i] == 'p') {
183         flag = MAP_PRIVATE;
184     } else if (permChs[i] == 's') {
185         flag = MAP_SHARED;
186     } else {
187         return false;
188     }
189 
190     return true;
191 }
192 #endif
193 }
194 
Create(std::string buf,size_t size)195 std::shared_ptr<DfxMap> DfxMap::Create(std::string buf, size_t size)
196 {
197     if (buf.empty() || size == 0) {
198         return nullptr;
199     }
200     auto map = std::make_shared<DfxMap>();
201     if (!map->Parse(&buf[0], size)) {
202         LOGW("Failed to parse map: %s", buf.c_str());
203         return nullptr;
204     }
205     return map;
206 }
207 
Parse(char * buf,size_t size)208 bool DfxMap::Parse(char* buf, size_t size)
209 {
210 #if defined(is_ohos) && is_ohos
211     if (buf == nullptr || size == 0) {
212         return false;
213     }
214 
215     char permChs[5] = {0}; // 5 : rwxp
216     char dash = 0;
217     char colon = 0;
218     unsigned long tmp = 0;
219     const char *path = nullptr;
220     const char* cp = buf;
221 
222     // 7658d38000-7658d40000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
223     /* scan: "begin-end perms offset major:minor inum path" */
224     cp = ScanHex(cp, tmp);
225     begin = static_cast<uint64_t>(tmp);
226     cp = ScanChar(cp, dash);
227     if (dash != '-') {
228         return false;
229     }
230     cp = ScanHex(cp, tmp);
231     end = static_cast<uint64_t>(tmp);
232     cp = ScanString(cp, permChs, sizeof(permChs));
233     if (!PermsToProtsAndFlag(permChs, sizeof(permChs), prots, flag)) {
234         return false;
235     }
236     cp = ScanHex(cp, tmp);
237     offset = static_cast<uint64_t>(tmp);
238     cp = ScanHex(cp, tmp);
239     major = static_cast<uint64_t>(tmp);
240     cp = ScanChar(cp, colon);
241     if (colon != ':') {
242         return false;
243     }
244     cp = ScanHex(cp, tmp);
245     minor = static_cast<uint64_t>(tmp);
246     cp = ScanDec(cp, tmp);
247     inode = static_cast<ino_t>(tmp);
248     path = SkipWhiteSpace(cp);
249 
250     perms = std::string(permChs, sizeof(permChs));
251     TrimAndDupStr(path, name);
252     return true;
253 #else
254     return false;
255 #endif
256 }
257 
IsMapExec()258 bool DfxMap::IsMapExec()
259 {
260     if ((prots & PROT_EXEC) != 0) {
261         return true;
262     }
263     return false;
264 }
265 
IsArkExecutable()266 bool DfxMap::IsArkExecutable()
267 {
268     if (name.length() == 0) {
269         return false;
270     }
271 
272     if ((!StartsWith(name, "[anon:ArkTS Code")) && (!StartsWith(name, "/dev/zero")) && (!EndsWith(name, "stub.an"))) {
273         return false;
274     }
275 
276     if (!IsMapExec()) {
277         LOGU("Current ark map(%s) is not exec", name.c_str());
278         return false;
279     }
280     LOGU("Current ark map: %s", name.c_str());
281     return true;
282 }
283 
IsVdsoMap()284 bool DfxMap::IsVdsoMap()
285 {
286     if ((name == "[shmm]" || name == "[vdso]") && IsMapExec()) {
287         return true;
288     }
289     return false;
290 }
291 
GetRelPc(uint64_t pc)292 uint64_t DfxMap::GetRelPc(uint64_t pc)
293 {
294     if (GetElf() != nullptr) {
295         return GetElf()->GetRelPc(pc, begin, offset);
296     }
297     return (pc - begin + offset);
298 }
299 
ToString()300 std::string DfxMap::ToString()
301 {
302     char buf[LINE_BUF_SIZE] = {0};
303     int ret = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%" PRIx64 "-%" PRIx64 " %s %08" PRIx64 " %s\n", \
304         begin, end, perms.c_str(), offset, name.c_str());
305     if (ret <= 0) {
306         LOGE("%s :: snprintf_s failed, line: %d.", __func__, __LINE__);
307     }
308     return std::string(buf);
309 }
310 
PermsToProts(const std::string perms,uint32_t & prots,uint32_t & flag)311 void DfxMap::PermsToProts(const std::string perms, uint32_t& prots, uint32_t& flag)
312 {
313     // rwxp
314     if (perms.find("r") != std::string::npos) {
315         prots |= PROT_READ;
316     }
317 
318     if (perms.find("w") != std::string::npos) {
319         prots |= PROT_WRITE;
320     }
321 
322     if (perms.find("x") != std::string::npos) {
323         prots |= PROT_EXEC;
324     }
325 
326     if (perms.find("p") != std::string::npos) {
327         flag = MAP_PRIVATE;
328     } else if (perms.find("s") != std::string::npos) {
329         flag = MAP_SHARED;
330     }
331 }
332 
GetHap()333 const std::shared_ptr<DfxHap> DfxMap::GetHap()
334 {
335     if (hap == nullptr) {
336         hap = std::make_shared<DfxHap>();
337     }
338     return hap;
339 }
340 
GetElf(pid_t pid)341 const std::shared_ptr<DfxElf> DfxMap::GetElf(pid_t pid)
342 {
343     if (elf == nullptr) {
344         if (name.empty()) {
345             LOGE("%s", "Invalid map, name empty.");
346             return nullptr;
347         }
348         LOGU("GetElf name: %s", name.c_str());
349         if (EndsWith(name, ".hap")) {
350             elf = DfxElf::CreateFromHap(name, prevMap, offset);
351         } else if (IsVdsoMap()) {
352 #if is_ohos && !is_mingw
353             size_t size = end - begin;
354             shmmData = std::make_shared<std::vector<uint8_t>>(size);
355             size_t byte = DfxMemory::ReadProcMemByPid(pid, begin, shmmData->data(), size);
356             if (byte != size) {
357                 LOGE("%s", "Failed to read shmm data");
358                 return nullptr;
359             }
360             elf = std::make_shared<DfxElf>(shmmData->data(), byte);
361 #endif
362         } else {
363             elf = DfxElf::Create(name);
364         }
365     }
366     return elf;
367 }
368 
GetElfName()369 std::string DfxMap::GetElfName()
370 {
371     if (name.empty() || GetElf() == nullptr) {
372         return name;
373     }
374     std::string soName = name;
375     if (EndsWith(name, ".hap")) {
376         soName.append("!" + elf->GetElfName());
377     }
378     return soName;
379 }
380 } // namespace HiviewDFX
381 } // namespace OHOS
382