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_memory.h" 17#include <algorithm> 18#include <securec.h> 19#if is_ohos && !is_mingw 20#include <sys/uio.h> 21#endif 22#include "dfx_define.h" 23#include "dfx_errors.h" 24#include "dfx_log.h" 25#include "dwarf_define.h" 26 27namespace OHOS { 28namespace HiviewDFX { 29namespace { 30#undef LOG_DOMAIN 31#undef LOG_TAG 32#define LOG_DOMAIN 0xD002D11 33#define LOG_TAG "DfxMemory" 34 35static const int SEVEN_BIT_OFFSET = 7; 36static const int TWO_BYTE_SIZE = 2; 37static const int FOUR_BYTE_SIZE = 4; 38static const int EIGHT_BYTE_SIZE = 8; 39} 40 41bool DfxMemory::ReadReg(int regIdx, uintptr_t *val) 42{ 43 if (acc_ != nullptr && acc_->AccessReg(regIdx, val, ctx_) == UNW_ERROR_NONE) { 44 return true; 45 } 46 return false; 47} 48 49bool DfxMemory::ReadMem(uintptr_t addr, uintptr_t *val) 50{ 51 if (acc_ != nullptr && acc_->AccessMem(addr, val, ctx_) == UNW_ERROR_NONE) { 52 return true; 53 } 54 return false; 55} 56 57size_t DfxMemory::Read(uintptr_t& addr, void* val, size_t size, bool incre) 58{ 59 uintptr_t tmpAddr = addr; 60 uint64_t maxSize; 61 if (val == nullptr || __builtin_add_overflow(tmpAddr, size, &maxSize)) { 62 DFXLOGE("val is nullptr or size(%{public}zu) overflow", size); 63 return 0; 64 } 65 size_t bytesRead = 0; 66 uintptr_t tmpVal; 67 if (alignAddr_ && (alignBytes_ != 0)) { 68 size_t alignBytes = tmpAddr & (static_cast<size_t>(alignBytes_) - 1); 69 if (alignBytes != 0) { 70 uintptr_t alignedAddr = tmpAddr & (~(static_cast<uintptr_t>(alignBytes_)) - 1); 71 DFXLOGU("alignBytes: %{public}zu, alignedAddr: %{public}" PRIx64 "", alignBytes, 72 static_cast<uint64_t>(alignedAddr)); 73 if (!ReadMem(alignedAddr, &tmpVal)) { 74 return bytesRead; 75 } 76 uintptr_t valp = static_cast<uintptr_t>(tmpVal); 77 size_t copyBytes = std::min(static_cast<size_t>(alignBytes_) - alignBytes, size); 78 if (memcpy_s(val, copyBytes, reinterpret_cast<uint8_t*>(&valp) + alignBytes, copyBytes) != 0) { 79 return bytesRead; 80 } 81 tmpAddr += copyBytes; 82 val = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(val) + copyBytes); 83 size -= copyBytes; 84 bytesRead += copyBytes; 85 } 86 } 87 for (size_t i = 0; i < size / sizeof(uintptr_t); i++) { 88 if (!ReadMem(tmpAddr, &tmpVal) || memcpy_s(val, sizeof(uintptr_t), &tmpVal, sizeof(uintptr_t)) != 0) { 89 return bytesRead; 90 } 91 val = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(val) + sizeof(uintptr_t)); 92 tmpAddr += sizeof(uintptr_t); 93 bytesRead += sizeof(uintptr_t); 94 } 95 size_t leftOver = size & (sizeof(uintptr_t) - 1); 96 if (leftOver) { 97 if (!ReadMem(tmpAddr, &tmpVal) || memcpy_s(val, leftOver, &tmpVal, leftOver) != 0) { 98 return bytesRead; 99 } 100 tmpAddr += leftOver; 101 bytesRead += leftOver; 102 } 103 if (incre) { 104 addr = tmpAddr; 105 } 106 return bytesRead; 107} 108 109bool DfxMemory::ReadU8(uintptr_t& addr, uint8_t *val, bool incre) 110{ 111 if (Read(addr, val, sizeof(uint8_t), incre) == sizeof(uint8_t)) { 112 return true; 113 } 114 return false; 115} 116 117bool DfxMemory::ReadU16(uintptr_t& addr, uint16_t *val, bool incre) 118{ 119 if (Read(addr, val, sizeof(uint16_t), incre) == sizeof(uint16_t)) { 120 return true; 121 } 122 return false; 123} 124 125bool DfxMemory::ReadU32(uintptr_t& addr, uint32_t *val, bool incre) 126{ 127 if (Read(addr, val, sizeof(uint32_t), incre) == sizeof(uint32_t)) { 128 return true; 129 } 130 return false; 131} 132 133bool DfxMemory::ReadU64(uintptr_t& addr, uint64_t *val, bool incre) 134{ 135 if (Read(addr, val, sizeof(uint64_t), incre) == sizeof(uint64_t)) { 136 return true; 137 } 138 return false; 139} 140 141bool DfxMemory::ReadUptr(uintptr_t& addr, uintptr_t *val, bool incre) 142{ 143 if (Read(addr, val, sizeof(uintptr_t), incre) == sizeof(uintptr_t)) { 144 return true; 145 } 146 return false; 147} 148 149bool DfxMemory::ReadString(uintptr_t& addr, std::string* str, size_t maxSize, bool incre) 150{ 151 if (str == nullptr) { 152 return false; 153 } 154 char buf[NAME_BUF_LEN]; 155 size_t size = 0; 156 uintptr_t ptr = addr; 157 for (size_t offset = 0; offset < maxSize; offset += size) { 158 size_t readn = std::min(sizeof(buf), maxSize - offset); 159 ptr = ptr + offset; 160 size = Read(ptr, buf, readn, false); 161 if (size == 0) { 162 return false; 163 } 164 size_t length = strnlen(buf, size); 165 if (length < size) { 166 if (offset == 0) { 167 str->assign(buf, length); 168 return true; 169 } else { 170 str->assign(offset + length, '\0'); 171 Read(addr, (void*)str->data(), str->size(), false); 172 return true; 173 } 174 } 175 } 176 if (incre && str != nullptr) { 177 addr += str->size(); 178 } 179 return false; 180} 181 182bool DfxMemory::ReadPrel31(uintptr_t& addr, uintptr_t *val) 183{ 184 uintptr_t offset; 185 if (!ReadUptr(addr, &offset, false)) { 186 return false; 187 } 188 offset = static_cast<uintptr_t>(static_cast<int32_t>(offset << 1) >> 1); 189 *val = addr + offset; 190 return true; 191} 192 193uint64_t DfxMemory::ReadUleb128(uintptr_t& addr) 194{ 195 uint64_t val = 0; 196 uint64_t shift = 0; 197 uint8_t u8 = 0; 198 do { 199 if (!ReadU8(addr, &u8, true)) { 200 break; 201 } 202 203 val |= static_cast<uint64_t>(u8 & 0x7f) << shift; 204 shift += SEVEN_BIT_OFFSET; 205 } while (u8 & 0x80); 206 return val; 207} 208 209int64_t DfxMemory::ReadSleb128(uintptr_t& addr) 210{ 211 uint64_t val = 0; 212 uint64_t shift = 0; 213 uint8_t byte = 0; 214 do { 215 if (!ReadU8(addr, &byte, true)) { 216 break; 217 } 218 219 val |= static_cast<uint64_t>(byte & 0x7f) << shift; 220 shift += SEVEN_BIT_OFFSET; 221 } while (byte & 0x80); 222 223 if ((byte & 0x40) != 0) { 224 val |= (-1ULL) << shift; 225 } 226 return static_cast<int64_t>(val); 227} 228 229size_t DfxMemory::GetEncodedSize(uint8_t encoding) 230{ 231 switch (encoding & 0x0f) { 232 case DW_EH_PE_absptr: 233 return sizeof(uintptr_t); 234 case DW_EH_PE_udata1: 235 case DW_EH_PE_sdata1: 236 return 1; 237 case DW_EH_PE_udata2: 238 case DW_EH_PE_sdata2: 239 return TWO_BYTE_SIZE; 240 case DW_EH_PE_udata4: 241 case DW_EH_PE_sdata4: 242 return FOUR_BYTE_SIZE; 243 case DW_EH_PE_udata8: 244 case DW_EH_PE_sdata8: 245 return EIGHT_BYTE_SIZE; 246 case DW_EH_PE_uleb128: 247 case DW_EH_PE_sleb128: 248 default: 249 return 0; 250 } 251} 252 253uintptr_t DfxMemory::ReadEncodedValue(uintptr_t& addr, uint8_t encoding) 254{ 255 uintptr_t startAddr = addr; 256 uintptr_t val = 0; 257 if (encoding == DW_EH_PE_omit) { 258 return val; 259 } else if (encoding == DW_EH_PE_aligned) { 260 if (__builtin_add_overflow(addr, sizeof(uintptr_t) - 1, &addr)) { 261 return val; 262 } 263 addr &= -sizeof(uintptr_t); 264 ReadUptr(addr, &val, true); 265 return val; 266 } 267 268 switch (encoding & DW_EH_PE_FORMAT_MASK) { 269 case DW_EH_PE_absptr: 270 ReadUptr(addr, &val, true); 271 return val; 272 case DW_EH_PE_uleb128: 273 val = static_cast<uintptr_t>(ReadUleb128(addr)); 274 break; 275 case DW_EH_PE_sleb128: 276 val = static_cast<uintptr_t>(ReadSleb128(addr)); 277 break; 278 case DW_EH_PE_udata1: { 279 uint8_t tmp = 0; 280 ReadU8(addr, &tmp, true); 281 val = static_cast<uintptr_t>(tmp); 282 } 283 break; 284 case DW_EH_PE_sdata1: { 285 int8_t tmp = 0; 286 ReadS8(addr, &tmp, true); 287 val = static_cast<uintptr_t>(tmp); 288 } 289 break; 290 case DW_EH_PE_udata2: { 291 uint16_t tmp = 0; 292 ReadU16(addr, &tmp, true); 293 val = static_cast<uintptr_t>(tmp); 294 } 295 break; 296 case DW_EH_PE_sdata2: { 297 int16_t tmp = 0; 298 ReadS16(addr, &tmp, true); 299 val = static_cast<uintptr_t>(tmp); 300 } 301 break; 302 case DW_EH_PE_udata4: { 303 uint32_t tmp = 0; 304 ReadU32(addr, &tmp, true); 305 val = static_cast<uintptr_t>(tmp); 306 } 307 break; 308 case DW_EH_PE_sdata4: { 309 int32_t tmp = 0; 310 ReadS32(addr, &tmp, true); 311 val = static_cast<uintptr_t>(tmp); 312 } 313 break; 314 case DW_EH_PE_udata8: { 315 uint64_t tmp = 0; 316 ReadU64(addr, &tmp, true); 317 val = static_cast<uintptr_t>(tmp); 318 } 319 break; 320 case DW_EH_PE_sdata8: { 321 int64_t tmp = 0; 322 ReadS64(addr, &tmp, true); 323 val = static_cast<uintptr_t>(tmp); 324 } 325 break; 326 default: 327 DFXLOGW("Unexpected encoding format 0x%{public}x", encoding & DW_EH_PE_FORMAT_MASK); 328 break; 329 } 330 331 switch (encoding & DW_EH_PE_APPL_MASK) { 332 case DW_EH_PE_pcrel: 333 val += startAddr; 334 break; 335 case DW_EH_PE_textrel: 336 DFXLOGE("XXX For now we don't support text-rel values"); 337 break; 338 case DW_EH_PE_datarel: 339 val += dataOffset_; 340 break; 341 case DW_EH_PE_funcrel: 342 val += funcOffset_; 343 break; 344 default: 345 break; 346 } 347 348 if (encoding & DW_EH_PE_indirect) { 349 uintptr_t indirectAddr = val; 350 ReadUptr(indirectAddr, &val, true); 351 } 352 return val; 353} 354#if is_ohos && !is_mingw 355size_t DfxMemory::ReadProcMemByPid(const pid_t pid, const uint64_t addr, void* data, size_t size) 356{ 357 constexpr size_t maxSize = 64; 358 struct iovec RemoteIovs[maxSize]; 359 360 uint64_t cur = addr; 361 size_t totalRead = 0; 362 struct iovec dataIov = { 363 .iov_base = &reinterpret_cast<uint8_t*>(data)[totalRead], 364 .iov_len = size, 365 }; 366 size_t iovecsIndex = 0; 367 while (size > 0) { 368 if (cur >= UINTPTR_MAX) { 369 return totalRead; 370 } 371 RemoteIovs[iovecsIndex].iov_base = reinterpret_cast<void*>(cur); 372 uintptr_t misalign = cur & static_cast<uint64_t>(getpagesize() - 1); 373 size_t iovLen = std::min(getpagesize() - misalign, size); 374 375 size -= iovLen; 376 if (__builtin_add_overflow(cur, iovLen, &cur)) { 377 return totalRead; 378 } 379 380 RemoteIovs[iovecsIndex].iov_len = iovLen; 381 ++iovecsIndex; 382 if (iovecsIndex >= maxSize || size <= 0) { 383 ssize_t count = process_vm_readv(pid, &dataIov, 1, RemoteIovs, iovecsIndex, 0); 384 if (count == -1) { 385 return totalRead; 386 } 387 totalRead += static_cast<size_t>(count); 388 if (iovecsIndex >= maxSize) { 389 iovecsIndex -= maxSize; 390 } 391 dataIov.iov_base = &reinterpret_cast<uint8_t*>(data)[totalRead]; 392 dataIov.iov_len = size; 393 } 394 } 395 396 return totalRead; 397} 398#endif 399} // namespace HiviewDFX 400} // namespace OHOS 401