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