1 /**
2 * Copyright (c) 2021-2022 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
17 #include "file_ext.h"
18 #include "panda_file_external.h"
19 #include "libpandafile/file-inl.h"
20 #include "libpandafile/class_data_accessor-inl.h"
21 #include "libpandafile/method_data_accessor-inl.h"
22 #include "libpandafile/code_data_accessor-inl.h"
23 #include "libpandafile/debug_helpers.h"
24 #include "libpandabase/utils/logger.h"
25 #include "libpandabase/os/native_stack.h"
26 #include <map>
27
28 namespace panda::panda_file::ext {
29
30 struct MethodSymEntry {
31 panda::panda_file::File::EntityId id;
32 uint32_t length {0};
33 std::string name;
34 };
35
36 } // namespace panda::panda_file::ext
37
38 #ifdef __cplusplus
39 extern "C" {
40 #endif
41
42 struct PandaFileExt {
43 public:
PandaFileExtPandaFileExt44 explicit PandaFileExt(std::unique_ptr<const panda::panda_file::File> &&pandaFile) : pandaFile_(std::move(pandaFile))
45 {
46 }
47
GetExtFileLineNumberPandaFileExt48 size_t GetExtFileLineNumber(panda::panda_file::MethodDataAccessor mda, uint32_t bcOffset)
49 {
50 return panda::panda_file::debug_helpers::GetLineNumber(mda, bcOffset, pandaFile_.get());
51 }
52
QueryMethodSymByOffsetPandaFileExt53 panda::panda_file::ext::MethodSymEntry *QueryMethodSymByOffset(uint64_t offset)
54 {
55 auto it = methodSymbols_.upper_bound(offset);
56 if (it != methodSymbols_.end() && offset >= it->second.id.GetOffset()) {
57 return &it->second;
58 }
59
60 // Enmuate all methods and put them to local cache.
61 panda::panda_file::ext::MethodSymEntry *found = nullptr;
62 auto callBack = [this, offset, &found](panda::panda_file::MethodDataAccessor &mda) -> void {
63 if (!mda.GetCodeId().has_value()) {
64 return;
65 }
66 panda::panda_file::CodeDataAccessor ca {*pandaFile_, mda.GetCodeId().value()};
67 panda::panda_file::ext::MethodSymEntry entry;
68 entry.id = mda.GetCodeId().value();
69 entry.length = ca.GetCodeSize();
70 entry.name = std::string(panda::utf::Mutf8AsCString(pandaFile_->GetStringData(mda.GetNameId()).data));
71
72 auto ret = methodSymbols_.emplace(offset, entry);
73 if (mda.GetCodeId().value().GetOffset() <= offset &&
74 offset < mda.GetCodeId().value().GetOffset() + ca.GetCodeSize()) {
75 found = &ret.first->second;
76 }
77 };
78
79 for (uint32_t id : pandaFile_->GetClasses()) {
80 if (pandaFile_->IsExternal(panda::panda_file::File::EntityId(id))) {
81 continue;
82 }
83 panda::panda_file::ClassDataAccessor cda {*pandaFile_, panda::panda_file::File::EntityId(id)};
84 cda.EnumerateMethods(callBack);
85 }
86 return found;
87 }
88
QueryMethodSymAndLineByOffsetPandaFileExt89 panda::panda_file::ext::MethodSymEntry *QueryMethodSymAndLineByOffset(uint64_t offset)
90 {
91 auto it = methodSymbols_.upper_bound(offset);
92 if (it != methodSymbols_.end() && offset >= it->second.id.GetOffset()) {
93 return &it->second;
94 }
95
96 // Enmuate all methods and put them to local cache.
97 panda::panda_file::ext::MethodSymEntry *found = nullptr;
98 for (uint32_t id : pandaFile_->GetClasses()) {
99 if (pandaFile_->IsExternal(panda::panda_file::File::EntityId(id))) {
100 continue;
101 }
102 panda::panda_file::ClassDataAccessor cda {*pandaFile_, panda::panda_file::File::EntityId(id)};
103 cda.EnumerateMethods([&](panda::panda_file::MethodDataAccessor &mda) -> void {
104 if (mda.GetCodeId().has_value()) {
105 panda::panda_file::CodeDataAccessor ca {*pandaFile_, mda.GetCodeId().value()};
106 panda::panda_file::ext::MethodSymEntry entry;
107 entry.id = mda.GetCodeId().value();
108 entry.length = ca.GetCodeSize();
109 entry.name =
110 std::string(panda::utf::Mutf8AsCString(pandaFile_->GetStringData(mda.GetNameId()).data));
111
112 auto ret = methodSymbols_.emplace(offset, entry);
113 if (mda.GetCodeId().value().GetOffset() <= offset &&
114 offset < mda.GetCodeId().value().GetOffset() + ca.GetCodeSize()) {
115 size_t lineNumber = GetExtFileLineNumber(mda, offset - mda.GetCodeId().value().GetOffset());
116 found = &ret.first->second;
117 panda::panda_file::File::EntityId idNew(lineNumber);
118 auto nameId = cda.GetDescriptor();
119 found->id = idNew;
120 found->name =
121 panda::os::native_stack::ChangeJaveStackFormat(reinterpret_cast<const char *>(nameId)) +
122 "." + found->name;
123 }
124 }
125 });
126 }
127 return found;
128 }
129
QueryAllMethodSymsPandaFileExt130 std::vector<struct MethodSymInfoExt> QueryAllMethodSyms()
131 {
132 // Enmuate all methods and put them to local cache.
133 std::vector<panda::panda_file::ext::MethodSymEntry> res;
134 auto queryCb = [this, &res](panda::panda_file::MethodDataAccessor &mda) -> void {
135 if (!mda.GetCodeId().has_value()) {
136 return;
137 }
138 panda::panda_file::CodeDataAccessor ca {*pandaFile_, mda.GetCodeId().value()};
139 std::stringstream ss;
140 std::string_view cname(
141 panda::utf::Mutf8AsCString(pandaFile_->GetStringData(mda.GetClassId()).data));
142 if (!cname.empty()) {
143 ss << cname.substr(0, cname.size() - 1);
144 }
145 ss << "." << panda::utf::Mutf8AsCString(pandaFile_->GetStringData(mda.GetNameId()).data);
146 res.push_back({mda.GetCodeId().value(), ca.GetCodeSize(), ss.str()});
147 };
148 for (uint32_t id : pandaFile_->GetClasses()) {
149 if (pandaFile_->IsExternal(panda::panda_file::File::EntityId(id))) {
150 continue;
151 }
152 panda::panda_file::ClassDataAccessor cda {*pandaFile_, panda::panda_file::File::EntityId(id)};
153 cda.EnumerateMethods(queryCb);
154 }
155
156 std::vector<struct MethodSymInfoExt> methodInfo;
157 methodInfo.reserve(res.size());
158 for (auto const &me : res) {
159 struct MethodSymInfoExt msi {};
160 msi.offset = me.id.GetOffset();
161 msi.length = me.length;
162 msi.name = me.name;
163 methodInfo.push_back(msi);
164 }
165 return methodInfo;
166 }
167
168 private:
169 std::map<uint64_t, panda::panda_file::ext::MethodSymEntry> methodSymbols_;
170 std::unique_ptr<const panda::panda_file::File> pandaFile_;
171 };
172
OpenPandafileFromMemoryExt(void * addr,const uint64_t * size,const std::string & fileName,PandaFileExt ** pandaFileExt)173 bool OpenPandafileFromMemoryExt(void *addr, const uint64_t *size, [[maybe_unused]] const std::string &fileName,
174 PandaFileExt **pandaFileExt)
175 {
176 if (pandaFileExt == nullptr) {
177 return false;
178 }
179
180 panda::os::mem::ConstBytePtr ptr(
181 reinterpret_cast<std::byte *>(addr), *size,
182 []([[maybe_unused]] std::byte *paramBuffer, [[maybe_unused]] size_t paramSize) noexcept {});
183 auto pf = panda::panda_file::File::OpenFromMemory(std::move(ptr));
184 if (pf == nullptr) {
185 return false;
186 }
187
188 auto pfExt = std::make_unique<PandaFileExt>(std::move(pf));
189 *pandaFileExt = pfExt.release();
190 return true;
191 }
192
OpenPandafileFromFdExt(int fd,uint64_t offset,const std::string & fileName,PandaFileExt ** pandaFileExt)193 bool OpenPandafileFromFdExt([[maybe_unused]] int fd, [[maybe_unused]] uint64_t offset, const std::string &fileName,
194 PandaFileExt **pandaFileExt)
195 {
196 auto pf = panda::panda_file::OpenPandaFile(fileName);
197 if (pf == nullptr) {
198 return false;
199 }
200
201 auto pfExt = std::make_unique<PandaFileExt>(std::move(pf));
202 *pandaFileExt = pfExt.release();
203 return true;
204 }
205
QueryMethodSymByOffsetExt(struct PandaFileExt * pf,uint64_t offset,struct MethodSymInfoExt * methodInfo)206 bool QueryMethodSymByOffsetExt(struct PandaFileExt *pf, uint64_t offset, struct MethodSymInfoExt *methodInfo)
207 {
208 auto entry = pf->QueryMethodSymByOffset(offset);
209 if ((entry != nullptr) && (methodInfo != nullptr)) {
210 methodInfo->offset = entry->id.GetOffset();
211 methodInfo->length = entry->length;
212 methodInfo->name = entry->name;
213 return true;
214 }
215 return false;
216 }
217
QueryMethodSymAndLineByOffsetExt(struct PandaFileExt * pf,uint64_t offset,struct MethodSymInfoExt * methodInfo)218 bool QueryMethodSymAndLineByOffsetExt(struct PandaFileExt *pf, uint64_t offset, struct MethodSymInfoExt *methodInfo)
219 {
220 auto entry = pf->QueryMethodSymAndLineByOffset(offset);
221 if ((entry != nullptr) && (methodInfo != nullptr)) {
222 methodInfo->offset = entry->id.GetOffset();
223 methodInfo->length = entry->length;
224 methodInfo->name = entry->name;
225 return true;
226 }
227 return false;
228 }
229
QueryAllMethodSymsExt(PandaFileExt * pf,MethodSymInfoExtCallBack callback,void * userData)230 void QueryAllMethodSymsExt(PandaFileExt *pf, MethodSymInfoExtCallBack callback, void *userData)
231 {
232 auto methodInfos = pf->QueryAllMethodSyms();
233 for (auto mi : methodInfos) {
234 callback(&mi, userData);
235 }
236 }
237
238 #ifdef __cplusplus
239 } // extern "C"
240 #endif
241