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