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