1 /*
2 * Copyright (c) 2021-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_maps.h"
17
18 #include <algorithm>
19 #include <cinttypes>
20 #include <climits>
21 #include <cstdio>
22 #include <cstdio>
23 #include <cstdlib>
24 #include <cstring>
25 #include <memory>
26 #include <securec.h>
27 #include <sys/mman.h>
28 #include <string>
29 #include <sstream>
30 #include <vector>
31 #include "dfx_define.h"
32 #include "dfx_elf.h"
33 #include "dfx_memory_file.h"
34 #include "dfx_log.h"
35 #include "dfx_util.h"
36 #include "string_util.h"
37
38 namespace OHOS {
39 namespace HiviewDFX {
40 static const int MAPINFO_SIZE = 256;
41 MAYBE_UNUSED static constexpr int X86_ONE_STEP_NORMAL = 1;
42 MAYBE_UNUSED static constexpr int ARM_TWO_STEP_NORMAL = 2;
43 MAYBE_UNUSED static constexpr int ARM_FOUR_STEP_NORMAL = 4;
44 MAYBE_UNUSED static constexpr int PROT_DEVICE_MAP = 0x8000;
45
Create(const std::string mapBuf,int size)46 std::shared_ptr<DfxElfMap> DfxElfMap::Create(const std::string mapBuf, int size)
47 {
48 auto elfMap = std::make_shared<DfxElfMap>();
49
50 int pos = 0;
51 uint64_t begin = 0;
52 uint64_t end = 0;
53 uint64_t offset = 0;
54 char perms[5] = {0}; // 5:rwxp
55
56 // 7658d38000-7658d40000 rw-p 00000000 00:00 0 [anon:thread signal stack]
57 if (sscanf_s(mapBuf.c_str(), "%" SCNxPTR "-%" SCNxPTR " %4s %" SCNxPTR " %*x:%*x %*d%n", &begin, &end,
58 &perms, sizeof(perms), &offset,
59 &pos) != 4) { // 4:scan size
60 DFXLOG_WARN("Fail to parse maps info.");
61 return nullptr;
62 }
63
64 elfMap->begin = begin;
65 elfMap->end = end;
66 elfMap->offset = offset;
67 elfMap->perms = std::string(perms, sizeof(perms));
68 PermsToProts(elfMap->perms, elfMap->prots);
69 TrimAndDupStr(mapBuf.substr(pos), elfMap->path);
70 if (!elfMap->IsValidPath()) {
71 elfMap->prots |= PROT_DEVICE_MAP;
72 }
73 return elfMap;
74 }
75
Create(pid_t pid)76 std::shared_ptr<DfxElfMaps> DfxElfMaps::Create(pid_t pid)
77 {
78 char path[NAME_BUF_LEN] = {0};
79 if (snprintf_s(path, sizeof(path), sizeof(path) - 1, "/proc/%d/maps", pid) <= 0) {
80 DFXLOG_WARN("Fail to print path.");
81 return nullptr;
82 }
83 return Create(std::string(path));
84 }
85
CreateFromLocal()86 std::shared_ptr<DfxElfMaps> DfxElfMaps::CreateFromLocal()
87 {
88 return Create(std::string(PROC_SELF_MAPS_PATH));
89 }
90
Create(const std::string path)91 std::shared_ptr<DfxElfMaps> DfxElfMaps::Create(const std::string path)
92 {
93 std::string realPath = path;
94 if (!RealPath(path, realPath)) {
95 DFXLOG_WARN("Failed to realpath %s", path.c_str());
96 return nullptr;
97 }
98
99 FILE *fp = fopen(realPath.c_str(), "r");
100 if (fp == nullptr) {
101 DFXLOG_WARN("Fail to open maps info.");
102 return nullptr;
103 }
104
105 auto dfxElfMaps = std::make_shared<DfxElfMaps>();
106 char mapBuf[MAPINFO_SIZE] = {0};
107 while (fgets(mapBuf, sizeof(mapBuf), fp) != nullptr) {
108 std::shared_ptr<DfxElfMap> map = DfxElfMap::Create(mapBuf, sizeof(mapBuf));
109 if (map == nullptr) {
110 DFXLOG_WARN("Fail to init map info:%s.", mapBuf);
111 continue;
112 } else {
113 dfxElfMaps->AddMap(map);
114 }
115 }
116 int ret = fclose(fp);
117 if (ret < 0) {
118 DFXLOG_WARN("Fail to close maps info.");
119 }
120 dfxElfMaps->Sort();
121 return dfxElfMaps;
122 }
123
Create(const char * buffer)124 std::shared_ptr<DfxElfMaps> DfxElfMaps::Create(const char* buffer)
125 {
126 if (buffer == nullptr) {
127 return nullptr;
128 }
129 std::istringstream iss(buffer);
130 std::string temp;
131 auto dfxElfMaps = std::make_shared<DfxElfMaps>();
132 while (std::getline(iss, temp)) {
133 std::shared_ptr<DfxElfMap> map = DfxElfMap::Create(temp, temp.length());
134 if (map == nullptr) {
135 DFXLOG_WARN("Fail to init map info:%s.", temp.c_str());
136 continue;
137 } else {
138 dfxElfMaps->AddMap(map);
139 }
140 }
141 dfxElfMaps->Sort();
142 return dfxElfMaps;
143 }
144
AddMap(std::shared_ptr<DfxElfMap> map)145 void DfxElfMaps::AddMap(std::shared_ptr<DfxElfMap> map)
146 {
147 maps_.push_back(map);
148 return;
149 }
150
FindMapByAddr(uintptr_t address,std::shared_ptr<DfxElfMap> & map) const151 bool DfxElfMaps::FindMapByAddr(uintptr_t address, std::shared_ptr<DfxElfMap>& map) const
152 {
153 if ((maps_.size() == 0) || (address == 0x0)) {
154 return false;
155 }
156
157 size_t first = 0;
158 size_t last = maps_.size();
159 while (first < last) {
160 size_t index = (first + last) / 2;
161 const auto& cur = maps_[index];
162 if (address >= cur->begin && address < cur->end) {
163 map = cur;
164 return true;
165 } else if (address < cur->begin) {
166 last = index;
167 } else {
168 first = index + 1;
169 }
170 }
171 return false;
172 }
173
Sort(bool less)174 void DfxElfMaps::Sort(bool less)
175 {
176 if (less) {
177 std::sort(maps_.begin(), maps_.end(),
178 [](const std::shared_ptr<DfxElfMap>& a, const std::shared_ptr<DfxElfMap>& b) {
179 return a->begin < b->begin;
180 });
181 } else {
182 std::sort(maps_.begin(), maps_.end(),
183 [](const std::shared_ptr<DfxElfMap>& a, const std::shared_ptr<DfxElfMap>& b) {
184 return a->begin > b->begin;
185 });
186 }
187 }
188
GetMaps() const189 const std::vector<std::shared_ptr<DfxElfMap>>& DfxElfMaps::GetMaps() const
190 {
191 return maps_;
192 }
193
IsValidPath()194 bool DfxElfMap::IsValidPath()
195 {
196 if (path.length() == 0) {
197 return false;
198 }
199
200 if (strncmp(path.c_str(), "/dev/", 5) == 0) { // 5:length of "/dev/"
201 return false;
202 }
203 return true;
204 }
205
PrintMap()206 std::string DfxElfMap::PrintMap()
207 {
208 char buf[LINE_BUF_SIZE] = {0};
209 int ret = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%" PRIx64 "-%" PRIx64 " %s %08" PRIx64 " %s\n", \
210 begin, end, perms.c_str(), offset, path.c_str());
211 if (ret <= 0) {
212 DFXLOG_ERROR("%s :: snprintf_s failed, line: %d.", __func__, __LINE__);
213 }
214 return std::string(buf);
215 }
216
PermsToProts(const std::string perms,uint64_t & prots)217 void DfxElfMap::PermsToProts(const std::string perms, uint64_t& prots)
218 {
219 prots = PROT_NONE;
220 // rwxp
221 if (perms.find("r") != std::string::npos) {
222 prots |= PROT_READ;
223 }
224
225 if (perms.find("w") != std::string::npos) {
226 prots |= PROT_WRITE;
227 }
228
229 if (perms.find("x") != std::string::npos) {
230 prots |= PROT_EXEC;
231 }
232 }
233
GetRelPc(uint64_t pc)234 uint64_t DfxElfMap::GetRelPc(uint64_t pc)
235 {
236 uint64_t relPc = 0;
237 if (elf == nullptr) {
238 auto memory = DfxMemoryFile::CreateFileMemory(path, 0);
239 if (memory != nullptr) {
240 elf = std::make_shared<DfxElf>(memory);
241 }
242 }
243
244 if (elf == nullptr) {
245 relPc = pc - (begin - offset);
246 } else {
247 relPc = (pc - begin) + elf->GetRealLoadOffset(offset);
248 }
249
250 #ifdef __aarch64__
251 relPc = relPc - 4; // 4 : instr offset
252 #elif defined(__x86_64__)
253 relPc = relPc - 1; // 1 : instr offset
254 #endif
255 return relPc;
256 }
257
GetPcAdjustment(uint64_t pc)258 uint32_t DfxElfMap::GetPcAdjustment(uint64_t pc)
259 {
260 if (pc <= ARM_FOUR_STEP_NORMAL) { // pc zero is abnormal case, so we don't adjust pc.
261 return 0;
262 }
263 uint32_t sz = ARM_FOUR_STEP_NORMAL;
264 #if defined(__arm__)
265 if (pc & 1) {
266 // This is a thumb instruction, it could be 2 or 4 bytes.
267 uint32_t value;
268 uint64_t adjustedPc = pc - 5;
269 if ((perms.find("r") == std::string::npos) ||
270 adjustedPc < begin || (adjustedPc + sizeof(value)) >= end ||
271 memcpy_s(&value, sizeof(value), reinterpret_cast<const void *>(adjustedPc), sizeof(value)) ||
272 (value & 0xe000f000) != 0xe000f000) {
273 sz = ARM_TWO_STEP_NORMAL;
274 }
275 }
276 #elif defined(__aarch64__)
277 sz = ARM_FOUR_STEP_NORMAL;
278 #elif defined(__x86_64__)
279 sz = X86_ONE_STEP_NORMAL;
280 #endif
281 return sz;
282 }
283 } // namespace HiviewDFX
284 } // namespace OHOS
285