1 /*
2 * Copyright (c) 2023 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_accessors.h"
17
18 #include <algorithm>
19 #include <fcntl.h>
20 #include <sys/ptrace.h>
21 #include <sys/syscall.h>
22 #include "dfx_define.h"
23 #include "dfx_errors.h"
24 #include "dfx_log.h"
25 #include "dfx_regs.h"
26 #include "dfx_trace_dlsym.h"
27 #include "dfx_elf.h"
28 #include "dfx_maps.h"
29
30 namespace OHOS {
31 namespace HiviewDFX {
32 namespace {
33 #undef LOG_DOMAIN
34 #undef LOG_TAG
35 #define LOG_DOMAIN 0xD002D11
36 #define LOG_TAG "DfxAccessors"
37
38 static const int FOUR_BYTES = 4;
39 static const int EIGHT_BYTES = 8;
40 static const int THIRTY_TWO_BITS = 32;
41 }
42
GetMapByPcAndCtx(uintptr_t pc,std::shared_ptr<DfxMap> & map,void * arg)43 bool DfxAccessors::GetMapByPcAndCtx(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg)
44 {
45 if (arg == nullptr) {
46 return false;
47 }
48 UnwindContext* ctx = reinterpret_cast<UnwindContext *>(arg);
49 if (ctx->map != nullptr && ctx->map->Contain(static_cast<uint64_t>(pc))) {
50 map = ctx->map;
51 DFXLOGU("map had matched by ctx, map name: %{public}s", map->name.c_str());
52 return true;
53 }
54
55 if (ctx->maps == nullptr || !ctx->maps->FindMapByAddr(pc, map) || (map == nullptr)) {
56 ctx->map = nullptr;
57 return false;
58 }
59 ctx->map = map;
60 return true;
61 }
62
CreatePipe()63 bool DfxAccessorsLocal::CreatePipe()
64 {
65 if (readFd_) {
66 return true;
67 }
68 std::lock_guard<std::mutex> lock(mutex_);
69 if (readFd_) {
70 return true;
71 }
72 int pipe[PIPE_NUM_SZ] = {-1, -1};
73 if (syscall(SYS_pipe2, pipe, O_CLOEXEC | O_NONBLOCK) != 0) {
74 return false;
75 }
76 readFd_ = SmartFd{pipe[PIPE_READ]};
77 writeFd_ = SmartFd{pipe[PIPE_WRITE]};
78 return static_cast<bool>(readFd_);
79 }
80
AccessMem(uintptr_t addr,uintptr_t * val,void * arg)81 NO_SANITIZE int DfxAccessorsLocal::AccessMem(uintptr_t addr, uintptr_t *val, void *arg)
82 {
83 if (val == nullptr) {
84 return UNW_ERROR_INVALID_MEMORY;
85 }
86 UnwindContext* ctx = reinterpret_cast<UnwindContext *>(arg);
87 if (ctx == nullptr || ctx->stackCheck == false) {
88 *val = *reinterpret_cast<uintptr_t *>(addr);
89 return UNW_ERROR_NONE;
90 }
91 if (UNLIKELY(ctx->stackTop < ctx->stackBottom)) {
92 DFXLOGU("Failed to access addr, the stackTop smaller than stackBottom");
93 return UNW_ERROR_INVALID_MEMORY;
94 }
95 if ((addr >= ctx->stackBottom) && (addr + sizeof(uintptr_t) < ctx->stackTop)) {
96 *val = *reinterpret_cast<uintptr_t *>(addr);
97 return UNW_ERROR_NONE;
98 }
99 if (!CreatePipe()) {
100 DFXLOGU("Failed to access addr, the pipe create fail, errno:%{public}d", errno);
101 return UNW_ERROR_INVALID_MEMORY;
102 }
103 if (OHOS_TEMP_FAILURE_RETRY(syscall(SYS_write, writeFd_.GetFd(), addr, sizeof(uintptr_t))) == -1) {
104 DFXLOGU("Failed to access addr, the pipe write fail, errno:%{public}d", errno);
105 return UNW_ERROR_INVALID_MEMORY;
106 }
107 if (OHOS_TEMP_FAILURE_RETRY(syscall(SYS_read, readFd_.GetFd(), val, sizeof(uintptr_t))) == -1) {
108 DFXLOGU("Failed to access addr, the pipe read fail, errno:%{public}d", errno);
109 return UNW_ERROR_INVALID_MEMORY;
110 }
111 return UNW_ERROR_NONE;
112 }
113
AccessReg(int reg,uintptr_t * val,void * arg)114 int DfxAccessorsLocal::AccessReg(int reg, uintptr_t *val, void *arg)
115 {
116 UnwindContext* ctx = reinterpret_cast<UnwindContext *>(arg);
117 if (ctx == nullptr) {
118 return UNW_ERROR_INVALID_CONTEXT;
119 }
120 if (ctx->regs == nullptr || reg < 0 || reg >= (int)ctx->regs->RegsSize()) {
121 return UNW_ERROR_INVALID_REGS;
122 }
123
124 *val = static_cast<uintptr_t>((*(ctx->regs))[reg]);
125 return UNW_ERROR_NONE;
126 }
127
FindUnwindTable(uintptr_t pc,UnwindTableInfo & uti,void * arg)128 int DfxAccessorsLocal::FindUnwindTable(uintptr_t pc, UnwindTableInfo& uti, void *arg)
129 {
130 UnwindContext *ctx = reinterpret_cast<UnwindContext *>(arg);
131 if (ctx == nullptr) {
132 return UNW_ERROR_INVALID_CONTEXT;
133 }
134
135 int ret = UNW_ERROR_INVALID_ELF;
136 if (ctx->map != nullptr && ctx->map->IsVdsoMap()) {
137 auto elf = ctx->map->GetElf(getpid());
138 if (elf == nullptr) {
139 DFXLOGU("FindUnwindTable elf is null");
140 return ret;
141 }
142 ret = elf->FindUnwindTableInfo(pc, ctx->map, uti);
143 } else {
144 ret = DfxElf::FindUnwindTableLocal(pc, uti);
145 }
146 if (ret == UNW_ERROR_NONE) {
147 ctx->di = uti;
148 }
149 return ret;
150 }
151
GetMapByPc(uintptr_t pc,std::shared_ptr<DfxMap> & map,void * arg)152 int DfxAccessorsLocal::GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg)
153 {
154 if (!DfxAccessors::GetMapByPcAndCtx(pc, map, arg)) {
155 return UNW_ERROR_INVALID_MAP;
156 }
157 return UNW_ERROR_NONE;
158 }
159
AccessMem(uintptr_t addr,uintptr_t * val,void * arg)160 int DfxAccessorsRemote::AccessMem(uintptr_t addr, uintptr_t *val, void *arg)
161 {
162 #if !defined(__LP64__)
163 // Cannot read an address greater than 32 bits in a 32 bit context.
164 if (addr > UINT32_MAX) {
165 return UNW_ERROR_ILLEGAL_VALUE;
166 }
167 #endif
168 UnwindContext *ctx = reinterpret_cast<UnwindContext *>(arg);
169 if ((ctx == nullptr) || (ctx->pid <= 0)) {
170 return UNW_ERROR_INVALID_CONTEXT;
171 }
172
173 if (ctx->map != nullptr && ctx->map->elf != nullptr) {
174 uintptr_t pos = ctx->map->GetRelPc(addr);
175 if (ctx->map->elf->Read(pos, val, sizeof(uintptr_t))) {
176 DFXLOGU("Read elf mmap pos: %{public}p", (void *)pos);
177 return UNW_ERROR_NONE;
178 }
179 }
180
181 int end;
182 if (sizeof(long) == FOUR_BYTES && sizeof(uintptr_t) == EIGHT_BYTES) {
183 end = 2; // 2 : read two times
184 } else {
185 end = 1;
186 }
187
188 uintptr_t tmpVal;
189 for (int i = 0; i < end; i++) {
190 uintptr_t tmpAddr = ((i == 0) ? addr : addr + FOUR_BYTES);
191 errno = 0;
192
193 tmpVal = (unsigned long) ptrace(PTRACE_PEEKDATA, ctx->pid, tmpAddr, nullptr);
194 if (i == 0) {
195 *val = 0;
196 }
197
198 #if __BYTE_ORDER == __LITTLE_ENDIAN
199 *val |= tmpVal << (i * THIRTY_TWO_BITS);
200 #else
201 *val |= (i == 0 && end == 2 ? tmpVal << THIRTY_TWO_BITS : tmpVal); // 2 : read two times
202 #endif
203 if (errno) {
204 DFXLOGU("errno: %{public}d", errno);
205 return UNW_ERROR_ILLEGAL_VALUE;
206 }
207 }
208 return UNW_ERROR_NONE;
209 }
210
AccessReg(int reg,uintptr_t * val,void * arg)211 int DfxAccessorsRemote::AccessReg(int reg, uintptr_t *val, void *arg)
212 {
213 UnwindContext *ctx = reinterpret_cast<UnwindContext *>(arg);
214 if (ctx == nullptr) {
215 return UNW_ERROR_INVALID_CONTEXT;
216 }
217 if (ctx->regs == nullptr || reg < 0 || reg >= (int)ctx->regs->RegsSize()) {
218 return UNW_ERROR_INVALID_REGS;
219 }
220
221 *val = static_cast<uintptr_t>((*(ctx->regs))[reg]);
222 return UNW_ERROR_NONE;
223 }
224
FindUnwindTable(uintptr_t pc,UnwindTableInfo & uti,void * arg)225 int DfxAccessorsRemote::FindUnwindTable(uintptr_t pc, UnwindTableInfo& uti, void *arg)
226 {
227 DFX_TRACE_SCOPED_DLSYM("FindUnwindTable");
228 UnwindContext *ctx = reinterpret_cast<UnwindContext *>(arg);
229 if (ctx == nullptr || ctx->map == nullptr) {
230 return UNW_ERROR_INVALID_CONTEXT;
231 }
232 if (pc >= ctx->di.startPc && pc < ctx->di.endPc) {
233 DFXLOGU("FindUnwindTable had pc matched");
234 uti = ctx->di;
235 return UNW_ERROR_NONE;
236 }
237
238 auto elf = ctx->map->GetElf(ctx->pid);
239 if (elf == nullptr) {
240 DFXLOGU("FindUnwindTable elf is null");
241 return UNW_ERROR_INVALID_ELF;
242 }
243 int ret = elf->FindUnwindTableInfo(pc, ctx->map, uti);
244 if (ret == UNW_ERROR_NONE) {
245 ctx->di = uti;
246 }
247 return ret;
248 }
249
GetMapByPc(uintptr_t pc,std::shared_ptr<DfxMap> & map,void * arg)250 int DfxAccessorsRemote::GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg)
251 {
252 if (!DfxAccessors::GetMapByPcAndCtx(pc, map, arg)) {
253 return UNW_ERROR_INVALID_MAP;
254 }
255 return UNW_ERROR_NONE;
256 }
257
AccessMem(uintptr_t addr,uintptr_t * val,void * arg)258 int DfxAccessorsCustomize::AccessMem(uintptr_t addr, uintptr_t *val, void *arg)
259 {
260 if (accessors_ == nullptr || accessors_->AccessMem == nullptr) {
261 return -1;
262 }
263 return accessors_->AccessMem(addr, val, arg);
264 }
265
AccessReg(int reg,uintptr_t * val,void * arg)266 int DfxAccessorsCustomize::AccessReg(int reg, uintptr_t *val, void *arg)
267 {
268 if (accessors_ == nullptr || accessors_->AccessReg == nullptr) {
269 return -1;
270 }
271 return accessors_->AccessReg(reg, val, arg);
272 }
273
FindUnwindTable(uintptr_t pc,UnwindTableInfo & uti,void * arg)274 int DfxAccessorsCustomize::FindUnwindTable(uintptr_t pc, UnwindTableInfo& uti, void *arg)
275 {
276 if (accessors_ == nullptr || accessors_->FindUnwindTable == nullptr) {
277 return -1;
278 }
279 return accessors_->FindUnwindTable(pc, uti, arg);
280 }
281
GetMapByPc(uintptr_t pc,std::shared_ptr<DfxMap> & map,void * arg)282 int DfxAccessorsCustomize::GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg)
283 {
284 if (accessors_ == nullptr || accessors_->GetMapByPc == nullptr) {
285 return -1;
286 }
287 return accessors_->GetMapByPc(pc, map, arg);
288 }
289 } // namespace HiviewDFX
290 } // namespace OHOS
291