• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2025 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 "elf_factory_selector.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(const std::string & vma)195 std::shared_ptr<DfxMap> DfxMap::Create(const std::string& vma)
196 {
197     if (vma.empty()) {
198         return nullptr;
199     }
200     auto map = std::make_shared<DfxMap>();
201     if (!map->Parse(vma.c_str(), vma.size())) {
202         DFXLOGW("Failed to parse map: %{public}s", vma.c_str());
203         return nullptr;
204     }
205     return map;
206 }
207 
Parse(const char * buf,size_t size)208 bool DfxMap::Parse(const 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     if (path != nullptr && path < buf + size - 1) { // Prevent null pointer dereference when using TrimAndDupStr
252         TrimAndDupStr(path, name);
253     }
254     return true;
255 #else
256     return false;
257 #endif
258 }
259 
IsMapExec()260 bool DfxMap::IsMapExec()
261 {
262     if ((prots & PROT_EXEC) != 0) {
263         return true;
264     }
265     return false;
266 }
267 
IsArkExecutable()268 bool DfxMap::IsArkExecutable()
269 {
270     if (name.length() == 0) {
271         return false;
272     }
273 
274     if ((!StartsWith(name, "[anon:ArkTS Code")) && (!StartsWith(name, "/dev/zero")) && (!EndsWith(name, "stub.an"))) {
275         return false;
276     }
277 
278     if (!IsMapExec()) {
279         DFXLOGU("Current ark map(%{public}s) is not exec", name.c_str());
280         return false;
281     }
282     DFXLOGU("Current ark map: %{public}s", name.c_str());
283     return true;
284 }
285 
IsJsvmExecutable()286 bool DfxMap::IsJsvmExecutable()
287 {
288     if (name.empty()) {
289         return false;
290     }
291     if (StartsWith(name, "[anon:JSVM_JIT")) {
292         return true;
293     }
294     if (!EndsWith(name, "libv8_shared.so")) {
295         return false;
296     }
297 
298     if (!IsMapExec()) {
299         DFXLOGU("Current jsvm map(%{public}s) is not exec", name.c_str());
300         return false;
301     }
302     DFXLOGU("Current jsvm map: %{public}s", name.c_str());
303     return true;
304 }
305 
IsVdsoMap()306 bool DfxMap::IsVdsoMap()
307 {
308     if ((name == "[shmm]" || name == "[vdso]") && IsMapExec()) {
309         return true;
310     }
311     return false;
312 }
313 
GetRelPc(uint64_t pc)314 uint64_t DfxMap::GetRelPc(uint64_t pc)
315 {
316     if (GetElf() != nullptr) {
317         return GetElf()->GetRelPc(pc, begin, offset);
318     }
319     if (prevMap != nullptr) {
320         return (pc - prevMap->begin);
321     }
322     return (pc - begin + offset);
323 }
324 
ToString() const325 std::string DfxMap::ToString() const
326 {
327     char buf[LINE_BUF_SIZE] = {0};
328     std::string realMapName = UnFormatMapName(name);
329     int ret = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%" PRIx64 "-%" PRIx64 " %s %08" PRIx64 " %s\n", \
330         begin, end, perms.c_str(), offset, realMapName.c_str());
331     if (ret <= 0) {
332         DFXLOGE("%{public}s :: snprintf_s failed, line: %{public}d.", __func__, __LINE__);
333     }
334     return std::string(buf);
335 }
336 
PermsToProts(const std::string perms,uint32_t & prots,uint32_t & flag)337 void DfxMap::PermsToProts(const std::string perms, uint32_t& prots, uint32_t& flag)
338 {
339     // rwxp
340     if (perms.find("r") != std::string::npos) {
341         prots |= PROT_READ;
342     }
343 
344     if (perms.find("w") != std::string::npos) {
345         prots |= PROT_WRITE;
346     }
347 
348     if (perms.find("x") != std::string::npos) {
349         prots |= PROT_EXEC;
350     }
351 
352     if (perms.find("p") != std::string::npos) {
353         flag = MAP_PRIVATE;
354     } else if (perms.find("s") != std::string::npos) {
355         flag = MAP_SHARED;
356     }
357 }
358 
GetHap()359 const std::shared_ptr<DfxHap> DfxMap::GetHap()
360 {
361     if (hap == nullptr) {
362         hap = std::make_shared<DfxHap>();
363     }
364     return hap;
365 }
366 
GetElf(pid_t pid)367 const std::shared_ptr<DfxElf> DfxMap::GetElf(pid_t pid)
368 {
369     if (elf == nullptr) {
370         if (name.empty()) {
371             DFXLOGE("Invalid map, name empty.");
372             return nullptr;
373         }
374         auto elfFactory = ElfFactorySelector::Select(*this, pid);
375         if (elfFactory != nullptr) {
376             elf = elfFactory->Create();
377         }
378         if (elf != nullptr && !elf->IsValid()) {
379             elf = nullptr;
380         }
381         DFXLOGU("GetElf name: %{public}s", name.c_str());
382     }
383     return elf;
384 }
385 
GetElfName()386 std::string DfxMap::GetElfName()
387 {
388     if (name.empty() || GetElf() == nullptr) {
389         return name;
390     }
391     std::string soName = name;
392     if (EndsWith(name, ".hap")) {
393         soName.append("!" + elf->GetElfName());
394     }
395     return soName;
396 }
397 
FormatMapName(pid_t pid,std::string & mapName)398 void DfxMap::FormatMapName(pid_t pid, std::string& mapName)
399 {
400     if (pid <= 0 || pid == getpid()) {
401         return;
402     }
403     // format sandbox file path, add '/proc/xxx/root' prefix
404     if (StartsWith(mapName, "/data/storage/")) {
405         mapName = "/proc/" + std::to_string(pid) + "/root" + mapName;
406     }
407 }
408 
UnFormatMapName(const std::string & mapName)409 std::string DfxMap::UnFormatMapName(const std::string& mapName)
410 {
411     // unformat sandbox file path, drop '/proc/xxx/root' prefix
412     if (StartsWith(mapName, "/proc/")) {
413         auto startPos = mapName.find("/data/storage/");
414         if (startPos != std::string::npos) {
415             return mapName.substr(startPos);
416         }
417     }
418     return mapName;
419 }
420 
421 } // namespace HiviewDFX
422 } // namespace OHOS
423