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
33namespace OHOS {
34namespace HiviewDFX {
35namespace {
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
42AT_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
54AT_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
86AT_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
116AT_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
132AT_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
153AT_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
194std::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
207bool 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
259bool DfxMap::IsMapExec()
260{
261    if ((prots & PROT_EXEC) != 0) {
262        return true;
263    }
264    return false;
265}
266
267bool 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
285bool DfxMap::IsVdsoMap()
286{
287    if ((name == "[shmm]" || name == "[vdso]") && IsMapExec()) {
288        return true;
289    }
290    return false;
291}
292
293uint64_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
301std::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
315void 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
337const std::shared_ptr<DfxHap> DfxMap::GetHap()
338{
339    if (hap == nullptr) {
340        hap = std::make_shared<DfxHap>();
341    }
342    return hap;
343}
344
345const 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
373std::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
385void 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
396void 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