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