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