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