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 ark::panda_file::ClassDataAccessor cda {*pandaFile_, ark::panda_file::File::EntityId(id)};
147 cda.EnumerateMethods([&](ark::panda_file::MethodDataAccessor &mda) -> void {
148 if (mda.GetCodeId().has_value()) {
149 ark::panda_file::CodeDataAccessor ca {*pandaFile_, mda.GetCodeId().value()};
150 std::stringstream ss;
151 std::string_view cname(ark::utf::Mutf8AsCString(pandaFile_->GetStringData(mda.GetClassId()).data));
152 if (!cname.empty()) {
153 ss << cname.substr(0, cname.size() - 1);
154 }
155 ss << "." << ark::utf::Mutf8AsCString(pandaFile_->GetStringData(mda.GetNameId()).data);
156 res.push_back({mda.GetCodeId().value(), ca.GetCodeSize(), ss.str()});
157 }
158 });
159 }
160
161 std::vector<struct MethodSymInfoExt> methodInfo;
162 methodInfo.reserve(res.size());
163 for (auto const &me : res) {
164 struct MethodSymInfoExt msi {};
165 msi.offset = me.id.GetOffset();
166 msi.length = me.length;
167 msi.name = me.name;
168 methodInfo.push_back(msi);
169 }
170 return methodInfo;
171 }
172
173 private:
174 std::map<uint64_t, ark::panda_file::ext::MethodSymEntry> methodSymbols_;
175 std::unique_ptr<const ark::panda_file::File> pandaFile_;
176 };
177
OpenPandafileFromMemoryExt(void * addr,const uint64_t * size,const std::string & fileName,PandaFileExt ** pandaFileExt)178 bool OpenPandafileFromMemoryExt(void *addr, const uint64_t *size, [[maybe_unused]] const std::string &fileName,
179 PandaFileExt **pandaFileExt)
180 {
181 if (pandaFileExt == nullptr) {
182 return false;
183 }
184
185 ark::os::mem::ConstBytePtr ptr(
186 reinterpret_cast<std::byte *>(addr), *size,
187 []([[maybe_unused]] std::byte *paramBuffer, [[maybe_unused]] size_t paramSize) noexcept {});
188 auto pf = ark::panda_file::File::OpenFromMemory(std::move(ptr));
189 if (pf == nullptr) {
190 return false;
191 }
192
193 auto pfExt = std::make_unique<PandaFileExt>(std::move(pf));
194 *pandaFileExt = pfExt.release();
195 return true;
196 }
197
OpenPandafileFromFdExt(int fd,uint64_t offset,const std::string & fileName,PandaFileExt ** pandaFileExt)198 bool OpenPandafileFromFdExt([[maybe_unused]] int fd, [[maybe_unused]] uint64_t offset, const std::string &fileName,
199 PandaFileExt **pandaFileExt)
200 {
201 auto pf = ark::panda_file::OpenPandaFile(fileName);
202 if (pf == nullptr) {
203 return false;
204 }
205
206 auto pfExt = std::make_unique<PandaFileExt>(std::move(pf));
207 *pandaFileExt = pfExt.release();
208 return true;
209 }
210
QueryMethodSymByOffsetExt(struct PandaFileExt * pf,uint64_t offset,struct MethodSymInfoExt * methodInfo)211 bool QueryMethodSymByOffsetExt(struct PandaFileExt *pf, uint64_t offset, struct MethodSymInfoExt *methodInfo)
212 {
213 auto entry = pf->QueryMethodSymByOffset(offset);
214 if ((entry != nullptr) && (methodInfo != nullptr)) {
215 methodInfo->offset = entry->id.GetOffset();
216 methodInfo->length = entry->length;
217 methodInfo->name = entry->name;
218 return true;
219 }
220 return false;
221 }
222
QueryMethodSymAndLineByOffsetExt(struct PandaFileExt * pf,uint64_t offset,struct MethodSymInfoExt * methodInfo)223 bool QueryMethodSymAndLineByOffsetExt(struct PandaFileExt *pf, uint64_t offset, struct MethodSymInfoExt *methodInfo)
224 {
225 auto entry = pf->QueryMethodSymAndLineByOffset(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
QueryAllMethodSymsExt(PandaFileExt * pf,MethodSymInfoExtCallBack callback,void * userData)235 void QueryAllMethodSymsExt(PandaFileExt *pf, MethodSymInfoExtCallBack callback, void *userData)
236 {
237 auto methodInfos = pf->QueryAllMethodSyms();
238 for (auto mi : methodInfos) {
239 callback(&mi, userData);
240 }
241 }
242
243 #ifdef __cplusplus
244 } // extern "C"
245 #endif
246