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