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 LOGU("map had matched by ctx, map name: %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
IsValidFrame(uintptr_t addr,uintptr_t stackBottom,uintptr_t stackTop)88 bool DfxAccessorsLocal::IsValidFrame(uintptr_t addr, uintptr_t stackBottom, uintptr_t stackTop)
89 {
90 if (UNLIKELY(stackTop < stackBottom)) {
91 return false;
92 }
93 if ((addr >= stackBottom) && (addr + sizeof(uintptr_t) < stackTop)) {
94 return true;
95 }
96
97 if (CreatePipe()) {
98 return OHOS_TEMP_FAILURE_RETRY(syscall(SYS_write, pfd_[PIPE_WRITE], addr, sizeof(uintptr_t))) != -1;
99 }
100 return false;
101 }
102
AccessMem(uintptr_t addr,uintptr_t * val,void * arg)103 NO_SANITIZE int DfxAccessorsLocal::AccessMem(uintptr_t addr, uintptr_t *val, void *arg)
104 {
105 if (val == nullptr) {
106 return UNW_ERROR_INVALID_MEMORY;
107 }
108 UnwindContext* ctx = reinterpret_cast<UnwindContext *>(arg);
109 if ((ctx != nullptr) && (ctx->stackCheck == true) && (!IsValidFrame(addr, ctx->stackBottom, ctx->stackTop))) {
110 LOGU("%s", "Failed to access addr");
111 return UNW_ERROR_INVALID_MEMORY;
112 }
113 *val = *reinterpret_cast<uintptr_t *>(addr);
114 return UNW_ERROR_NONE;
115 }
116
AccessReg(int reg,uintptr_t * val,void * arg)117 int DfxAccessorsLocal::AccessReg(int reg, uintptr_t *val, void *arg)
118 {
119 UnwindContext* ctx = reinterpret_cast<UnwindContext *>(arg);
120 if (ctx == nullptr) {
121 return UNW_ERROR_INVALID_CONTEXT;
122 }
123 if (ctx->regs == nullptr || reg < 0 || reg >= (int)ctx->regs->RegsSize()) {
124 return UNW_ERROR_INVALID_REGS;
125 }
126
127 *val = static_cast<uintptr_t>((*(ctx->regs))[reg]);
128 return UNW_ERROR_NONE;
129 }
130
FindUnwindTable(uintptr_t pc,UnwindTableInfo & uti,void * arg)131 int DfxAccessorsLocal::FindUnwindTable(uintptr_t pc, UnwindTableInfo& uti, void *arg)
132 {
133 UnwindContext *ctx = reinterpret_cast<UnwindContext *>(arg);
134 if (ctx == nullptr) {
135 return UNW_ERROR_INVALID_CONTEXT;
136 }
137
138 int ret = UNW_ERROR_INVALID_ELF;
139 if (ctx->map != nullptr && ctx->map->IsVdsoMap()) {
140 auto elf = ctx->map->GetElf(getpid());
141 if (elf == nullptr) {
142 LOGU("%s", "FindUnwindTable elf is null");
143 return ret;
144 }
145 ret = elf->FindUnwindTableInfo(pc, ctx->map, uti);
146 } else {
147 ret = DfxElf::FindUnwindTableLocal(pc, uti);
148 }
149 if (ret == UNW_ERROR_NONE) {
150 ctx->di = uti;
151 }
152 return ret;
153 }
154
GetMapByPc(uintptr_t pc,std::shared_ptr<DfxMap> & map,void * arg)155 int DfxAccessorsLocal::GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg)
156 {
157 if (!DfxAccessors::GetMapByPcAndCtx(pc, map, arg)) {
158 return UNW_ERROR_INVALID_MAP;
159 }
160 return UNW_ERROR_NONE;
161 }
162
AccessMem(uintptr_t addr,uintptr_t * val,void * arg)163 int DfxAccessorsRemote::AccessMem(uintptr_t addr, uintptr_t *val, void *arg)
164 {
165 #if !defined(__LP64__)
166 // Cannot read an address greater than 32 bits in a 32 bit context.
167 if (addr > UINT32_MAX) {
168 return UNW_ERROR_ILLEGAL_VALUE;
169 }
170 #endif
171 UnwindContext *ctx = reinterpret_cast<UnwindContext *>(arg);
172 if ((ctx == nullptr) || (ctx->pid <= 0)) {
173 return UNW_ERROR_INVALID_CONTEXT;
174 }
175
176 if (ctx->map != nullptr && ctx->map->elf != nullptr) {
177 uintptr_t pos = ctx->map->GetRelPc(addr);
178 if (ctx->map->elf->Read(pos, val, sizeof(uintptr_t))) {
179 LOGU("Read elf mmap pos: %p", (void *)pos);
180 return UNW_ERROR_NONE;
181 }
182 }
183
184 int i, end;
185 if (sizeof(long) == FOUR_BYTES && sizeof(uintptr_t) == EIGHT_BYTES) {
186 end = 2; // 2 : read two times
187 } else {
188 end = 1;
189 }
190
191 uintptr_t tmpVal;
192 for (i = 0; i < end; i++) {
193 uintptr_t tmpAddr = ((i == 0) ? addr : addr + FOUR_BYTES);
194 errno = 0;
195
196 tmpVal = (unsigned long) ptrace(PTRACE_PEEKDATA, ctx->pid, tmpAddr, nullptr);
197 if (i == 0) {
198 *val = 0;
199 }
200
201 #if __BYTE_ORDER == __LITTLE_ENDIAN
202 *val |= tmpVal << (i * THIRTY_TWO_BITS);
203 #else
204 *val |= (i == 0 && end == 2 ? tmpVal << THIRTY_TWO_BITS : tmpVal); // 2 : read two times
205 #endif
206 if (errno) {
207 LOGU("errno: %d", errno);
208 return UNW_ERROR_ILLEGAL_VALUE;
209 }
210 }
211 return UNW_ERROR_NONE;
212 }
213
AccessReg(int reg,uintptr_t * val,void * arg)214 int DfxAccessorsRemote::AccessReg(int reg, uintptr_t *val, void *arg)
215 {
216 UnwindContext *ctx = reinterpret_cast<UnwindContext *>(arg);
217 if (ctx == nullptr) {
218 return UNW_ERROR_INVALID_CONTEXT;
219 }
220 if (ctx->regs == nullptr || reg < 0 || reg >= (int)ctx->regs->RegsSize()) {
221 return UNW_ERROR_INVALID_REGS;
222 }
223
224 *val = static_cast<uintptr_t>((*(ctx->regs))[reg]);
225 return UNW_ERROR_NONE;
226 }
227
FindUnwindTable(uintptr_t pc,UnwindTableInfo & uti,void * arg)228 int DfxAccessorsRemote::FindUnwindTable(uintptr_t pc, UnwindTableInfo& uti, void *arg)
229 {
230 DFX_TRACE_SCOPED_DLSYM("FindUnwindTable");
231 UnwindContext *ctx = reinterpret_cast<UnwindContext *>(arg);
232 if (ctx == nullptr || ctx->map == nullptr) {
233 return UNW_ERROR_INVALID_CONTEXT;
234 }
235 if (pc >= ctx->di.startPc && pc < ctx->di.endPc) {
236 LOGU("%s", "FindUnwindTable had pc matched");
237 uti = ctx->di;
238 return UNW_ERROR_NONE;
239 }
240
241 auto elf = ctx->map->GetElf(ctx->pid);
242 if (elf == nullptr) {
243 LOGU("%s", "FindUnwindTable elf is null");
244 return UNW_ERROR_INVALID_ELF;
245 }
246 int ret = elf->FindUnwindTableInfo(pc, ctx->map, uti);
247 if (ret == UNW_ERROR_NONE) {
248 ctx->di = uti;
249 }
250 return ret;
251 }
252
GetMapByPc(uintptr_t pc,std::shared_ptr<DfxMap> & map,void * arg)253 int DfxAccessorsRemote::GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg)
254 {
255 if (!DfxAccessors::GetMapByPcAndCtx(pc, map, arg)) {
256 return UNW_ERROR_INVALID_MAP;
257 }
258 return UNW_ERROR_NONE;
259 }
260
AccessMem(uintptr_t addr,uintptr_t * val,void * arg)261 int DfxAccessorsCustomize::AccessMem(uintptr_t addr, uintptr_t *val, void *arg)
262 {
263 if (accessors_ == nullptr || accessors_->AccessMem == nullptr) {
264 return -1;
265 }
266 return accessors_->AccessMem(addr, val, arg);
267 }
268
AccessReg(int reg,uintptr_t * val,void * arg)269 int DfxAccessorsCustomize::AccessReg(int reg, uintptr_t *val, void *arg)
270 {
271 if (accessors_ == nullptr || accessors_->AccessReg == nullptr) {
272 return -1;
273 }
274 return accessors_->AccessReg(reg, val, arg);
275 }
276
FindUnwindTable(uintptr_t pc,UnwindTableInfo & uti,void * arg)277 int DfxAccessorsCustomize::FindUnwindTable(uintptr_t pc, UnwindTableInfo& uti, void *arg)
278 {
279 if (accessors_ == nullptr || accessors_->FindUnwindTable == nullptr) {
280 return -1;
281 }
282 return accessors_->FindUnwindTable(pc, uti, arg);
283 }
284
GetMapByPc(uintptr_t pc,std::shared_ptr<DfxMap> & map,void * arg)285 int DfxAccessorsCustomize::GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg)
286 {
287 if (accessors_ == nullptr || accessors_->GetMapByPc == nullptr) {
288 return -1;
289 }
290 return accessors_->GetMapByPc(pc, map, arg);
291 }
292 } // namespace HiviewDFX
293 } // namespace OHOS
294